SkillGild

API and execution reference

Endpoints, authentication, run payloads and responses, idempotency, webhooks, error codes, SDKs, CLI and MCP tools.

15 min readReference

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
sh
curl https://api.skillgild.com/v1
# {"name":"SkillGild API","version":"v1"}

#Authentication

Send every credential as a bearer token:

http
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 active and not banned;
  • the key has the runtime:execute scope.

#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.
sh
curl "https://api.skillgild.com/v1/skills?q=accessibility&limit=20"
json
{
  "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:

json
{
  "input": {
    "prompt": "Audit this signup form: email field without label, 12px placeholder text, red-only error state."
  }
}
  • input is required and must be a JSON object.
  • The request body is capped at 2 MiB, and the serialized input at the policy's max_input_bytes (64 KiB by default).
  • input is checked against the skill's input schema: every required key must be present, and each declared top-level property must match its type (string, number or integer, boolean, object, array). Undeclared keys are allowed.

Response 200:

json
{
  "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:

sh
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):

ts
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):

python
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):

go
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:

sh
skillgild run accessibility-audit --input '{"prompt":"Audit this signup form"}' \
  --idempotency-key 8d9ba436-a430-44af-9d12-f1d3d4198227

MCP (what an agent sends to skillgild mcp):

json
{
  "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.

  1. Policy. Load runtime policy. If the global runtime is paused, return runtime_paused.
  2. Input size. The input must be an object within max_input_bytes.
  3. Idempotent replay. If the key matches a completed run, replay its stored response.
  4. Resolve. Find the published hosted skill and its latest published version. The version must use prompt_pipeline.
  5. Free tier. For a free skill, the free tier must not be paused.
  6. Validate. Check the input against the input schema.
  7. Rate limits: per user and version, per API key, and per client IP.
  8. Claim idempotency key.
  9. Provider health. The circuit breaker must be closed and the provider configured.
  10. 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 running and reserve one run.
  11. Abuse protection. Record privacy-preserving abuse signals, never the input text.
  12. Decrypt. Audit the access, then decrypt the version's private prompt.
  13. Capacity. Reserve capacity against the platform's daily model budget.
  14. Call the model, with a 90-second timeout and up to 4,096 output tokens.
  15. Settle. Record usage and enforce the 1 MiB output limit.
  16. Commit. Count the run, mark the execution completed, queue execution.completed webhooks, 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.

text
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.

json
{
  "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.

json
{ "data": { "status": "authorization_pending" } }
json
{ "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:

http
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…
json
{
  "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:

ts
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.