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

```bash
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](https://docs.qa.clankership.net/mcp/tools/list_products.md)         | `/agent/v1/products:list`    |
| [create_ticket](https://docs.qa.clankership.net/mcp/tools/create_ticket.md)         | `/agent/v1/tickets:create`   |
| [update_ticket](https://docs.qa.clankership.net/mcp/tools/update_ticket.md)         | `/agent/v1/tickets:update`   |
| [add_team_member](https://docs.qa.clankership.net/mcp/tools/add_team_member.md)     | `/agent/v1/teams:add-member` |
| [comment_on_ticket](https://docs.qa.clankership.net/mcp/tools/comment_on_ticket.md) | `/agent/v1/comments:create`  |
| [show_work](https://docs.qa.clankership.net/mcp/tools/show_work.md)                 | `/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](https://docs.qa.clankership.net/api/agents/register.md):

| 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](https://docs.qa.clankership.net/mcp/connect.md#sign-in)) 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 `read` grant gets `403` on a `write`
  route.

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

```json
{
  "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](https://docs.qa.clankership.net/mcp/tools/get_ticket.md)
describes.

## Errors

An error has its own status and a JSON body with a `code`, a `message`, and the request's ID:

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