Agents API
The /api/agents endpoints manage agents in the current workspace. This is the application API — authenticate with a JWT access token from POST /api/auth/login, sent as a bearer token. Errors use the { "detail": "..." } shape; see the API Reference for the full auth and error model.
Status codes to expect
Create returns 200 (not 201). Accessing an agent you don't own returns 404 (not 403), so the existence of other users' agents is never leaked.
Endpoints
POST /api/agents # create an agent
GET /api/agents # list agents
GET /api/agents/{agent_id} # get an agent
PUT /api/agents/{agent_id} # update an agent
DELETE /api/agents/{agent_id} # delete an agent
POST /api/agents/preview # dry-run a configuration
POST /api/agents/optimize-instructions # refine instructions
POST /api/agents/{agent_id}/publish
POST /api/agents/{agent_id}/unpublish
POST /api/agents/{agent_id}/logo
POST /api/agents/{agent_id}/api-key # mint runtime key
GET /api/agents/{agent_id}/api-key # key metadata
DELETE /api/agents/{agent_id}/api-key # revoke runtime key
POST /api/agents/from-template # create from a template
POST /api/agents/from-template/resolve # reuse-or-create from a template
GET /api/agents/{agent_id}/share-link # share-link state
POST /api/agents/{agent_id}/share-link # enable sharing
POST /api/agents/{agent_id}/share-link/rotate # replace share token
DELETE /api/agents/{agent_id}/share-link # disable sharing
GET /api/agents/{agent_id}/widget-key # widget key
POST /api/agents/{agent_id}/widget-key/rotate # replace widget key
POST /api/agents/{agent_id}/promote-team # share with the team
POST /api/agents/{agent_id}/demote-personal # make personal againCreate an Agent — POST /api/agents
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Display name. 1–200 characters. |
| description | string | No | Short summary of the agent. |
| instructions | string | No | System prompt for the agent. |
| execution_mode | string | No | One of flash, balanced, think, auto. Default balanced. |
| models | object | No | Model assignments by slot (integer model ids). |
| knowledge_bases | array | No | Knowledge bases to retrieve from. |
| skills | array | No | Skills the agent can recall. |
| tool_categories | array | No | Tool categories the agent may use. |
| suggested_prompts | array | No | Starter prompts shown to users. |
| logo_base64 | string | No | Agent logo as a data URL. |
Request:
curl -X POST http://localhost:8000/api/agents \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Research Assistant",
"description": "Competitor analysis on demand",
"instructions": "Produce concise, sourced competitor reports.",
"execution_mode": "balanced",
"skills": ["evidence-based-rag"],
"tool_categories": ["basic", "file"],
"suggested_prompts": ["Summarize the market"]
}' Response (200):
{
"id": 42,
"user_id": 7,
"name": "Research Assistant",
"description": "Competitor analysis on demand",
"instructions": "Produce concise, sourced competitor reports.",
"execution_mode": "balanced",
"models": null,
"knowledge_bases": [],
"skills": ["evidence-based-rag"],
"tool_categories": ["basic", "file"],
"suggested_prompts": ["Summarize the market"],
"logo_url": null,
"status": "active",
"published_at": null,
"created_at": "2026-07-10T09:15:00Z",
"updated_at": "2026-07-10T09:15:00Z",
"widget_enabled": false,
"allowed_domains": []
}Errors: 400 "Agent with this name already exists", 400 for an unknown knowledge base or tool category, 401 (missing/invalid token), 500.
List Agents — GET /api/agents
Returns agents visible to the caller, each with the caller's permissions on it.
Response (200):
[
{
"id": 42,
"name": "Research Assistant",
"description": "Competitor analysis on demand",
"logo_url": null,
"status": "active",
"created_at": "2026-07-10T09:15:00Z",
"updated_at": "2026-07-10T09:15:00Z",
"widget_enabled": false,
"allowed_domains": [],
"access": "owner",
"readonly": false,
"can_edit": true,
"can_publish": true,
"can_delete": true
}
]Get an Agent — GET /api/agents/{agent_id}
Response (200): the full agent object (same shape as the create response). Errors: 404 "Agent not found".
Update an Agent — PUT /api/agents/{agent_id}
Send only the fields you want to change; omitted fields are left untouched. Adds widget_enabled and allowed_domains on top of the create fields.
Request:
{
"instructions": "Always cite sources inline.",
"widget_enabled": true,
"allowed_domains": ["example.com"]
}Response (200): the updated agent object. Errors: 404 "Agent not found", 400 duplicate name or invalid knowledge base / tool.
Delete an Agent — DELETE /api/agents/{agent_id}
Response (200):
{ "message": "Agent deleted successfully" }Errors: 404 "Agent not found".
Runtime Keys
Mint, inspect, and revoke the agent's runtime key — the xag_<prefix>_<secret> key used to run tasks through the Workspace SDK. These endpoints take no request body (agent id in the path + your JWT).
Mint / rotate — POST /api/agents/{agent_id}/api-key
Issues a new runtime key, revoking any existing one. The full_key is returned once.
{
"full_key": "xag_Ab3xY9_Qw7Rt2Kp9Lm4Nz8Vc1Bd6Hf5Jg0Xs3Tr",
"key_prefix": "Ab3xY9",
"created_at": "2026-07-10T09:15:00Z"
}Errors: 404 "Agent not found", 409 "rotation_conflict" (concurrent rotation — retry).
Metadata — GET /api/agents/{agent_id}/api-key
Returns non-secret metadata for the active key. Never returns full_key.
{
"key_prefix": "Ab3xY9",
"masked_key": "xag_Ab3xY9_••••••••",
"created_at": "2026-07-10T09:15:00Z"
}Errors: 404 "no_active_key" (agent has no active key), 404 "Agent not found".
Revoke — DELETE /api/agents/{agent_id}/api-key
Idempotent — revoking when no key is active returns revoked: false.
{ "revoked": true, "revoked_at": "2026-07-10T10:00:00Z" }Share Links
Owner-only endpoints that manage an agent's public guest link. All four return the same body, including the raw token — treat the response as sensitive. See Sharing Agents for the concepts.
GET /api/agents/{agent_id}/share-link # state only — never mints a token
POST /api/agents/{agent_id}/share-link # enable (mints only if absent)
POST /api/agents/{agent_id}/share-link/rotate # always mints a new token
DELETE /api/agents/{agent_id}/share-link # disable and clear the tokenResponse (200): the same shape for every operation. After a disable, share_token is null.
{
"agent_id": 42,
"share_enabled": true,
"share_token": "shr_8fa2c1d4e6b7",
"share_updated_at": "2026-08-10T09:15:00Z"
}None of these take a request body. Errors: 400 "Only published agents can be shared" (enable and rotate only), 404 "Agent not found", 500. Note that disable responds 200 with a body, not 204.
Widget Keys
Owner-only key used by the embeddable chat widget.
GET /api/agents/{agent_id}/widget-key # read (creates one if absent)
POST /api/agents/{agent_id}/widget-key/rotate # replace the key{
"agent_id": 42,
"widget_enabled": true,
"widget_key": "wgt_51c8a0f3d92b"
}Rotation breaks deployed embeds
Rotating invalidates the previous key, so any widget snippet already installed on a site stops working until it is updated. Note also that the GET generates and stores a key when the agent does not yet have one.
Errors: 404 "Agent not found", 500.
Team Visibility
Move an agent between personal and team ownership.
POST /api/agents/{agent_id}/promote-team # share with the team
POST /api/agents/{agent_id}/demote-personal # make personal againPromote request — the body is optional; omitting it defaults to team:
{ "visibility": "team" } // or "admins"Both return the full agent object (200). demote-personal takes no body.
Errors: 400 "No team to promote into", 404 "Agent not found", 403 when permissions do not allow the change, 500. Promotion also returns 422 when the agent depends on resources the team cannot see — and unusually for this API, the detail is an object naming them:
{
"detail": {
"message": "Agent uses knowledge bases not shared with the team",
"unshared_knowledge_bases": ["research-notes"]
}
}The connector variant reports unshared_connectors instead. Share or promote those resources first, then retry.
Create from a Template
Two flows create an agent from a template:
POST /api/agents/from-template # always creates a new named agent
POST /api/agents/from-template/resolve # reuses your existing agent for that templateRequest (both): template_id is required; name is optional.
{ "template_id": "marketing-content-agent", "name": "My Marketing Agent" }Resolve response (200): wraps the agent with a created flag.
{
"agent": { "id": 42, "name": "Content Agent", "status": "published" },
"created": true
}created: false means an agent you already have for this template was returned as-is — any name you sent is ignored. Use this flow for a quick-access entry point; use plain from-template when you want a distinct new agent each time.
Errors: 404 "Template not found", 400 for a workforce template ("This template creates a workforce, not a single agent; use it from the Templates page instead"), 400 "Agent with this name already exists", 409 "Too many concurrent requests for this template; please retry", 500.