Skip to content
Developer platform · Security

Authenticate server-to-server requests

Developer credentials are service principals. They do not create dashboard users, mailbox users, or shared browser sessions.

Send the key as a bearer credential

Use a sandbox key while integrating and a separately issued production key at launch. A complete key is displayed only once.

                        curl "$MAILDECK_API_URL/v1/mailboxes" \
  -H "Authorization: Bearer $MAILDECK_API_KEY"
                      

Grant the minimum scopes

A request succeeds only when its key grants every endpoint scope. Scopes are fixed at issuance; issue a replacement key to change privileges. Revocation and expiration take effect without restarting services.

  • mailboxes:read — list mailboxes and inspect domain health
  • messages:read — list/retrieve messages and reconcile GET /v1/events
  • messages:write — change read, starred, or folder state with PATCH /v1/messages/{id}
  • messages:send — enqueue outbound mail with POST /v1/messages
  • attachments:write — upload an attachment for a later send
  • attachments:read — download an attachment only after a clean scan
  • webhooks:read — list endpoints and inspect delivery attempts
  • webhooks:write — create endpoints and replay failed deliveries

Authentication pipeline

Omnero Mail parses the Bearer token, rejects malformed prefixes, verifies the stored Argon2id hash, checks app/key status and expiration, evaluates all required scopes, and derives the tenant and application from the credential. Resource ownership is checked again inside each operation; a caller-supplied tenant ID never changes the authorization boundary.

Rotate and revoke without downtime

Issue a second key for the same application, deploy it to consumers, confirm last-used activity, and revoke the previous key. Never log Authorization, include keys in URLs, reuse dashboard cookies, or copy production keys into sandbox tooling.

Handle limits explicitly

Requests pass an IP guard (120 per minute), then the active plan’s per-key requests-per-minute limit and tenant-wide daily limit. A 429 response includes the stable rate_limited code and Retry-After. Back off, add jitter, and do not rotate keys to bypass protection.

                        const response = await fetch(url, { headers });
if (response.status === 429) {
  const retryAfter = Number(response.headers.get('retry-after') ?? '1');
  await new Promise((resolve) =>
    setTimeout(resolve, retryAfter * 1000 + Math.random() * 250),
  );
}
                      

Stable failures

Errors use { code, message, requestId, details }. Treat 401 as missing, malformed, expired, or revoked credentials; 403 as insufficient_scope; 404 as an absent or inaccessible tenant resource; 409 as idempotency conflict/pending; 413 as an oversized attachment; and 429 as rate limiting. Include requestId when escalating a failure.

Next step

Create the first mailbox at no cost.

Start freeLog in