Skip to content
Esc
  • OverviewGuidesWhat exists today, and where to start.
  • QuickstartGuidesKey, domain, first send — in that order.
  • AuthenticationGuidesBearer keys, the mandatory User-Agent, and what each refusal means.
  • ErrorsGuidesThe whole vocabulary, with the status each name carries.
  • IdempotencyGuidesRetry a send without sending it twice.
  • PaginationGuidesCursors are item IDs, not page numbers.
  • Rate limitsGuidesTen a second per team, and the headers that tell you where you are.
  • EventsGuidesEvery event a webhook can carry, with one real payload each.
  • DomainsGuidesThe records, where they go at each registrar, and what the page does while you wait.
  • TrackingGuidesOpens and clicks: one record, two toggles, and what an open really means.
  • ReceivingGuidesInbound mail, and the Inbox: a webhook fires, you read it, you answer it.
  • InboxGuidesChannels, personal mailboxes and seats: who sees what, and where a reply goes.
  • Node SDKGuidesThe rasket package: typed from the API's own document, retries only what is safe.
  • Python SDKGuidesThe rasket package on PyPI: the Node client's methods, in snake_case, over httpx.
  • MCP serverGuidesConnect Claude, ChatGPT or any MCP client: your scopes, no key.
  • AI assistGuidesSubject lines, drafts and diagnosis — in the dashboard and over the API, off until you allow it.
  • AgentsGuidesLet an AI agent set Rasket up: the skill, the rules file, MCP, and the recipe they share.
  • OAuthGuidesLet another app act for a team: register, authorize with PKCE, exchange, refresh.
  • Single sign-onGuidesOIDC login for your team, a domain proved by DNS, enforcement and break-glass.
  • IntegrationsGuidesVercel, Netlify and Cloudflare, plus Zapier and n8n for workflows without code.
  • SMTPGuidesSend from anything that speaks SMTP: settings, setup guides, limits and replies.
  • ZapierGuidesSend email, add contacts and react to email events from a Zap, with no code.
  • n8nGuidesThe Rasket node and trigger for n8n workflows: install, connect, every operation.
  • EmailsAPI referenceSend, batch, retrieve, list, reschedule, cancel, attachments.
  • DomainsAPI referenceAdd a domain, publish its records, verify it.
  • API keysAPI referenceCreate, list, rename and revoke credentials.
  • WebhooksAPI referencePayloads, signature verification, retries and replay.
  • SuppressionsAPI referenceAddresses we will not send to, and why.
  • LogsAPI referenceEvery request made with this team's credentials.
  • MetricsAPI referenceDelivery, bounce, complaint and engagement counts.
  • TemplatesAPI referenceVersioned email content with typed variables, addressed by ID or alias.
  • ContactsAPI referenceYour audience: contacts, their typed properties, segments and topic choices.
  • SegmentsAPI referenceAudiences defined by a filter, by hand, or both.
  • TopicsAPI referenceWhat contacts subscribe to, and the preference page's list.
  • CampaignsAPI referenceCampaigns, at /broadcasts: one message to a segment, from draft to results.
  • ImportsAPI referenceCSV uploads: column mapping, conflicts and counts.
  • AutomationsAPI referenceWorkflows that run per contact: the graph, its versions, and every run.
  • Custom eventsAPI referenceThe names your product fires, and what starts a workflow.
  • ReceivingAPI referenceMail sent to you: the message, its attachments, its raw source.
  • OAuthAPI referenceClient registration, the token endpoint, and the grants a team has given.
  • TeamAPI referenceThe team a credential belongs to: its plan, sender identity, AI flag and members.
  • BillingAPI referencePlan, usage, invoices and add-ons, and the hosted pages where a customer pays.
  • AI helpersAPI referenceSubject lines, a first draft, and why an email did what it did.

GuidesErrors

Errors

Every failure answers with the same three fields and a name from a closed list. Match on the name, not on the message.

The shape

{
  "statusCode": 422,
  "name": "missing_required_field",
  "message": "Missing required field: subject."
}

statusCode repeats the HTTP status, name is the stable identifier to branch on, and message is a sentence for a human. Messages are written to be readable and may be reworded; names are part of the contract and are not.

Validation failures add an errors array naming each field that failed. It is additive — code that ignores it keeps working.

{
  "statusCode": 400,
  "name": "validation_error",
  "message": "Unknown fields: is_admin, priority.",
  "errors": [
    { "path": "is_admin", "message": "Unknown field: is_admin." },
    { "path": "priority", "message": "Unknown field: priority." }
  ]
}

One error carries a fourth field: a missing User-Agent answers with code: 1010. No other response has it, so it can be matched exactly.

The vocabulary

