from decimalai.skill_router import SkillRouter
router = SkillRouter(api_key="dai_sk_...")
router.create_skill(
name="search-flights",
description="Search available flights",
body_markdown="# Search Flights\n\n## When to activate\nUse when the user asks about flight availability.",
)curl --request POST \
--url https://api.decimal.ai/api/v1/skills \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "search-flights",
"description": "Search available flights",
"body_markdown": "# Search Flights\n\n## When to activate\nUse this when the user asks about flight availability."
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'search-flights',
description: 'Search available flights',
body_markdown: '# Search Flights\n\n## When to activate\nUse this when the user asks about flight availability.'
})
};
fetch('https://api.decimal.ai/api/v1/skills', 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.decimal.ai/api/v1/skills",
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([
'name' => 'search-flights',
'description' => 'Search available flights',
'body_markdown' => '# Search Flights
## When to activate
Use this when the user asks about flight availability.'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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.decimal.ai/api/v1/skills"
payload := strings.NewReader("{\n \"name\": \"search-flights\",\n \"description\": \"Search available flights\",\n \"body_markdown\": \"# Search Flights\\n\\n## When to activate\\nUse this when the user asks about flight availability.\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.decimal.ai/api/v1/skills")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"search-flights\",\n \"description\": \"Search available flights\",\n \"body_markdown\": \"# Search Flights\\n\\n## When to activate\\nUse this when the user asks about flight availability.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.decimal.ai/api/v1/skills")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"search-flights\",\n \"description\": \"Search available flights\",\n \"body_markdown\": \"# Search Flights\\n\\n## When to activate\\nUse this when the user asks about flight availability.\"\n}"
response = http.request(request)
puts response.read_body{
"status": "ok",
"skill_id": "sk_abc123",
"name": "search-flights",
"version": 1,
"offer_scope": "restricted"
}Create a skill
Create a new skill with its first version.
Ownership is set automatically:
creator_user_idfrom the authenticated userowning_workspace_idfrom the payload or auth context
Name must be lowercase alphanumeric with hyphens/underscores (1-200 chars).
Visibility must be one of: org, workspace, personal — public is
rejected here (422); listing on the registry is done by
POST /skills/{name}/publish, which is where the publish gates live.
Stability must be one of: stable, experimental, deprecated.
Supports X-Idempotency-Key for safe retries — within 60s, a request with
the same key returns the original response instead of creating a duplicate.
from decimalai.skill_router import SkillRouter
router = SkillRouter(api_key="dai_sk_...")
router.create_skill(
name="search-flights",
description="Search available flights",
body_markdown="# Search Flights\n\n## When to activate\nUse when the user asks about flight availability.",
)curl --request POST \
--url https://api.decimal.ai/api/v1/skills \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "search-flights",
"description": "Search available flights",
"body_markdown": "# Search Flights\n\n## When to activate\nUse this when the user asks about flight availability."
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'search-flights',
description: 'Search available flights',
body_markdown: '# Search Flights\n\n## When to activate\nUse this when the user asks about flight availability.'
})
};
fetch('https://api.decimal.ai/api/v1/skills', 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.decimal.ai/api/v1/skills",
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([
'name' => 'search-flights',
'description' => 'Search available flights',
'body_markdown' => '# Search Flights
## When to activate
Use this when the user asks about flight availability.'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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.decimal.ai/api/v1/skills"
payload := strings.NewReader("{\n \"name\": \"search-flights\",\n \"description\": \"Search available flights\",\n \"body_markdown\": \"# Search Flights\\n\\n## When to activate\\nUse this when the user asks about flight availability.\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.decimal.ai/api/v1/skills")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"search-flights\",\n \"description\": \"Search available flights\",\n \"body_markdown\": \"# Search Flights\\n\\n## When to activate\\nUse this when the user asks about flight availability.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.decimal.ai/api/v1/skills")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"search-flights\",\n \"description\": \"Search available flights\",\n \"body_markdown\": \"# Search Flights\\n\\n## When to activate\\nUse this when the user asks about flight availability.\"\n}"
response = http.request(request)
puts response.read_body{
"status": "ok",
"skill_id": "sk_abc123",
"name": "search-flights",
"version": 1,
"offer_scope": "restricted"
}Authorizations
Enter your API key (e.g. dai_sk_test_key_001)
Cookies
Body
Validated request for POST /skills.
Uses relaxed name validation (allows underscores, dots, up to 200 chars).
URL-safe slug. Lowercase alphanumeric with hyphens/underscores/dots.
1 - 200"search-flights"
Short one-line description shown in the skill registry.
1 - 1024"Search available flights given origin, destination, and dates."
Human registry title shown instead of the slug name. Optional — the registry humanizes the slug when omitted.
200"Search Flights"
Full SKILL.md body. At least 50 characters of substantive content.
1Alias for body_markdown — accepted for backwards compatibility.
1One of: org, workspace, personal. Public listing is not settable here — use POST /skills/{name}/publish.
"org"
One of: stable, experimental, deprecated.
"stable"
Which agents get this skill. 'restricted' (the default): only agents you add it to (POST /agents/{name}/skills/{skill_id}, or agent_name here). 'workspace': every agent in the org. A deleted skill's name created again gets the same. (Until 2026-10-25 an API key that left it out got 'workspace', and a re-created name kept its previous scope.)
"restricted"
Also add the new skill to this agent (the dashboard sends it when you create a skill from an agent's page). Implies offer_scope='restricted' unless you say otherwise, and records a new config version for that agent.
255"support-agent"
Optional grouping label, e.g. 'tools', 'policies', 'workflows'.
100"tools"
Taxonomy class: capability or preference. Defaults to the frontmatter skill-type: when omitted (legacy model-gap/proprietary/convention labels still accepted).
"preference"
Knowledge scope: public or private. Pairs with skill_type; defaults to the frontmatter skill-scope: when omitted (or the scope a legacy skill-type implies).
"public"
Who may fire the skill: model, user, or any. Defaults to the frontmatter invocation: when omitted, else 'model'.
"model"
Phrases that hint the skill should activate. Used by smart routing.
["find flights", "search for flights"]
Raw frontmatter dict parsed from the SKILL.md header.
Optional message attached to the first version.
Optional label shown as the first version's author in its history (anyone can read it once the skill is published). Who made the version is recorded separately.
Scope the skill to a specific project.
Owning workspace ID. Defaults to the caller's current workspace.
Response
Successful Response
"ok"Stable ID, always prefixed 'sk_'
Starts at 1; increments on each body change
Which agents get the skill, as stored: 'restricted' (only agents you add it to) or 'workspace' (every agent).
The agent the new skill was added to, when agent_name was sent.