curl --request POST \
--url https://api.example.com/api/skills/install \
--header 'Content-Type: application/json' \
--header 'X-Session-API-Key: <api-key>' \
--data '
{
"source": "<string>",
"force": false,
"ref": "<string>",
"repo_path": "<string>"
}
'import requests
url = "https://api.example.com/api/skills/install"
payload = {
"source": "<string>",
"force": False,
"ref": "<string>",
"repo_path": "<string>"
}
headers = {
"X-Session-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Session-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({source: '<string>', force: false, ref: '<string>', repo_path: '<string>'})
};
fetch('https://api.example.com/api/skills/install', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/skills/install",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'source' => '<string>',
'force' => false,
'ref' => '<string>',
'repo_path' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Session-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/skills/install"
payload := strings.NewReader("{\n \"source\": \"<string>\",\n \"force\": false,\n \"ref\": \"<string>\",\n \"repo_path\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Session-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/skills/install")
.header("X-Session-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"source\": \"<string>\",\n \"force\": false,\n \"ref\": \"<string>\",\n \"repo_path\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/skills/install")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Session-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source\": \"<string>\",\n \"force\": false,\n \"ref\": \"<string>\",\n \"repo_path\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"install_path": "<string>",
"installed_at": "<string>",
"name": "<string>",
"source": "<string>",
"description": "",
"enabled": true,
"repo_path": "<string>",
"resolved_ref": "<string>",
"version": ""
}Install Skill Endpoint
Install a skill from a source.
Installs a skill from a git URL, GitHub shorthand, or local path into the user’s installed skills directory (~/.openhands/skills/installed/).
Args: request: InstallSkillRequest containing source and options.
Returns: InstalledSkillResponse with details about the installation.
Raises: HTTPException 409: If skill is already installed and force=False. HTTPException 400: If fetching the skill source fails. HTTPException 422: If the skill is invalid.
curl --request POST \
--url https://api.example.com/api/skills/install \
--header 'Content-Type: application/json' \
--header 'X-Session-API-Key: <api-key>' \
--data '
{
"source": "<string>",
"force": false,
"ref": "<string>",
"repo_path": "<string>"
}
'import requests
url = "https://api.example.com/api/skills/install"
payload = {
"source": "<string>",
"force": False,
"ref": "<string>",
"repo_path": "<string>"
}
headers = {
"X-Session-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Session-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({source: '<string>', force: false, ref: '<string>', repo_path: '<string>'})
};
fetch('https://api.example.com/api/skills/install', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/skills/install",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'source' => '<string>',
'force' => false,
'ref' => '<string>',
'repo_path' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Session-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/skills/install"
payload := strings.NewReader("{\n \"source\": \"<string>\",\n \"force\": false,\n \"ref\": \"<string>\",\n \"repo_path\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Session-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/skills/install")
.header("X-Session-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"source\": \"<string>\",\n \"force\": false,\n \"ref\": \"<string>\",\n \"repo_path\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/skills/install")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Session-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source\": \"<string>\",\n \"force\": false,\n \"ref\": \"<string>\",\n \"repo_path\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"install_path": "<string>",
"installed_at": "<string>",
"name": "<string>",
"source": "<string>",
"description": "",
"enabled": true,
"repo_path": "<string>",
"resolved_ref": "<string>",
"version": ""
}Authorizations
Body
Request body for installing a skill.
Skill source - git URL, GitHub shorthand, or local path. Examples: 'https://github.com/OpenHands/extensions/tree/main/skills/github', 'github:OpenHands/extensions/skills/github', '/path/to/skill'
1If true, overwrite existing installation
Optional branch, tag, or commit to install
Subdirectory path within the repository (for monorepos)
Response
Successful Response
Response containing installed skill information.
Path where the skill is installed
ISO 8601 timestamp of installation
Skill name
Original source (e.g., 'github:owner/repo')
Skill description
Whether the skill is enabled
Subdirectory path within the repository
Resolved git commit SHA
Skill version
Was this page helpful?

