xAI Skills API (/v1/skills) — hosted skills
Status: all five routes DOCUMENTED (OpenAPI only) + ACCOUNT_RESTRICTED — every call with our key returned 404 on 2026-09-18 ({"error":{"code":404,"message":"The requested resource was not found…"}} for GET, text/plain 404 for POST). No public docs page mentions /v1/skills; the only reference is sources/xai/openapi/openapi.json (5 operations, 5 schemas). A 404 does not mean the API does not exist (gated rollout is the likely explanation) — treat shapes below as OpenAPI-derived and untested.
Sources: OpenAPI https://docs.x.ai/openapi.json (local sources/xai/openapi/openapi.json, operations handle_list_skills_request, handle_upload_skill_request, handle_retrieve_skill_request, handle_delete_skill_request, handle_download_skill_content_request; schemas Skill, SkillList, UploadSkillMultipartRequest, DeletedSkill, ListSkillsParams, LocalShellSkill, ShellEnvironment) · Grok Build Skills, Plugins & Marketplaces (SKILL.md format) · Grok Bot Skills and routines.
Last verified: 2026-09-18 (raw tmp-live/xai-media/skills-*.json).
Machine-readable: generated/fragments/endpoints/xai-media-voice-skills.json (api_family skills), parameters/xai-skills.json, objects/xai-media-objects.json (Skill, SkillList, DeletedSkill, LocalShellSkill).
What a skill is
A skill is a directory whose SKILL.md starts with YAML frontmatter (name, description, optional when-to-use, paths, allowed-tools, argument-hint, user-invocable, disable-model-invocation, metadata) followed by Markdown instructions; it may carry scripts and resources. The same format is used by Grok Build (discovered from .grok/skills/, ~/.grok/skills/, plugins, ~/.agents/skills/, and Claude Code / AGENTS.md layouts) and by Grok Bot (private skill library, Marketplace). The hosted Skills API stores such a directory (zipped) server-side and extracts name/description from the frontmatter.
Endpoints (OpenAPI)
| Method / path | Request | Response | Documented errors | Live (our key) |
|---|---|---|---|---|
GET /v1/skills |
query limit (1–100, default 100), after (cursor = last id), order (asc|desc, default desc) |
SkillList {object:"list", data:[Skill], first_id, last_id, has_more} |
— | 404 JSON |
POST /v1/skills |
multipart/form-data, field files: one zip or the field repeated per file of a directory upload |
Skill |
400 invalid upload, 413 payload too large | 404 text/plain (zip, single file, JSON body) |
GET /v1/skills/{skill_id} |
path skill_id |
Skill |
404 "Skill not found." | 404 JSON |
DELETE /v1/skills/{skill_id} |
path | DeletedSkill {id, object:"skill.deleted", deleted} |
404 | not attempted |
GET /v1/skills/{skill_id}/content |
path | raw zip bytes (application/zip, chunked) |
404 | not attempted |
Skill = {id, object:"skill", name, description, created_at (unix s), default_version:"1", latest_version:"1"} — versions are "currently always 1".
How skills reach inference
The OpenAPI wires skills into inference only through the shell tool environment: ShellCall.environment = {type:"local", skills:[LocalShellSkill{name, description, path}]} — i.e. the client tells the model which local skill directories exist when it runs shell commands on the model's behalf (Responses API, shell tool; see the tools agent's docs). No request schema references a hosted skill_id yet (no skills: [...] field on /v1/responses or /v1/chat/completions in the spec), so the hosted store appears to be groundwork for Grok Build / Grok Bot sync rather than an inference-time parameter.
Live probe (2026-09-18)
GET /v1/skills?limit=5 → 404 {"error":{"code":404,"message":"The requested resource was not found. Please check the URL and try again. Documentation is available at https://docs.x.ai/"}}
POST /v1/skills files=@atlas-probe-skill.zip → 404 text/plain (122 bytes)
POST /v1/skills files=@SKILL.md → 404 text/plain
GET /v1/skills/skill_does_not_exist → 404 (same JSON envelope)The test (tests/xai/test_skills.py) asserts the current behaviour (404 with the generic envelope) and will fail loudly when the API opens up, so the atlas can be re-verified. Example examples/xai/skills/skills_crud.py performs list → upload → get → content → delete and stops at the first non-2xx.