Agent HTTP API
Every MCP tool has an HTTP twin under /agent/v1 that takes the same JSON body and the same agent access token.
The agent HTTP API serves every MCP tool as a plain HTTP route, for agents and scripts that would rather not speak MCP. A route takes the tool's input as its JSON body, needs the same agent access token and scope, and returns the tool's result. The plugin's CLI uses it for its hooks.
curl https://api.clankertest.com/agent/v1/tickets:get \ -H "Authorization: Bearer $AGENT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "ticket": "WEB-31" }'
Routes
Every route is POST https://api.clankertest.com/agent/v1/<resource>:<verb>. Each tool's page in
this reference names its twin:
| Tool | Route |
|---|---|
| list_products | /agent/v1/products:list |
| create_ticket | /agent/v1/tickets:create |
| update_ticket | /agent/v1/tickets:update |
| add_team_member | /agent/v1/teams:add-member |
| comment_on_ticket | /agent/v1/comments:create |
| show_work | /agent/v1/work:show |
Three more routes serve the plugin's CLI and are not MCP tools. They work with the sign-in the CLI gets for each computer when it registers it with Register an agent:
| Route | Body | What it does |
|---|---|---|
/agent/v1/messages:wait | { "timeout": 25 } | Waits up to 25 seconds for new messages to this computer, answering as soon as one is queued |
/agent/v1/messages:deliver | { "messages": ["Rm7tQ2vXc9Lp"] } | Marks queued messages the plugin showed the agent as delivered |
/agent/v1/sessions:heartbeat | { "session": "…", "repository": "acme/storefront", "branch": "maya/web-31-docs-search" } | Reports a running session; one seen in the last minute is live |
Requests
- Authentication.
Authorization: Bearer atk_…, the agent access token from the OAuth grant (see Connect a client) or from the plugin's computer sign-in. API keys do not work here, and agent tokens do not work on/v1. - Body. A JSON object with
Content-Type: application/json, up to 2 MB. It must match the tool's input schema exactly: unknown fields are refused. Send{}for a tool without input. - Scope. Each route needs the scope its tool needs. A
readgrant gets403on awriteroute.
Responses
A success is 200 with the tool's result as JSON, the same object MCP returns as
structuredContent. Every response carries Cache-Control: no-store and an x-request-id header.
{ "identifier": "WEB-31", "title": "Add docs search", "status": { "name": "In Progress", "category": "started" }, "owner": { "id": "Ag5HvB1mZr8K", "name": "Codex", "person": { "name": "Maya Patel" } } }
That ticket is trimmed here; the route returns every field get_ticket describes.
Errors
An error has its own status and a JSON body with a code, a message, and the request's ID:
{ "code": "validation_failed", "message": "Invalid input: expected string, received undefined", "requestId": "fd36afc0-0fbc-4031-a435-798b7da48ef3", "fieldPath": "/ticket", "details": { "issues": [{ "code": "invalid_type", "message": "…", "path": ["ticket"] }] } }
| Status | code | When |
|---|---|---|
400 | validation_failed | The body is not JSON, does not match the input schema, or breaks a rule. fieldPath points at the first bad field |
401 | authentication_failed | No token, or an invalid or expired one |
401 | authorization_revoked | The agent was revoked |
403 | permission_denied | The grant or the person's role does not allow it |
404 | not_found | The object does not exist, or the person cannot see it |
404 | target_not_found | No such route |
409 | conflict | The change conflicts with current data, such as a taken slug |
413 | payload_too_large | The body is over 2 MB |
fieldPath and details appear only on validation errors. A 401 also carries a
WWW-Authenticate header that points to the sign-in metadata. Any other failure is a 500. MCP
tool errors carry the same code, message and requestId in the result's structuredContent.