Search the docs
Find a page, a section, or an endpoint.
Messages
Every messages endpoint, generated from the API's own schemas.
List messages
get/api/v1/messages
Parameters
- limitnumber
- cursorstring
- thread_iduuid
- bodyenum
original · clean
Responses
- 200A page of messages.
- 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/messages \
-H "Authorization: Bearer $PIDGEON_API_KEY"Send a message
post/api/v1/messages
Idempotent with an `Idempotency-Key` header. Answers `202` with the id, the thread and anything **rejected** — addresses the provider would not accept at hand-off, so read it before assuming every recipient got it. A recipient this address is not allowed to write to is a different thing and refuses the whole send: `403` with `blocked` when a rule says no, `not_permitted` when the send policy does. Nothing is sent to anybody in that case.
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
- fromemail
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
Left off a reply, where the thread already has one.
- textstring
The plain-text body. Send this even when you send `html` — some clients only read it.
- htmlstring
The rich body. Sanitised on the way in; scripts and remote content are removed.
- dataobject
Typed data: structured JSON sent beside the text, for software at the other end to read as fields. Travels as its own part; it is never mixed into the text.
- attachment_idsuuid[]
Ids from `POST /attachments`, uploaded before the message that carries them. Up to 20.
- in_reply_touuid | null
The message being answered. Keeps the conversation together in the recipient’s client too.
- 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`.
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) 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”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 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/messages \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Search messages
get/api/v1/messages/search
Full-text, ranked by relevance, across every address this credential can reach. Name an `identity_id` to search exactly one. Every result carries the address it was found in and the folder it is in, because a hit you cannot attribute to a mailbox is a hit you cannot open. Not paged: page two of a relevance ranking is a place nobody goes, so narrow the query instead.
Parameters
- identity_iduuid
- qstring
- inenum
inbox · starred · sent · drafts · scheduled · snoozed · archive · spam · trash · unauthenticated
- fromstring
- tostring
- has_attachmentstring
- is_unreadstring
- is_starredstring
- label_iduuid
- limitnumber
Responses
- 200What matched.
- 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/messages/search \
-H "Authorization: Bearer $PIDGEON_API_KEY"Wait for the next message
get/api/v1/messages/await
Holds the request open until a message matching the filters arrives at the address, or `timeout` seconds pass — one call where you would otherwise poll. The message carries `extracted`: verification codes and links, most likely first. Without `after`, it first looks back 2 minutes and returns the newest matching message from then, because a code often lands before the call is made; narrow it with `from`, `subject_contains` or `has_code` so an earlier email is not taken for the one you want, or pass `after=now` to wait only for new mail. Pass the returned `cursor` as `after` on the next call so nothing arriving between calls is missed. Answers 200 with `message: null` and `timed_out: true` when nothing came. A matching message filed as spam is not returned unless you pass `include_spam=true`; it is counted in `held_back`, with its id, so a timeout never hides mail that arrived.
Parameters
- identity_iduuid
- bodyenum
original · clean
- fromstring
- subject_containsstring
- has_codeenum
true · false
- include_spamenum
true · false
- afterstring | date-time
- timeoutnumber
Responses
- 200The message, or that none came in time.
- 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/messages/await \
-H "Authorization: Bearer $PIDGEON_API_KEY"Fetch one message
get/api/v1/messages/{id}
Parameters
- idstring
- bodyenum
original · clean
Responses
- 200The message.
- 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/messages/{id} \
-H "Authorization: Bearer $PIDGEON_API_KEY"Apply one action to many conversations
post/api/v1/messages/actions
Up to 200, the same cap the dashboard uses. Thread-scoped, like everything else that changes a conversation. The response carries an `undo`.
Body
- message_idsuuid[]
- actionenum
`read` and `unread` are refused for an API key: read state belongs to a person and a key is not one. Every other action works.
read · unread · star · unstar · archive · unarchive · delete · restore · spam · not_spam · delete_forever
Responses
- 200What was applied.
- 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/messages/actions \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Put an action back
post/api/v1/messages/undo
Hand back the `undo` from a `PATCH` or a batch. Unusual for an API to have, and the argument is that software makes mistakes at a speed a person does not.
Body
- undoobject[]
Responses
- 200How many rows went back.
- 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/messages/undo \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Change one conversation
patch/api/v1/messages/{id}
Three different kinds of fact: `is_read` is about the reader, `is_starred` is about the mailbox, and `folder` is where the conversation lives. Thread-scoped. The response carries an `undo`.
Parameters
- idstring
Body
- is_readboolean
Refused for an API key: read state belongs to a person and a key is not one. Archive the message, or track what you have seen with `GET /v1/mailboxes/changes`. A person signed in to the dashboard sets this.
- is_starredboolean
- folderenum
inbox · archive · spam · trash
Responses
- 200What changed.
- 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/messages/{id} \
-X PATCH \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'The message as it arrived
get/api/v1/messages/{id}/raw
`message/rfc822`, not JSON — the point is bytes nobody re-encoded. Received mail is the archived original while it is kept; sent mail, and received mail past its retention, is rebuilt from the stored message. `X-Pidgeon-Reconstructed` says which: a rebuild is not the signed bytes and cannot verify DKIM.
Parameters
- idstring
Responses
- 200The message, original or rebuilt (`X-Pidgeon-Reconstructed`).
- 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/messages/{id}/raw \
-H "Authorization: Bearer $PIDGEON_API_KEY"Reply to one message
post/api/v1/messages/{id}/reply
The recipients, the subject and the thread are read off the message being answered. You bring the words.
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
- textstring
The plain-text body. Send this even when you send `html` — some clients only read it.
- htmlstring
The rich body. Sanitised on the way in; scripts and remote content are removed.
- dataobject
Typed data: structured JSON sent beside the text, for software at the other end to read as fields. Travels as its own part; it is never mixed into the text.
- toemail[]
One address or several. A string is accepted for the single case.
- ccemail[]
Copied, and visible to everybody who receives the message.
- bccemail[]
Copied, and visible to nobody else.
- attachment_idsuuid[]
Ids from `POST /attachments`, uploaded before the message that carries them. Up to 20.
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) 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”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 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/messages/{id}/reply \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Reply to everybody on one message
post/api/v1/messages/{id}/reply-all
Every address on the message except the ones that reach this mailbox — which a caller cannot work out, because it needs the routing table.
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
- textstring
The plain-text body. Send this even when you send `html` — some clients only read it.
- htmlstring
The rich body. Sanitised on the way in; scripts and remote content are removed.
- dataobject
Typed data: structured JSON sent beside the text, for software at the other end to read as fields. Travels as its own part; it is never mixed into the text.
- toemail[]
One address or several. A string is accepted for the single case.
- ccemail[]
Copied, and visible to everybody who receives the message.
- bccemail[]
Copied, and visible to nobody else.
- attachment_idsuuid[]
Ids from `POST /attachments`, uploaded before the message that carries them. Up to 20.
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) 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”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 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/messages/{id}/reply-all \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Forward one message
post/api/v1/messages/{id}/forward
`to` is required: a forward exists to reach somebody the original did not.
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
- textstring
The plain-text body. Send this even when you send `html` — some clients only read it.
- htmlstring
The rich body. Sanitised on the way in; scripts and remote content are removed.
- dataobject
Typed data: structured JSON sent beside the text, for software at the other end to read as fields. Travels as its own part; it is never mixed into the text.
- toemail[]
One address or several. A string is accepted for the single case.
- ccemail[]
Copied, and visible to everybody who receives the message.
- bccemail[]
Copied, and visible to nobody else.
- attachment_idsuuid[]
Ids from `POST /attachments`, uploaded before the message that carries them. Up to 20.
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) 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”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 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/messages/{id}/forward \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Accept, decline or tentatively accept a meeting
post/api/v1/messages/{id}/invitation/respond
The one call here that changes something in somebody else's calendar: an iCalendar `REPLY` goes to the organiser, which is how Google, Outlook and Calendly learn about an acceptance. The message must be carrying an `invitation`, and one of this mailbox's own addresses must be on it — including an address routed to it, which is the half you cannot work out from the message. Idempotent on the answer: the same answer to the same `sequence` sends once and returns `already_sent`.
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
- statusenum
accepted · declined · tentative
- commentstring
Responses
- 200Already answered.
- 202Sent.
- 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) 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”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 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/messages/{id}/invitation/respond \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'