Two names appear more than once, with different statuses, because the situation genuinely differs — the status is what tells them apart, so branch on both.

  • validation_error is 400 when a field is wrong, 403 when the request is well-formed and not allowed — an unverified sending domain, a missing User-Agent, a policy restriction — and 422 when the body is fine and the resource itself refuses it, such as a plan limit reached.
  • restricted_api_key is 401 when a sending_access key is used on an endpoint that is not sending, and 403 when the key has been revoked. The first is the wrong key for the job; the second is a key that no longer works at all.

missing_api_key and invalid_api_key are both 401 and differ in what you sent. The first means no usable Authorization: Bearer header arrived; the second means a token did arrive and matches nothing live, so check the value rather than your environment.

not_implemented at 501 is not a mistake on your side either. It means the route is ours and reserved for a later phase — it exists so an SDK gets a straight answer rather than a 404 that would imply something about the id it asked for.

Every error name and the status it carries
nameStatusMeans
invalid_idempotency_key400key length outside 1-256
validation_error400field-level validation failed (body, query, headers)
missing_api_key401no Authorization header, or one that is not `Bearer <token>`
invalid_api_key401a Bearer token that resolves to no live key or OAuth access token
restricted_api_key401sending_access key used on a non-sending endpoint
email_above_quota403email content unavailable because the team is over quota
invalid_permission403actor lacks a required scope (unverified user, future scoped keys/OAuth)
restricted_api_key403key status = revoked (not active)
suspended_api_key403key status = suspended or team risk_state = suspended
validation_error403domain not verified, from-domain not allowed for this key, sandbox restriction, missing User-Agent (code 1010), policy restriction
not_found404resource not found or not in this team (no existence leak), unknown route
method_not_allowed405known path, wrong method
concurrent_idempotent_requests409same key in flight
invalid_idempotent_request409same key, different fingerprint
resource_locked409scheduled email already dispatched / resource being updated
receiving_mx_in_use409turning receiving on (or moving it) at a name whose MX already delivers to another mailbox provider, without `confirm_replace_mx`
validation_error422the body is well-formed but the resource refuses it: a plan limit reached, an unroutable webhook endpoint, a replay against a disabled webhook (14 §2)
invalid_attachment422neither content nor path, both, fetch blocked, too large, bad type
invalid_parameter422path/query parameter malformed (not a UUID, bad enum)
missing_required_field422body missing from/to/subject/name/...
missing_required_parameter422required query/path parameter absent
daily_quota_exceeded429team daily quota
monthly_quota_exceeded429team monthly quota
marketing_quota_exceeded429team marketing quota (0149): the broadcast meter, never the transactional one
rate_limit_exceeded42910 rps team limit
application_error500unexpected error (request id in message)
not_implemented501the route exists and is reserved for a later phase
service_unavailable503dependency down, global send disabled, readiness failed

The OAuth flow's own vocabulary

The OAuth flow's routes answer in the shape OAuth libraries already parse instead: POST /oauth/token and POST /oauth/revoke use RFC 6749 §5.2's, and /oauth/register and its management routes RFC 7591 §3.2.2's. Branch on error; the description is for a human.

{
  "error": "invalid_grant",
  "error_description": "The authorization code has expired."
}
Every OAuth flow error and the status it carries
errorStatusMeans
invalid_request400A parameter is missing, repeated or malformed. The same name at 429 means the client or address is over its limit.
invalid_client401The client is unknown or disabled.
invalid_grant400The code or refresh token is expired, revoked, already used, issued to another client, or the verifier or redirect URI does not match.
unauthorized_client400The client may not use this grant type.
unsupported_grant_type400The grant type is neither authorization_code nor refresh_token.
invalid_scope400A scope is unknown, or wider than the grant.
invalid_client_metadata400Registration: a metadata field was refused.
invalid_redirect_uri400Registration: a redirect URI was refused — not https or loopback, or malformed.
invalid_token401Client management: the registration access token is missing, malformed or unknown.

One refusal in the flow is not a body at all. GET /oauth/authorize with an unknown client or an unregistered redirect URI answers 400 with an error page and never redirects; every other authorize failure redirects back to the client with error and state. And an invalid_token from client management also carries WWW-Authenticate: Bearer error="invalid_token".

Every other route — /oauth/grants included, and any endpoint called with an OAuth access token — answers with the Rasket error body above.

What to retry

rate_limit_exceeded and 503 are worth retrying, with backoff — a rate_limit_exceeded tells you exactly how long to wait in its retry-after header. The quota 429s — daily_quota_exceeded, monthly_quota_exceeded and marketing_quota_exceeded — carry no retry-after and do not clear until the quota resets or the plan changes. 500 is worth one retry with an idempotency key, and worth reporting if it persists. Everything in the 4xx range other than rate_limit_exceeded describes something about the request that will not change on its own; retrying it unchanged will fail again.