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
Authentication
Send your key as a bearer token: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.
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 same401 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, noadmin, 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 a403 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 inGET /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. Thecode 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 returns429 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.
quota_exceeded, not rate_limited.
Conventions
Pagination
Endpoints that can return an unbounded amount takelimit 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
Everything that is not a send is safe to retry. Reads have no side effects, andPATCH on flags, folders and labels is idempotent by nature — setting read
to true twice is the same as once.
Versioning
Thev1 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 carriesCache-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.