1. Make a key
Settings → API keys → Create key. Give it a name you will recognise in six months, then choose permissions. Give it the narrowest set that does the job. If it leaks, that set is the whole blast radius — a key that only reads the inbox cannot also send mail from your domain. For this walkthrough: Mailboxes → Read and Messages → Read. The key is shown once, startingrk_. Copy it now.
2. Find out what you own
Every other endpoint takes an address, and this is where the valid ones come from.401, the key is wrong or revoked. If it returns 403, the key
is real but lacks mailboxes:read — the error names the scope it wanted.
Check
mode before anything else. A mailbox with "mode": "private" is
encrypted to a key only its owner’s browser holds. It is listed so you know it
exists, and every other endpoint will refuse it — there is nothing the API could
return but ciphertext.3. Read the inbox
Recipe: react to new mail
Do not poll for this. Register a webhook formessage.received and Ruber
calls you the moment mail is delivered — see Webhooks.
Polling GET /v1/messages on a timer is the obvious approach and the wrong one:
it is a fixed cost whether or not anything arrived, it is always at least one
interval behind, and it opens an IMAP session on the mail server every tick.
If you genuinely cannot receive a webhook — no public URL, say — poll the inbox
with filter=unread and track the newest received_at you have already
handled. Do not use uid as a high-water mark: uids are per folder and a
mailbox rebuild can reset the range.
Recipe: send a reply that threads properly
Read the message you are answering, then send with itsmessage_id:
in_reply_to and references the reply is delivered correctly and
appears as a brand new conversation in the recipient’s client — which is the
kind of wrong that nobody reports and everybody notices.
Needs Sending → Write.
Recipe: archive a whole conversation
Threads hand you exactly what the bulk endpoint wants:Recipe: draft a reply for a human to approve
The shape most teams actually want from an assistant: write it, do not send it.Four things worth knowing before you build
Auid is not a permanent id. It identifies a message inside one folder,
and moving a message gives it a new one. Store message_id — the sender’s — if
you need something durable.
Reading a message does not mark it read. That is a separate call, so an
integration that indexes a mailbox does not silently change what its owner sees
as new.
Sending is its own permission. messages:write files and stars mail that
already exists; it does not let a key send. That separation is the point.
202 from a send means accepted, not delivered. Whether it arrived is
decided minutes later by a server nobody here controls. Subscribe to
message.bounced if you need to know.
Where to go next
Messages
List, read, and change mail — including up to 500 at once.
Sending
Send, with attachments, threading and the limits that apply.
Webhooks
Be told when mail arrives, sends or bounces.
API overview
Permissions, errors, pagination and versioning.