Search the docs
Find a page, a section, or an endpoint.
Reading and triaging
Folders, paging, changing a conversation, undo and search.
Every address with an inbox has one, and the API reads and writes it: folders with counts, a page of conversations, and the state changes a person would make by clicking. The mailbox is not a browser-only half of the product.
The folders
Nine, and they are the same nine the web client has. identity_id is
required — there is no merged view across addresses, because two mailboxes
joined together are a mailbox nobody has.
curl "https://pidgeon.ai/api/v1/mailboxes?identity_id=idn_4f2a1" \
-H "Authorization: Bearer $PIDGEON_API_KEY"total and unread are null rather than 0 where the product does not
count that folder. Zero would be a claim; null says we do not count it.
One folder, a page at a time
A row is a conversation — the newest message in each thread — not a
message. It carries no body: a list of forty conversations does not want
forty bodies. GET /messages?thread_id= opens one.
curl "https://pidgeon.ai/api/v1/mailboxes/inbox?identity_id=idn_4f2a1&limit=50" \
-H "Authorization: Bearer $PIDGEON_API_KEY"cursor is opaque, and it is not an offset. It carries a sort key and an
id together, because several messages can arrive in the same millisecond and
a cursor on the timestamp alone repeats or drops rows at a page boundary. An
offset would be worse than either: rows shift underneath it and a client
syncing an inbox silently loses mail. Pass back what next_cursor gave you;
do not construct one.
A cursor we did not write is page one, not an error. It arrives in a URL, and a URL is a thing people edit.
Changing a conversation
Three fields, and they are three different kinds of fact: is_read is about
the reader, is_starred is about the mailbox, and folder is where the
conversation lives.
curl https://pidgeon.ai/api/v1/messages/msg_9a13c \
-X PATCH \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_read": true, "folder": "archive" }'Everything here is thread-scoped: archiving one message of a conversation
and leaving the rest in the inbox is never what anyone means. The four
folders you can move a conversation to are inbox, archive, spam and
trash — the ones that are places. Starred is a view over a flag, which is
why it is is_starred and not a destination.
POST /messages/actions applies one action to up to 200 conversations, which
is the same cap the dashboard uses.
Undo
Unusual for an API to have, and the argument is that software makes mistakes at a speed a person does not. An agent that archived the wrong forty conversations should be able to say so.
curl https://pidgeon.ai/api/v1/messages/undo \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "undo": [ ... ] }'The undo value is the rows as they were before. Hand it back; do not read
it. Access to the messages it names is re-checked, so the worst a tampered
one can do is set flags on mail you can already set flags on.
Search
Full-text over one mailbox, ranked by relevance. A result carries the folder it was found in — the whole reason you are searching is that you do not know where it lives.
curl "https://pidgeon.ai/api/v1/messages/search?identity_id=idn_4f2a1&q=invoice" \
-H "Authorization: Bearer $PIDGEON_API_KEY"Not paged: page two of a relevance ranking is a place nobody goes. Narrow the query instead.