Search the docs
Find a page, a section, or an endpoint.
API keys
Every api keys endpoint, generated from the API's own schemas.
List the keys in this workspace
get/api/v1/api-keys
Names, prefixes, what each reaches and when it was last used — never a secret. The list is what you read before revoking something.
Responses
- 200A page of keys.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `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.
- 404No 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”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/api-keys \
-H "Authorization: Bearer $PIDGEON_API_KEY"Mint a key
post/api/v1/api-keys
`identity_ids` is required and must be a subset of what the calling key already reaches: a key cannot delegate reach it does not have. The `token` is in this response and nowhere else, ever.
Body
- namestring
- identity_idsuuid[]
- pod_iduuid
The pod this belongs to — your account’s default when you name none. A pod is a sending boundary: mail from it lands on its own reputation, so one of your customers cannot damage another’s delivery. It does not change what an address can do, and no key is scoped to one.
- accessenum
What a key may do. Give an agent the narrowest one that works.
read · send · admin
- expires_atdate-time | null
When it stops accepting mail. Expiry suspends; it never deletes, so the correspondence survives.
- testboolean
A test-mode key (`pgn_test_`): everything works, nothing is sent outside Pidgeon, and nothing counts against the plan. A test key mints only test keys.
Responses
- 201The key, with its token.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 402Your plan does not include this. `error.message` says what does.
- 403Refused. `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.
- 404No 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”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/api-keys \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Fetch one key
get/api/v1/api-keys/{id}
What it reaches and when it was last used. Never the secret — that is shown once at creation and nothing can produce it again.
Parameters
- idstring
Responses
- 200The key.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `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.
- 404No 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”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/api-keys/{id} \
-H "Authorization: Bearer $PIDGEON_API_KEY"Rename a key
patch/api/v1/api-keys/{id}
Its name and its secret (`POST /api-keys/{id}/rotate`) are the only things about a key that can change. Not its scope — a key's reach is what somebody granted when they made it, and widening that in place would let a credential already in circulation reach further than whoever issued it agreed to.
Parameters
- idstring
Body
- namestring
Responses
- 200The key.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `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.
- 404No 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”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/api-keys/{id} \
-X PATCH \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Revoke a key, and everything it minted
delete/api/v1/api-keys/{id}
Transitive, and that is the point: a leaked key would otherwise mint a second one and survive its own rotation. A key may revoke itself and keys it made — not a sibling, and not its parent.
Parameters
- idstring
Responses
- 200Revoked.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `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.
- 404No 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”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/api-keys/{id} \
-X DELETE \
-H "Authorization: Bearer $PIDGEON_API_KEY"Give a key a new secret
post/api/v1/api-keys/{id}/rotate
A new secret for the same key id. Everything that hangs off the id is unchanged — its scope, its ceiling, its expiry, and **every key it minted**, which keep working — so rotating an admin key does not take its children down. Revoking is still how a key and its children end together. The old secret stops now, or after `grace_period_seconds` (at most 86400, a day), so the servers that hold it can move over. One previous secret at a time: rotating again inside the window ends the older one immediately. A key may rotate itself and keys it minted; a person must own the workspace. The `token` is in this response and nowhere else, ever.
Parameters
- idstring
Body
- grace_period_secondsnumber
Responses
- 200The key, with its new token.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `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.
- 404No 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”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/api-keys/{id}/rotate \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'