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
}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
}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"
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 author label for the first version.
Scope the skill to a specific project.
Owning workspace ID. Defaults to the caller's current workspace.