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.
  • VercelGuidesAdd Rasket on Vercel: a Sending key in each project as RASKET_API_KEY, no copying.
  • 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.

API referenceOAuth

OAuth

The authorization server's routes, and the grants a team has given. For how they fit together, start with the OAuth guide.

Three credentials

What each OAuth route is authorised by
RoutesAuthorization
/oauth/register, /oauth/authorize, /oauth/token, /oauth/revokeNone. A public client calls them before it holds anything, so the samples send no Authorization header.
/oauth/register/{client_id}The registration access token, Bearer rkor_…. An API key is refused.
/oauth/grantsA full_access rk_ key. An OAuth access token (rko_) is refused, whatever its scopes.

Two error shapes

  • /oauth/token, /oauth/revoke and /oauth/register answer the standard OAuth shape, { error, error_description } (RFC 6749 §5.2 and RFC 7591 §3.2.2), because that is what an OAuth library parses.
  • /oauth/grants answers the Rasket error body every other endpoint uses.

Both vocabularies are listed on Errors.

Endpoints

Register a client

POST /oauth/register

Dynamic client registration (RFC 7591). Unauthenticated; do it once per app.

Body

  • redirect_urisstring[]Required

    At most 10. https URLs, or loopback-address URLs for a native app (RFC 8252 §7.3). Matched exactly at authorize time.

  • client_namestringRequired

    Shown to the person on the consent page.

  • scopestring

    Space-separated scopes the client may ask for, from the catalogue on the OAuth guide. An authorize request that names none asks for these.

  • client_uristring

    An https link to the app, shown on the consent page.

  • logo_uristring

    An https image, shown on the consent page.

5 more fields (grant_types, response_types, token_endpoint_auth_method, software_id, software_version)
  • grant_typesstring[]

    authorization_code and refresh_token, which is also the default.

  • response_typesstring[]

    Only ["code"].

  • token_endpoint_auth_methodstring

    Only none: every client is a public client.

  • software_idstring

    Your own identifier for the app, stored and returned.

  • software_versionstring

    Your own version string, stored and returned.

Request

curl -X POST "https://api.rasket.com/oauth/register" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "client_name": "Acme Invoices",
  "redirect_uris": ["https://invoices.acme.example/rasket/callback"],
  "scope": "emails:send emails:read"
}'

Response 201

