---
title: "Claim"
method: POST
path: "/agent-keys/claim"
tags: ["agent-keys"]
---

# Claim

`POST /agent-keys/claim`

## Request

- Base URL: `https://app.zeno-works.ch` (the document declares no server; this is the origin it was published from)
- URL: `https://app.zeno-works.ch/agent-keys/claim`
- Auth: none declared

**Public** — the agent polls with its claim token. `pending` is HTTP **200**.

Shares SP-15's short-poll semantics and its invariant-I6 contract: a 4xx would be
shaped by the mcp refusal middleware into "do not retry", appended to the one
response whose entire contract is *retry it*.

**This route serves BOTH kinds, and that is load-bearing rather than incidental.**
R-8 removed `/signup/claim`, which was the shared one; `claim_status` → `_live` looks
a row up by claim-token hash with no `kind` predicate at any layer, so a signup row
resolves here too. Every sibling in this router (`claim_status_by_id`,
`waiting_for_email`, `approve_pairing`) DOES filter `kind == pairing`, which makes
"add the same filter here for consistency" the obvious-looking tidy-up — and it would
leave the signup half of one state machine with no claim route at all.
`test_the_claim_route_serves_a_signup_row_too` exists to make that change red.

**A browser registration's token resolves here too, and mints nothing.** Those rows are
written `key_purpose=unspecified`, which `issue_claimed_key` refuses outright. That is
the single guard separating a status-reading token from a key-minting one, so it is
asserted end to end rather than assumed.

## Request body

- PairingClaimIn
  - `claim_token` string, required

## Response `200`

Successful Response

- PairingClaimOut: Shares SP-15's short-poll semantics and its **HTTP 200 `pending`** contract. ``pending`` | ``ready`` | ``collected`` | ``denied`` | ``expired``. ``collected``: this request already issued its key, and this poll issued nothing (ZCT-806). A 4xx would be shaped by the mcp refusal middleware into "do not retry", which kills a protocol whose entire design is that the agent keeps polling while a human approves.
  - `status` string, required
  - `api_key` string, nullable
  - `scope` string, nullable
  - `company_id` string, nullable
  - `poll_interval_seconds` integer, nullable

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zeno-works/apis/zeno-api.md) · [All operations](https://skmtc.dev/zeno-works/apis/zeno-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zeno-works/apis/zeno-api/revisions/1eb93dde146c?raw)
