Skip to main content
Ruber’s HTTP API lets your own software work with mail in the mailboxes your organization owns. It is a normal JSON API over HTTPS: no SDK required, no websocket, nothing to install.

What you can do

Ruber can also call you. See Webhooks for events raised when mail arrives, sends, bounces or is marked as spam, and when somebody books or cancels a slot on one of your booking links — those are configured in the dashboard rather than with an API key, so they need no permission here.
Rolling out. The endpoints above are documented as built. Some are still reaching production — until one lands it answers 404, and folder renaming and deleting answer 502 until the mail-server update ships. Nothing about the request or response shapes here changes when they arrive.

Base URL

Every request must be HTTPS. There is no HTTP port to redirect from.

Authentication

Send your key as a bearer token:
Keys are created under Settings → API keys in the dashboard. They begin rk_, and the full key is shown exactly once, in the dialog that creates it. Ruber stores only a SHA-256 hash — we cannot show it to you again, and we cannot recover it. If a key is lost, revoke it and create another.
A key is a password for your mail. Keep it out of source control, out of front-end code, and out of anything a browser downloads. If a key is exposed, revoke it in the dashboard — revocation takes effect immediately, on the next request.

Every authentication failure looks the same

A missing header, a malformed header, a key that never existed and a key you revoked all answer the same 401 with the same sentence. That is deliberate: distinguishing them would let someone holding a list of guesses learn which ones are real keys that happen to be revoked. A missing permission is different, and is deliberately specific — see below.

Permissions

Every key carries a set of permissions, and a key can only do what you granted it when you created it. There is no “full access” key, no admin, and no wildcard. Permissions cannot be changed after creation — to change what a key may do, create a new one and revoke the old. When you create a key you choose one level per resource: Two things about that table are worth reading twice. Write includes Read. Choosing Write on a resource grants its read scope too. A key that may move a message between folders but cannot see which folder it is in describes no real integration. Labels are organization labels. A key manages a shared catalogue owned by the organization; it never sees or touches a member’s personal labels. Putting an existing label on a message is messages:write, not labels:write — that changes the message, not the catalogue. Calendar is one resource, not two. It covers calendars and the events on them together. A key that could list a calendar but not read its events would be useless, and two boxes that are always ticked together teach people to stop reading the list. Booking is its own resource, and for the same reason as Sending. Creating a booking link publishes a page on the open internet that lets strangers put meetings on your calendar. Folded into calendar:write it would be granted to everybody who only wanted to add an event. Forwarding is its own resource, and it sends. A forward relays every message that arrives and an auto-reply answers strangers — both under your domain’s reputation. Neither is covered by Messages or by Sending. Sending is its own resource. messages:write covers changing mail that already exists — marking it read, starring it, filing it. It does not let a key send anything. Sending creates mail that leaves under your domain’s reputation and spends your plan’s daily allowance, so it is a separate, deliberate choice.

When a key lacks a permission

You get a 403 that names exactly what was missing, because by then the caller has proved they hold a real key and there is nothing left to protect:

Private mailboxes are not readable

A Private mailbox is encrypted to a key held only in its owner’s browser. Ruber holds no copy, so there is nothing the API could return but ciphertext and nothing it could send. Private mailboxes do appear in GET /v1/mailboxes, with "mode": "private" — hiding them would make an integration report a mailbox as missing when it simply cannot be read by anyone but its owner. Any endpoint that takes an address will refuse a Private one with invalid_request and say why. Smart mailboxes work normally.

Errors

Every error has the same shape. The code is stable and is what your software should branch on; the message is written for a human reading a log and may be reworded at any time.
404 does not mean “does not exist”. A mailbox belonging to another organization and a mailbox that was never created both answer not_found. Telling them apart would turn the API into a way to test whether an address exists on the platform, one guess at a time.

Rate limits

Requests are limited per key, at 600 per minute. That is ten a second sustained — comfortably above any integration doing real work. Going over returns 429 with a Retry-After header giving the seconds to wait. There is no lockout: a client that misjudges its own rate recovers on the next window.
Narrower limits still apply underneath. Sending, in particular, also passes a per-account hourly limit, a short burst ceiling, and your plan’s daily allowance — so this ceiling is never a way around them. A send refused by the daily allowance returns quota_exceeded, not rate_limited.

Conventions

Pagination

Endpoints that can return an unbounded amount take limit and page:
page is 0-based, and page 0 is the newest. Responses carry total and has_more, so you can tell whether another page exists without fetching it. Everything else — mailboxes, aliases, folders, labels, drafts, scheduled — returns the whole set up to a sensible cap, because an organization has tens of those, not thousands.

Retries and duplicate sends

There are no idempotency keys yet. If POST /v1/messages times out, you do not know whether the message went, and retrying may send it twice.Until keys exist, the safe pattern is: on a timeout or a 5xx, do not blindly retry a send. Check GET /v1/messages?folder=sent for the message first — a copy is filed there on success — and only resend if it is absent.4xx responses are safe to treat as final: nothing was sent.
Everything that is not a send is safe to retry. Reads have no side effects, and PATCH on flags, folders and labels is idempotent by nature — setting read to true twice is the same as once.

Versioning

The v1 in the path is a promise about breaking changes, not a release number. Inside v1 we may add fields, endpoints and error codes; we will not remove a field, repurpose an error code, or change what an existing one means. Write clients that ignore fields they do not recognise, and branch on the error code rather than the message — the sentence is written for a human reading a log and may be reworded at any time.

Caching

Every response carries Cache-Control: no-store. Responses are scoped to one organization’s mail, and a shared cache keyed on the URL alone would serve one customer’s inbox to another. Do not cache these responses in shared infrastructure.