{
  "client_id": "rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C",
  "client_id_issued_at": 1789205400,
  "registration_client_uri": "/oauth/register/rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C",
  "client_name": "Acme Invoices",
  "redirect_uris": ["https://invoices.acme.example/rasket/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "scope": "emails:send emails:read",
  "registration_access_token": "rkor_…"
}
  • registration_access_token is shown exactly once. Store it: it is the only credential for reading, replacing or deleting this client.
  • Public clients only. A token_endpoint_auth_method other than none, or response_types other than code, is 400 invalid_client_metadata; a redirect URI that is not https or loopback is 400 invalid_redirect_uri.
  • At most 10 registrations per IP address per hour and 1,000 a day across the platform; past either, 429.
  • Errors from this route are RFC 7591's { error, error_description }, not the Rasket error body.

Start authorization

GET /oauth/authorize

Send the person's browser here. It comes back to your redirect URI with a code.

Query parameters

  • response_typestringRequired

    Always code.

  • client_idstringRequired

    Your rkoc_ identifier.

  • redirect_uristringRequired

    Exactly one of the client's registered redirect URIs.

  • code_challengestringRequired

    BASE64URL(SHA256(code_verifier)): 43 characters.

  • code_challenge_methodstringRequired

    Always S256. plain is refused.

  • statestringRequired

    An unguessable value you check when the browser comes back. Returned unchanged; at most 512 characters.

  • scopestring

    Space-separated. The client's registered scopes when omitted.

1 more field (resource)
  • resourcestring

    An RFC 8707 resource indicator. When sent, it must name this API.

Request

curl -X GET "https://api.rasket.com/oauth/authorize?response_type=code&client_id=rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C&redirect_uri=https%3A%2F%2Finvoices.acme.example%2Frasket%2Fcallback&scope=emails%3Asend+emails%3Aread&state=af0ifjsldkj&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256" \
  -H "User-Agent: acme-billing/1.0"
  • This is a URL to open in the person's browser, not a call your server makes: the 302 goes to our consent page, which asks them to sign in, pick one team and approve the scopes. Server-side, fetch and requests would follow it to a login page.
  • Approval redirects to redirect_uri?code=rkc_…&state=…. Refusal, or any other failure, redirects there with error, error_description and your state.
  • An unknown or disabled client_id, or a redirect_uri the client never registered, never redirects: it answers 400 with an error page, because sending the browser to an unverified address is how codes leak (RFC 6749 §4.1.2.1).

Exchange a code, or refresh

POST /oauth/token

Swap an authorization code for tokens, or rotate a refresh token.

Body

  • grant_typestringRequired

    authorization_code or refresh_token.

  • client_idstringRequired

    Your client identifier.

  • codestring

    authorization_code only: the rkc_ code from the redirect.

  • redirect_uristring

    authorization_code only: the same URI the authorize request named.

  • code_verifierstring

    authorization_code only: the verifier whose hash you sent as the challenge.

  • refresh_tokenstring

    refresh_token only: the rkr_ token.

1 more field (scope)
  • scopestring

    refresh_token only: a narrower set. It can never widen the grant.

Request

curl -X POST "https://api.rasket.com/oauth/token" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "grant_type": "authorization_code",
  "client_id": "rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C",
  "code": "rkc_…",
  "redirect_uri": "https://invoices.acme.example/rasket/callback",
  "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}'

Response 200

{
  "access_token": "rko_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rkr_…",
  "scope": "emails:send emails:read"
}
  • The body may be application/x-www-form-urlencoded — what a conforming OAuth library sends — or JSON, as shown.
  • A refresh returns a new pair and revokes the refresh token you presented. Presenting a code, or a rotated refresh token, a second time revokes the whole grant and every token under it.
  • Errors are RFC 6749 §5.2's { error, error_description }: 400 with invalid_request, invalid_grant, unauthorized_client, unsupported_grant_type or invalid_scope, and 401 invalid_client for an unknown or disabled client.
  • The response carries Cache-Control: no-store.

Revoke a token

POST /oauth/revoke

RFC 7009. Sign a person out of your app without revoking the grant.

Body

  • tokenstringRequired

    The access or refresh token to revoke.

  • client_idstringRequired

    The client the token was issued to.

1 more field (token_type_hint)
  • token_type_hintstring

    access_token or refresh_token. Accepted and ignored.

Request

curl -X POST "https://api.rasket.com/oauth/revoke" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "token": "rkr_…",
  "client_id": "rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C"
}'
  • An access token is revoked alone. A refresh token takes its whole family with it, and every access token of the same grant.
  • The grant itself stays live. Only the team revoking it, or the client being deleted, ends a grant.
  • A token that is unknown, expired, already revoked or another client's still answers 200 with an empty body, as RFC 7009 §2.2 requires.
  • Form-encoded or JSON, as for the token endpoint.

Read a registered client

GET /oauth/register/{client_id}

RFC 7592. The metadata stored for your client.

Path parameters

  • client_idstringRequired

    The rkoc_ identifier registration returned.

Headers

  • AuthorizationstringRequired

    Bearer rkor_… — the registration access token, shown once when the client was registered. An API key is refused.

Request

curl -X GET "https://api.rasket.com/oauth/register/rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C" \
  -H "Authorization: Bearer $RASKET_REGISTRATION_TOKEN" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "client_id": "rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C",
  "client_id_issued_at": 1789205400,
  "registration_client_uri": "/oauth/register/rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C",
  "client_name": "Acme Invoices",
  "redirect_uris": ["https://invoices.acme.example/rasket/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "scope": "emails:send emails:read"
}
  • A missing, malformed or unknown registration token is 401 with WWW-Authenticate: Bearer error="invalid_token".

Replace a registered client

PUT /oauth/register/{client_id}

RFC 7592. A full replacement: a field you leave out is cleared.

Path parameters

  • client_idstringRequired

    The rkoc_ identifier registration returned.

Headers

  • AuthorizationstringRequired

    Bearer rkor_… — the registration access token, shown once when the client was registered. An API key is refused.

