Skip to main content

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, starting rk_. Copy it now.

2. Find out what you own

Every other endpoint takes an address, and this is where the valid ones come from.
If this returns 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

That is the whole quickstart. Everything below is a real task.

Recipe: react to new mail

Do not poll for this. Register a webhook for message.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 its message_id:
Without 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:
One request, not three. Needs Messages → Write.

Recipe: draft a reply for a human to approve

The shape most teams actually want from an assistant: write it, do not send it.
It appears in the Drafts folder in the web app, where somebody opens it in the composer, edits it and presses send. Needs Drafts → Write.

Four things worth knowing before you build

A uid 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.