Docs · Errors

Errors and rate limits

A failed request answers with an HTTP status and a problem document (RFC 9457, application/problem+json). Branch on the status and the problem type; show detail to a developer, not to an end user.

The error format

response · 409 · application/problem+json
{
  "type": "https://owlpost.to/problems/idempotency-conflict",
  "title": "This Idempotency-Key was used for a different request",
  "status": 409,
  "detail": "Use a new key for a new message; a retry must repeat the original request exactly.",
  "instance": "01J9ZQ5X7K3M2N4P6R8S0T1V2W"
}
FieldMeaning
typeA URI naming the problem. Match on its last path segment, the slug (idempotency-conflict). Owlpost's own problems live under https://owlpost.to/problems/; the ones the underlying platform raises (authentication, rate limits, not found, internal) under https://factory0.ventures/problems/. The URIs name the problem; they are not pages to fetch.
titleA short, stable summary of the problem type
statusThe HTTP status, repeated
detailWhat went wrong this time, in words. May be absent.
instanceThe request id, also in the x-request-id response header. Quote it when you contact us.
errorsOn POST /v1/emails and /batch validation failures only: every broken field as {field, message}. Fields are paths as you wrote them: to[2], attachments[0].content, headers.Subject, and [7].to in a batch.

One exception: a body that is not valid JSON, or is missing a required field, on the domain, webhook, inbound and suppression routes is refused by the web framework before Owlpost reads it, with a short plain-text 4xx instead of a problem document. The send routes always answer with a problem.

Status codes and problem types

StatusSlugWhen
200, 201, 202Success. 201 when a resource was created (inbox, rule, webhook, suppression); 202 when a reply was queued.
400validation-failedA rule on the suppression and topic routes: a bad address or topic, a complaint removed without a reason. (The same slug is a 422 elsewhere; see below.)
401api-key-unauthorizedThe key is missing, malformed, unknown or revoked. One answer for all four, so a probe learns nothing.
403api-key-forbiddenThe key is valid but lacks the route's scope
domain-not-verifiedLive mail from a domain the account has not verified
inbox-limitThe account has all the inboxes it may have (100)
held-needs-manageListing held mail without inbound:manage
404not-foundNo such route, or no such resource in your account. Another account's ids read as missing.
link-expiredAn attachment link that expired (after seven days) or was altered
409idempotency-conflictA key reused with a different request, or a batch that raced another (details)
domain-takenThe domain is already added, by this account or another
inbox-existsThat inbox address on your domain is taken
route-existsA routing rule for that match exists
not-heldReleasing a message that is not held
413request-too-largeThe body is over the route's limit (below). A send between 8 and 32 MiB is refused by the framework instead, with a plain-text 413.
422validation-failedThe request broke one or more rules: sends, batches, domains, inboxes, routing rules, replies, webhooks
429rate-limitedThe key's rate limit (below)
inbox-rate-limitedAn agent inbox sent 60 messages in the last hour
thread-rate-limitedA thread got 10 messages from Owlpost in the last hour
500internalOur fault. The body carries no internals; retry with backoff and send us the instance.
502ses-refusedAmazon SES refused a domain operation
503ses-unavailableSES did not answer a domain operation; try again
ses-not-configuredSending domains are not available on this deployment
not-readyA storage lookup failed; try again

Retry 429, 500, 502 and 503 with backoff. With an Idempotency-Key, retrying a send is always safe. Do not retry other 4xx answers unchanged.

Rate limits

Each API key may make 60 requests a minute. A batch counts as one request per message in it. Over the limit, the answer is 429 with the slug rate-limited, and nothing was stored. The 429 does not carry a Retry-After header yet, so back off on your side: wait a few seconds, then retry with growing pauses.

The limit applies to the email send and read routes, domains, webhooks and every inbound route. The suppression and topic routes are not rate-limited today.

When the rate limiter itself cannot be reached, the API refuses rather than letting traffic through unmetered, so a 429 can also mean "try again in a moment".

Agent inboxes have two more limits, counted per hour: 60 messages from one inbox and 10 into one thread (see loop limits).

Size limits

WhatLimit
One message, attachments included after base645 MiB
POST /v1/emails request body8 MiB
POST /v1/emails/batch request body32 MiB, 100 messages
Request bodies on other routes64 KiB
Recipients per message (to, cc and bcc together)50
Attachments, headers, tags per message50 each
A received message10 MiB
Text plus HTML of a reply from an inbox512 KiB