Body

  • redirect_urisstring[]Required

    At most 10. https URLs, or loopback-address URLs for a native app (RFC 8252 §7.3). Matched exactly at authorize time.

  • client_namestringRequired

    Shown to the person on the consent page.

  • scopestring

    Space-separated scopes the client may ask for, from the catalogue on the OAuth guide. An authorize request that names none asks for these.

  • client_uristring

    An https link to the app, shown on the consent page.

  • logo_uristring

    An https image, shown on the consent page.

5 more fields (grant_types, response_types, token_endpoint_auth_method, software_id, software_version)
  • grant_typesstring[]

    authorization_code and refresh_token, which is also the default.

  • response_typesstring[]

    Only ["code"].

  • token_endpoint_auth_methodstring

    Only none: every client is a public client.

  • software_idstring

    Your own identifier for the app, stored and returned.

  • software_versionstring

    Your own version string, stored and returned.

Request

curl -X PUT "https://api.rasket.com/oauth/register/rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C" \
  -H "Authorization: Bearer $RASKET_REGISTRATION_TOKEN" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "client_name": "Acme Invoices",
  "redirect_uris": ["https://invoices.acme.example/rasket/callback", "https://invoices.acme.example/rasket/callback-eu"],
  "scope": "emails:send emails:read"
}'

Response 200

{
  "client_id": "rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C",
  "client_id_issued_at": 1789205400,
  "registration_client_uri": "/oauth/register/rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C",
  "client_name": "Acme Invoices",
  "redirect_uris": ["https://invoices.acme.example/rasket/callback", "https://invoices.acme.example/rasket/callback-eu"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "scope": "emails:send emails:read"
}
  • The registration access token is not rotated: a client has nowhere to learn a new one.

Delete a registered client

DELETE /oauth/register/{client_id}

RFC 7592. Revokes every grant the client holds, on every team.

Path parameters

  • client_idstringRequired

    The rkoc_ identifier registration returned.

Headers

  • AuthorizationstringRequired

    Bearer rkor_… — the registration access token, shown once when the client was registered. An API key is refused.

Request

curl -X DELETE "https://api.rasket.com/oauth/register/rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C" \
  -H "Authorization: Bearer $RASKET_REGISTRATION_TOKEN" \
  -H "User-Agent: acme-billing/1.0"
  • Every grant is revoked with revoked_reason client, and every token under them stops working at once.
  • Each team still sees its grant, revoked, in GET /oauth/grants: the history is kept.

List OAuth grants

GET /oauth/grants

Every app this team has connected, live and revoked, newest first.

Query parameters

  • limitinteger

    How many items to return, 1–100. Defaults to 20.

  • afterstring

    Return the page that follows this item ID. Mutually exclusive with before.

  • beforestring

    Return the page that precedes this item ID. Mutually exclusive with after.

Request

curl -X GET "https://api.rasket.com/oauth/grants?limit=20" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "0199e0a4-6b2d-7c1e-8f3a-2d5b9c7e1a40",
      "client_id": "rkoc_7Fq2Lm9pXw3Kd8Vn1Zt5Rb4C",
      "scopes": ["emails:send", "emails:read"],
      "created_at": "2026-09-12T09:30:00.000Z",
      "revoked_at": null,
      "revoked_reason": null,
      "client": {
        "name": "Acme Invoices",
        "logo_uri": null
      }
    }
  ]
}
  • A revoked grant stays in the list with revoked_at and revoked_reason — user, client, code_reuse, refresh_reuse or operator — so an app never silently disappears.
  • Needs a full_access key. An OAuth access token is refused here whatever its scopes: no scope reaches the grants, so a connected app cannot see or manage connected apps.

Revoke an OAuth grant

DELETE /oauth/grants/{oauth_grant_id}

Disconnect an app: the grant and every token issued under it.

Path parameters

  • oauth_grant_idstringRequired

    The grant's ID.

Request

curl -X DELETE "https://api.rasket.com/oauth/grants/0199e0a4-6b2d-7c1e-8f3a-2d5b9c7e1a40" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "oauth_grant",
  "id": "0199e0a4-6b2d-7c1e-8f3a-2d5b9c7e1a40",
  "revoked_at": "2026-09-12T14:05:12.000Z",
  "revoked_reason": "user"
}
  • The app's next call with any of its tokens is 401 invalid_api_key.
  • Idempotent: revoking a revoked grant answers the revocation it already has.
  • Needs a full_access key; an OAuth access token is refused.