Search the docs
Find a page, a section, or an endpoint.
Drafts
Every drafts endpoint, generated from the API's own schemas.
Every draft in one mailbox
get/api/v1/drafts
Newest first, and including scheduled ones — a scheduled message is a draft with `send_at` set. `GET /mailboxes/drafts` lists the same rows as a mail client draws them: sender, subject, preview. This gives the recipients and the body, because a caller here is coming back to finish writing.
Parameters
- identity_iduuid
- limitnumber
- cursorstring
Responses
- 200The drafts.
- 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/drafts \
-H "Authorization: Bearer $PIDGEON_API_KEY"Write one without sending it
post/api/v1/drafts
An agent that drafts a reply, waits for a human to approve it and sends it an hour later is ordinary supervised automation, and this is where the half-written message lives in the meantime. Idempotent with `Idempotency-Key`.
Parameters
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Body
- identity_iduuid
The address this belongs to.
- fromstring
An address on a domain you own, and one this key may send from.
- toemail | email[]
One address or several. A string is accepted for the single case.
- ccemail | email[]
Copied, and visible to everybody who receives the message.
- bccemail | email[]
Copied, and visible to nobody else.
- subjectstring | null
Left off a reply, where the thread already has one.
- textstring | null
The plain-text body. Send this even when you send `html` — some clients only read it.
- dataobject | null
Typed data: `{ schema, body }`, where `schema` is a URL naming what `body` is and `body` is up to 64 KB of JSON. Sent as its own part beside the text, never mixed into it.
- in_reply_to_message_iduuid | null
The same field as `in_reply_to`, under the name this endpoint has always used. Both names work on both endpoints; reach for `in_reply_to`.
- in_reply_touuid | null
The message being answered. Keeps the conversation together in the recipient’s client too.
- attachment_idsuuid[]
Ids from `POST /attachments`, uploaded before the message that carries them. Up to 20.
Responses
- 201The draft.
- 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”.
- 409Nothing was done by this call. `error.code` says which: `idempotency_in_progress` when the first request with this `Idempotency-Key` has not answered yet (wait `Retry-After` seconds and send it again), `idempotency_mismatch` when the key was used with a different request (use a new key).
- 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.
- 504Test mode only: `Pidgeon-Test-Fail: drop` dropped the answer (`test_dropped`). The request was done; retry it with the same `Idempotency-Key` to get the first answer.
curl https://pidgeon.ai/api/v1/drafts \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Fetch one draft
get/api/v1/drafts/{id}
Parameters
- idstring
Responses
- 200The draft.
- 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/drafts/{id} \
-H "Authorization: Bearer $PIDGEON_API_KEY"Change some of it, and leave the rest
patch/api/v1/drafts/{id}
An omitted field survives; `null` clears it. Both are needed, because "I am not touching the subject" and "there is no subject" are different instructions.
Parameters
- idstring
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Body
- fromstring
An address on a domain you own, and one this key may send from.
- toemail | email[]
One address or several. A string is accepted for the single case.
- ccemail | email[]
Copied, and visible to everybody who receives the message.
- bccemail | email[]
Copied, and visible to nobody else.
- subjectstring | null
Left off a reply, where the thread already has one.
- textstring | null
The plain-text body. Send this even when you send `html` — some clients only read it.
- dataobject | null
Typed data: `{ schema, body }`, where `schema` is a URL naming what `body` is and `body` is up to 64 KB of JSON. Sent as its own part beside the text, never mixed into it.
- attachment_idsuuid[]
Ids from `POST /attachments`, uploaded before the message that carries them. Up to 20.
Responses
- 200The draft.
- 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”.
- 409Nothing was done by this call. `error.code` says which: `idempotency_in_progress` when the first request with this `Idempotency-Key` has not answered yet (wait `Retry-After` seconds and send it again), `idempotency_mismatch` when the key was used with a different request (use a new key).
- 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.
- 504Test mode only: `Pidgeon-Test-Fail: drop` dropped the answer (`test_dropped`). The request was done; retry it with the same `Idempotency-Key` to get the first answer.
curl https://pidgeon.ai/api/v1/drafts/{id} \
-X PATCH \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Throw it away
delete/api/v1/drafts/{id}
Parameters
- idstring
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Responses
- 200Deleted.
- 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”.
- 409Nothing was done by this call. `error.code` says which: `idempotency_in_progress` when the first request with this `Idempotency-Key` has not answered yet (wait `Retry-After` seconds and send it again), `idempotency_mismatch` when the key was used with a different request (use a new key).
- 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.
- 504Test mode only: `Pidgeon-Test-Fail: drop` dropped the answer (`test_dropped`). The request was done; retry it with the same `Idempotency-Key` to get the first answer.
curl https://pidgeon.ai/api/v1/drafts/{id} \
-X DELETE \
-H "Authorization: Bearer $PIDGEON_API_KEY"Where a held send stands
get/api/v1/drafts/{id}/approval
A send answered `status: "pending_approval"` is a draft waiting for a person: a first message to someone new on an address that asks first, or a reply on a conversation where a message tried to give the agent instructions. Poll this, or subscribe to `draft.approved`, `draft.rejected` and `draft.expired`. Only a person approves, in the dashboard; no key can, including the one that asked. Unanswered for 24 hours, it expires unsent and stays a draft. After approval `message_id` is the sent message.
Parameters
- idstring
Responses
- 200Where it stands.
- 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/drafts/{id}/approval \
-H "Authorization: Bearer $PIDGEON_API_KEY"Send what is stored
post/api/v1/drafts/{id}/send
No body: the row is the message. The browser passes the composer contents and the draft id together, because the composer *is* the draft; a caller here saved it an hour ago and may be a different process. `PATCH` first to change anything. The id that comes back is the draft's, because a send converts the row.
Parameters
- idstring
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Responses
- 202Accepted.
- 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, and nothing was sent to anybody. `error.code` says why: `blocked` (a rule), `not_permitted` (the send policy), `abuse_blocked` (a sending limit), `exfiltration_blocked` (it carries text a key read in another conversation; the message names it), `taken_over` (a person has this conversation: a key does not send in it until they hand it back) or `forbidden` (the address is not one this key can send from, or is not active).
- 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”.
- 409Nothing was done by this call. `error.code` says which: `idempotency_in_progress` when the first request with this `Idempotency-Key` has not answered yet (wait `Retry-After` seconds and send it again), `idempotency_mismatch` when the key was used with a different request (use a new key).
- 429Too soon, and nothing was sent. `error.code` says which: `rate_limited` (the plan’s five-minute sending limit) or `recipient_cooldown` (this address wrote to that recipient within its `recipient_cooldown_minutes`, and this is not a reply; the message names them). Wait `Retry-After` seconds, or reply in the conversation instead.
- 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.
- 504Test mode only: `Pidgeon-Test-Fail: drop` dropped the answer (`test_dropped`). The request was done; retry it with the same `Idempotency-Key` to get the first answer.
curl https://pidgeon.ai/api/v1/drafts/{id}/send \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Send it later
post/api/v1/drafts/{id}/schedule
A minute from now at the earliest, thirty days at the latest. A refused send unschedules rather than retrying forever, so a draft can come back on its own with `failed_reason` set — read that before scheduling the same message again.
Parameters
- idstring
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Body
- send_atdate-time
Responses
- 200The draft.
- 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”.
- 409Nothing was done by this call. `error.code` says which: `idempotency_in_progress` when the first request with this `Idempotency-Key` has not answered yet (wait `Retry-After` seconds and send it again), `idempotency_mismatch` when the key was used with a different request (use a new key).
- 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.
- 504Test mode only: `Pidgeon-Test-Fail: drop` dropped the answer (`test_dropped`). The request was done; retry it with the same `Idempotency-Key` to get the first answer.
curl https://pidgeon.ai/api/v1/drafts/{id}/schedule \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Back to being an ordinary draft
delete/api/v1/drafts/{id}/schedule
Parameters
- idstring
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Responses
- 200The draft.
- 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”.
- 409Nothing was done by this call. `error.code` says which: `idempotency_in_progress` when the first request with this `Idempotency-Key` has not answered yet (wait `Retry-After` seconds and send it again), `idempotency_mismatch` when the key was used with a different request (use a new key).
- 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.
- 504Test mode only: `Pidgeon-Test-Fail: drop` dropped the answer (`test_dropped`). The request was done; retry it with the same `Idempotency-Key` to get the first answer.
curl https://pidgeon.ai/api/v1/drafts/{id}/schedule \
-X DELETE \
-H "Authorization: Bearer $PIDGEON_API_KEY"Take a file off a draft
delete/api/v1/drafts/{id}/attachments/{attachmentId}
The object goes with the row. An attachment belongs to its draft rather than being shared, so leaving the bytes behind would be storage nobody can reach and everybody is billed for. `POST /attachments` is how one gets on.
Parameters
- idstring
- attachmentIdstring
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Responses
- 200Deleted.
- 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”.
- 409Nothing was done by this call. `error.code` says which: `idempotency_in_progress` when the first request with this `Idempotency-Key` has not answered yet (wait `Retry-After` seconds and send it again), `idempotency_mismatch` when the key was used with a different request (use a new key).
- 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.
- 504Test mode only: `Pidgeon-Test-Fail: drop` dropped the answer (`test_dropped`). The request was done; retry it with the same `Idempotency-Key` to get the first answer.
curl https://pidgeon.ai/api/v1/drafts/{id}/attachments/{attachmentId} \
-X DELETE \
-H "Authorization: Bearer $PIDGEON_API_KEY"