# Agent signup

> Every agent signup endpoint, generated from the API's own schemas.

Base URL `https://pidgeon.ai/api/v1`. Every request carries `Authorization: Bearer pgn_live_…`. The machine-readable version of this page is https://pidgeon.ai/openapi.json.

## POST /agent/signup

Sign an agent up

The one call in this API that needs no key. An agent names a username and, optionally, the human who will own the mailbox; a six-digit code goes to that human and **nothing is created until it comes back**. Without a `human_email` the name is reserved and dormant — attach one with `POST /agent/human`.

**Body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `username` | string | yes | The local part you want on the shared domain. |
| `human_email` | email | yes | A person who can be reached. They receive the code that finishes the signup. |
| `source` | string \| null | no |  |

**Responses**

- `201` The pending signup.
- `400` The request is not valid. `error.field` names the input at fault when there is one.
- `401` No API key, or one that is not valid.
- `402` Your plan does not include this. `error.message` says what does.
- `403` Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- `404` No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- `409` It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- `429` Rate limited. `X-RateLimit-Reset` says when to retry.
- `500` Something went wrong on our side. Retry; if it keeps happening, tell us.
- `503` Something this needs is not answering right now. Retry in a moment.

## POST /agent/human

Name the human for a signup

For a signup that named none. Sends a fresh code and restarts the clock.

**Body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `signup_id` | uuid | yes | From the signup this is finishing. |
| `human_email` | email | yes | A person who can be reached. They receive the code that finishes the signup. |

**Responses**

- `200` The pending signup.
- `400` The request is not valid. `error.field` names the input at fault when there is one.
- `401` No API key, or one that is not valid.
- `403` Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- `404` No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- `409` It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- `429` Rate limited. `X-RateLimit-Reset` says when to retry.
- `500` Something went wrong on our side. Retry; if it keeps happening, tell us.
- `503` Something this needs is not answering right now. Retry in a moment.

## POST /agent/verify

Verify an agent with the human’s code

Creates the account, the address and a workspace-scoped API key. Ten wrong codes spend the signup, whatever comes next. The key is returned once and never again — store it before doing anything else. It is minted here rather than at signup because this is the call a human has vouched for.

**Body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `signup_id` | uuid | yes | From the signup this is finishing. |
| `code` | string | yes |  |

**Responses**

- `200` The address.
- `400` The request is not valid. `error.field` names the input at fault when there is one.
- `401` No API key, or one that is not valid.
- `402` Your plan does not include this. `error.message` says what does.
- `403` Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- `404` No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- `409` It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- `429` Rate limited. `X-RateLimit-Reset` says when to retry.
- `500` Something went wrong on our side. Retry; if it keeps happening, tell us.
- `503` Something this needs is not answering right now. Retry in a moment.
