API and execution reference
Endpoints, authentication, run payloads and responses, idempotency, webhooks, error codes, SDKs, CLI and MCP tools.
This page covers how applications and agents call SkillGild: base URLs, authentication, every public and account endpoint, the run pipeline, idempotency, webhooks and error codes.
#Conventions
| Base URL | https://api.skillgild.com/v1 |
| Format | JSON request and response bodies |
| Success | { "data": … } |
| Error | { "error": { "code": "snake_case_code", "message": "Human-readable text" } } |
| Identifiers | Any {id-or-slug} accepts a skill's UUID or its slug |
| Health | GET /healthz and GET /readyz, outside /v1 |
curl https://api.skillgild.com/v1
# {"name":"SkillGild API","version":"v1"}#Authentication
Send every credential as a bearer token:
Authorization: Bearer <token>A token that starts with sg_live_ is treated as an API key. Anything else is treated as a session token (JWT).
| Credential | Format and lifetime | How to get it | Accepted by |
|---|---|---|---|
| API key | sg_live_ + 64 hex characters. 90-day default expiry, 365 days at most. |
Account → Agent connections, POST /v1/me/api-keys, or skillgild login |
POST /skills/{id-or-slug}/run, GET /me, DELETE /me/api-keys/current |
| Session token | JWT, expires after 15 minutes | GET https://skillgild.dev/api/auth/token while signed in |
Everything above, plus all /v1/me/* routes and POST /v1/reports |
#How an API key is validated
SkillGild accepts an API key only if all of these hold:
- the key exists and has not been revoked;
- it has not expired;
- if it belongs to a connected device, that device has not been revoked;
- the owning user is
activeand not banned; - the key has the
runtime:executescope.
#Authentication errors
| Status | code |
Meaning |
|---|---|---|
| 401 | unauthorized |
No bearer token was sent |
| 401 | invalid_api_key |
The key is unknown, expired or revoked |
| 401 | invalid_token |
The JWT is invalid or expired. Sign in again. |
| 403 | insufficient_scope |
The key lacks runtime:execute |
| 403 | account_unavailable |
The account is banned or missing |
| 503 | auth_unavailable |
Authentication is temporarily unavailable. Retry shortly. |
#Catalog (public, no auth)
#GET /v1/skills
Lists published, public, hosted skills, ordered by name.
| Query | Rule |
|---|---|
q |
Optional. Up to 200 characters. Case-insensitive substring match on name, slug and description. |
limit |
1 to 100. Defaults to 50. |
curl "https://api.skillgild.com/v1/skills?q=accessibility&limit=20"{
"data": {
"items": [
{
"id": "0b6f8f9e-4c4d-4d0a-9d3e-2f1c8a7b6e5d",
"slug": "accessibility-audit",
"name": "Accessibility audit",
"description": "Reviews a UI description or component code and returns prioritized WCAG 2.2 issues with fixes.",
"distribution_mode": "hosted_protected",
"access_tier": "free",
"price_amount_minor": 0,
"price_currency": "USD",
"free_runs_per_month": 25,
"current_version": "1.2.0",
"input_schema": { "type": "object", "required": ["prompt"], "properties": { "prompt": { "type": "string" } } },
"category": "Design",
"category_slug": "design",
"creator_name": "Ada Studio",
"visibility": "public",
"updated_at": "2026-09-14 10:12:03.511+00"
}
]
}
}category, category_slug and creator_name are omitted when empty. There is no cursor pagination; a single page returns at most 100 skills.
#GET /v1/skills/{id-or-slug}
Returns one published hosted skill, public or unlisted, with the same fields as the catalog except category, category_slug and creator_name. An unknown or unavailable skill returns 404 skill_not_found.
#Marketplace content
| Endpoint | Returns |
|---|---|
GET /v1/categories |
Active categories: id, parent_id, slug, name, short_description, sort_order |
GET /v1/tags |
Public tags |
GET /v1/collections |
Published collections: id, slug, name, description, image_url, skill_count, skill_ids, updated_at |
GET /v1/collections/{slug} |
One collection with its skills array |
GET /v1/featured-skills?slot=home |
Scheduled featured skills for a slot. slot defaults to home. |
GET /v1/featured-collections?slot= |
Featured collections for a slot |
GET /v1/homepage-content?slot= |
Active announcement and editorial blocks |
#Run a skill
#POST /v1/skills/{id-or-slug}/run
| Header | |
|---|---|
Authorization |
Required. Bearer sg_live_… or a user JWT. |
Content-Type |
application/json |
Idempotency-Key |
Optional. A canonical hyphenated UUID. See Idempotency. |
Request body:
{
"input": {
"prompt": "Audit this signup form: email field without label, 12px placeholder text, red-only error state."
}
}inputis required and must be a JSON object.- The request body is capped at 2 MiB, and the serialized
inputat the policy'smax_input_bytes(64 KiB by default). inputis checked against the skill's input schema: everyrequiredkey must be present, and each declared top-level property must match itstype(string,numberorinteger,boolean,object,array). Undeclared keys are allowed.
Response 200:
{
"data": {
"execution_id": "6f1d2c3b-8a9e-4f70-b1c2-d3e4f5a6b7c8",
"skill_id": "0b6f8f9e-4c4d-4d0a-9d3e-2f1c8a7b6e5d",
"skill_slug": "accessibility-audit",
"version": "1.2.0",
"output": "## Findings\n1. **Missing label (WCAG 1.3.1, 4.1.2)**: …",
"usage": {
"used": 3,
"limit": 25,
"reset_at": "2026-11-01T00:00:00Z",
"access_tier": "free"
}
}
}| Field | Meaning |
|---|---|
output |
The model's response as a string, at most 1 MiB by default |
version |
The published version that ran |
usage.used |
Runs counted this month for this skill, including this one |
usage.limit |
Monthly cap. null means unlimited, through a paid entitlement without a limit. |
usage.reset_at |
00:00 UTC on the 1st of next month |
#Examples
curl:
curl -X POST "https://api.skillgild.com/v1/skills/accessibility-audit/run" \
-H "Authorization: Bearer $SKILLGILD_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8d9ba436-a430-44af-9d12-f1d3d4198227" \
-d '{"input":{"prompt":"Audit this signup form"}}'TypeScript (@skillgild/sdk):
import { SkillGildClient, SkillGildError } from "@skillgild/sdk";
const client = new SkillGildClient({ apiKey: process.env.SKILLGILD_API_KEY });
try {
const run = await client.runSkill(
"accessibility-audit",
{ prompt: "Audit this signup form" },
{ idempotencyKey: crypto.randomUUID() },
);
console.log(run.output, run.usage);
} catch (error) {
if (error instanceof SkillGildError) console.error(error.status, error.code, error.message);
else throw error;
}Python (skillgild):
import os, uuid
from skillgild import SkillGildClient
client = SkillGildClient(api_key=os.environ["SKILLGILD_API_KEY"])
run = client.run_skill(
"accessibility-audit",
{"prompt": "Audit this signup form"},
idempotency_key=str(uuid.uuid4()),
)
print(run["output"])Go (github.com/skillgild/skillgild-dev/sdks/go/skillgild):
client, err := skillgild.NewClient("", skillgild.WithAPIKey(os.Getenv("SKILLGILD_API_KEY")))
if err != nil { return err }
run, err := client.RunSkill(ctx, "accessibility-audit", map[string]any{"prompt": "Audit this signup form"})
if err != nil { return err }
fmt.Println(run.Output)Passing "" as the base URL uses the default, https://api.skillgild.com/v1.
CLI:
skillgild run accessibility-audit --input '{"prompt":"Audit this signup form"}' \
--idempotency-key 8d9ba436-a430-44af-9d12-f1d3d4198227MCP (what an agent sends to skillgild mcp):
{
"name": "skillgild_run_skill",
"arguments": {
"skill_id": "accessibility-audit",
"input": { "prompt": "Audit this signup form" }
}
}#What happens during a run
SkillGild performs these steps in order. The first failure stops the run and returns the matching error from Run errors.
- Policy. Load runtime policy. If the global runtime is paused, return
runtime_paused. - Input size. The input must be an object within
max_input_bytes. - Idempotent replay. If the key matches a completed run, replay its stored response.
- Resolve. Find the published hosted skill and its latest published version. The version must use
prompt_pipeline. - Free tier. For a free skill, the free tier must not be paused.
- Validate. Check the input against the input schema.
- Rate limits: per user and version, per API key, and per client IP.
- Claim idempotency key.
- Provider health. The circuit breaker must be closed and the provider configured.
- Reserve (all at once, atomically):
- check the global and per-user concurrency caps;
- check that the account is active;
- for a paid skill, find the personal entitlement, then fall back to an organization entitlement;
- check the monthly quota;
- create the execution with status
runningand reserve one run.
- Abuse protection. Record privacy-preserving abuse signals, never the input text.
- Decrypt. Audit the access, then decrypt the version's private prompt.
- Capacity. Reserve capacity against the platform's daily model budget.
- Call the model, with a 90-second timeout and up to 4,096 output tokens.
- Settle. Record usage and enforce the 1 MiB output limit.
- Commit. Count the run, mark the execution
completed, queueexecution.completedwebhooks, and store the encrypted idempotent response.
A failure after step 10 marks the execution failed, releases the reserved run and sends execution.failed webhooks. If the model provider fails repeatedly, SkillGild briefly pauses new runs to protect your allowance (503 provider_circuit_open).
#Idempotency
Send Idempotency-Key: <uuid> to make a retry safe. Keys are scoped to your user account and kept for 24 hours. A request is identified by the skill reference plus the exact input.
| Situation | Result |
|---|---|
| Same key, same request, first run completed | 200, the original response is replayed (still subject to current account and entitlement checks) |
| Same key, different skill or input | 409 idempotency_conflict |
| Same key while the first attempt is still running | 409 execution_in_progress with Retry-After: 2 |
| The first attempt's lease expired with an unknown outcome | 409 execution_outcome_unknown |
| The first attempt failed | 409 execution_attempt_failed |
| The key is not a canonical UUID | 400 invalid_idempotency_key |
SkillGild never retries a model call on its own, because a call that timed out may still have been processed. To deliberately start another run, use a new key. The CLI's run command accepts --idempotency-key; the MCP tool does not send one.
#Run errors
| Status | code |
When |
|---|---|---|
| 400 | invalid_input |
The body is not { "input": {…} }, is too large, or the input fails the schema. The message names the field. |
| 400 | invalid_idempotency_key |
The Idempotency-Key is not a canonical UUID |
| 402 | entitlement_required |
A paid skill with no active entitlement |
| 403 | account_inactive |
The account is suspended or banned |
| 404 | skill_not_found |
Unknown, unpublished or non-hosted skill |
| 409 | idempotency_conflict, execution_in_progress, execution_outcome_unknown, execution_attempt_failed |
See Idempotency |
| 422 | unsupported_runtime |
The version's runtime type or schema cannot be executed |
| 429 | quota_exceeded |
The monthly allowance is used up |
| 429 | rate_limited |
A per-minute limit was hit. Wait a minute. |
| 429 | concurrency_limit |
Too many runs in progress. Retry shortly. |
| 429 | provider_daily_budget_reached, provider_daily_cost_budget_reached |
Hosted capacity for today is used up. Retry-After gives the seconds until UTC midnight. |
| 503 | runtime_paused, free_tier_paused |
Runs are temporarily paused for maintenance |
| 503 | provider_circuit_open |
Briefly paused after repeated model provider errors |
| 503 | provider_pricing_required |
Hosted runs are temporarily unavailable |
| 503 | runtime_unavailable |
Hosted runs are temporarily unavailable. Retry shortly. |
| 500 | runtime_failed |
Any other failure. The run is not counted. |
#Default limits
| Policy | Default |
|---|---|
| Free runs per skill per month | 25 |
| Runs per minute: per user and version, per key, per IP | 20, 10, 60 |
| Concurrent runs: per user, global | 5, 100 |
| Input and output size | 64 KiB, 1 MiB |
| Execution timeout | 90 seconds |
| Max completion tokens | 4,096 |
These are the standard limits; enterprise limits can differ.
#Account and keys
Every endpoint in this section needs a session token, except the two marked otherwise.
| Method and path | Body or result |
|---|---|
GET /v1/me |
Accepts an API key or session token. Returns { id, email, name, auth_type }, where auth_type is api_key or user_jwt. |
GET /v1/me/api-keys |
Key metadata: id, name, prefix, scopes, last_used_at, expires_at, created_at. Never the secret. |
POST /v1/me/api-keys |
Body { "name": "CI runner", "expires_at": "2027-01-01T00:00:00Z" }. name is 1 to 80 characters; expires_at is optional RFC 3339, at most 365 days away. Returns 201 with the same fields plus secret, shown once. A sixth active key returns 409 api_key_limit. |
DELETE /v1/me/api-keys/{key-id} |
Revoke one of your keys |
DELETE /v1/me/api-keys/current |
Accepts an API key. Revokes the key that made the request; used by skillgild logout. |
GET /v1/me/devices |
Connected devices: id, name, client_type, created_at, last_used_at, credential_expires_at |
DELETE /v1/me/devices/{device-id} |
Revoke a device and its key immediately |
#Device authorization (CLI login)
This is the flow skillgild login uses. A custom client can implement the same steps.
CLI API Browser (signed in)
│ POST /device-authorizations ───▶ │ │
│ ◀── device_code, user_code ───── │ │
│ open verification_uri ─────────────────────────────────────────────────▶ │
│ │ ◀── POST /me/device-authorizations/approve {user_code}
│ POST /device-authorizations/token every 5 s ──▶ │
│ ◀── { status: "authorized", access_token: "sg_live_…" } │1. Start. POST /v1/device-authorizations needs no auth. The body { "device_name": "…", "client_type": "…" } is optional.
{
"data": {
"device_code": "…",
"user_code": "ABCD-EFGH",
"verification_uri": "https://skillgild.dev/connect/device?user_code=ABCD-EFGH",
"expires_at": "2026-10-02T12:10:00Z",
"interval_seconds": 5
}
}2. Approve. The signed-in user approves the code on the /connect/device page, which calls POST /v1/me/device-authorizations/approve with { "user_code": "ABCD-EFGH" }.
3. Poll. POST /v1/device-authorizations/token needs no auth. Send { "device_code": "…" } and repeat until it succeeds.
{ "data": { "status": "authorization_pending" } }{ "data": { "status": "authorized", "access_token": "sg_live_…", "token_type": "Bearer", "key_prefix": "sg_live_1a2b3c4d" } }The access token is returned exactly once. Codes expire after 10 minutes. Starting a flow is limited to 5 per 10 minutes per IP, and polling to 30 per minute.
| Status | code |
Meaning |
|---|---|---|
| 401 | invalid_device_code |
Unknown or expired code |
| 404 | invalid_code |
On approve: the user code is invalid, expired or already used |
| 409 | device_code_used |
The credential was already issued |
| 409 | api_key_limit |
The user already has 5 active keys |
| 429 | slow_down, rate_limited |
Polling or starting too fast |
#Webhooks
Get a signed notification when one of your runs finishes. These endpoints need a session token.
| Method and path | Purpose |
|---|---|
POST /v1/me/webhooks |
Body { "url": "https://…", "events": ["execution.completed", "execution.failed"] }. The URL must be HTTPS with no query string or fragment. Returns the endpoint with signing_secret, shown once. |
GET /v1/me/webhooks |
Your endpoints: id, url, events, active, created_at |
DELETE /v1/me/webhooks/{id} |
Disable an endpoint |
GET /v1/me/webhooks/{id}/deliveries?limit=25 |
Recent deliveries (1 to 100): status, attempt_count, last_http_status, last_error_code, delivered_at |
POST /v1/me/webhooks/{id}/deliveries/{delivery}/replay |
Re-send a failed delivery |
Delivery request:
POST https://your.app/hooks/skillgild
Content-Type: application/json
User-Agent: SkillGild-Webhooks/1.0
X-SkillGild-Event: execution.completed
X-SkillGild-Delivery: 3c1e…
X-SkillGild-Signature: t=1791028800,v1=5f2b…{
"id": "3c1e…",
"type": "execution.completed",
"created_at": "2026-10-02T12:00:00Z",
"data": {
"execution_id": "6f1d2c3b-…",
"skill_id": "0b6f8f9e-…",
"status": "completed",
"error_code": null
}
}Payloads contain metadata only, never prompts, input or output. Each delivery has a 10-second timeout and does not follow redirects. The worker retries failed deliveries with backoff (up to 5 attempts by default); after that, use the replay endpoint.
Verify the signature. v1 is the hex HMAC-SHA256 of "{t}.{raw body}", keyed with your signing secret:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifySkillGild(rawBody: string, header: string, secret: string, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=") as [string, string]));
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!parts.t || !parts.v1 || age > toleranceSec) return false;
const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(parts.v1, "hex"));
}The timestamp tolerance check is your choice; SkillGild sends the current Unix time in t.
#Creator endpoints
All need a session token and an active creator profile. See Build and publish a skill for the full workflow.
| Method and path | Purpose |
|---|---|
GET /v1/me/creator-applications |
Your latest application and creator status |
POST /v1/me/creator-applications |
Apply: display_name, slug, portfolio_url, experience |
GET /v1/me/creator/studio |
{ active, skills: [{ id, slug, name, status, current_version }] } |
POST /v1/me/creator/skills |
Create a draft skill: slug, name, description |
GET /v1/me/creator/skills/{skill}/versions |
Version history with review status and notes |
POST /v1/me/creator/skills/{skill}/versions |
New draft version: version, release_notes |
GET /v1/me/creator/skills/{skill}/versions/{version}/runtime-config |
{ configured, updated_at } |
PUT /v1/me/creator/skills/{skill}/versions/{version}/runtime-config |
Set the encrypted system_prompt |
POST /v1/me/creator/skills/{skill}/submit |
Submit the latest draft version for review |
#Other account endpoints
| Area | Endpoints |
|---|---|
| Reports | POST /v1/reports (session token, reason 40 to 4,000 characters), GET /v1/me/reports, POST /v1/me/reports/{id}/appeal |
| Notifications | GET /v1/me/notifications, PATCH /v1/me/notifications/{id}/read, PATCH /v1/me/notifications/read-all |
| Support | GET and POST /v1/me/support-cases, GET /v1/me/support-cases/{id}, POST /v1/me/support-cases/{id}/messages |
| Organizations | GET and POST /v1/me/organizations, plus members, invitations, ownership transfer and POST /v1/me/organization-invitations/accept |
| Feature flags | GET /v1/me/feature-flags |
#SDK reference
The TypeScript, Python and Go SDKs are thin clients for the catalog and run endpoints. Every access decision stays on SkillGild's servers.
| Operation | TypeScript | Python | Go |
|---|---|---|---|
| Construct | new SkillGildClient({ apiKey?, baseUrl?, fetch? }) |
SkillGildClient(api_key=None, base_url=…, timeout=100.0) |
skillgild.NewClient(baseURL, skillgild.WithAPIKey(key)) |
| Search | searchSkills(query?, signal?) |
search_skills(query=None) |
SearchSkills(ctx, query) |
| Detail | getSkill(idOrSlug, signal?) |
get_skill(skill_id_or_slug) |
GetSkill(ctx, idOrSlug) |
| Run | runSkill(idOrSlug, input, { idempotencyKey?, signal? }) |
run_skill(ref, input, *, idempotency_key=None) |
RunSkill(ctx, idOrSlug, input, idempotencyKey...) |
| Errors | SkillGildError with .status and .code |
SkillGildError with .status and .code |
returned error |
Search requests limit=100. Run methods require an API key.
#CLI and MCP reference
| Command | What it does |
|---|---|
skillgild login |
Device authorization. Stores the key in the OS credential store. |
skillgild status |
Calls GET /v1/me and prints the connected email |
skillgild search [query] |
Lists up to 100 matching hosted skills |
skillgild install <slug> [--agent …] [--target path] [--force] |
Writes a metadata-only SKILL.md wrapper |
skillgild run <id-or-slug> --input '<json>' [--idempotency-key uuid] |
Runs a skill and prints the JSON result |
skillgild mcp |
Starts the stdio MCP server |
skillgild logout |
Revokes the key remotely and deletes it locally |
The MCP server (skillgild, version 0.1.0) exposes two tools:
| Tool | Arguments | Returns |
|---|---|---|
skillgild_search_skills |
{ "query"?: string } |
Array of public skill objects |
skillgild_run_skill |
{ "skill_id": string, "input": object } |
The run result described above |
Set SKILLGILD_API_URL to point the CLI and MCP server at a different API endpoint.