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.

API referenceBilling

Billing

The team's plan, usage, invoices and add-ons, and the two hosted pages where a customer pays.

Who can call it

Two reads are open to a scope; everything else on this page needs a full_access key. A scope can read billing; nothing but a full_access key can change it.

What each credential can reach on the billing routes
CredentialThe two readsEverything else
full_access200200
sending_access401 restricted_api_key401 restricted_api_key
OAuth token with billing:read200403 invalid_permission
OAuth token with every scope200403 invalid_permission

The reads are GET /billing and GET /billing/invoices. No OAuth scope reaches the rest: a token holding every scope in the catalogue is still 403 invalid_permission on checkout, the portal, a plan change, pay-as-you-go, both spend-cap routes, both add-on routes and both limit-request routes.

Hosted pages

  • POST /billing/checkout and POST /billing/portal answer a URL. Send the customer's browser there: the card is entered on that hosted page and never passes through this API.
  • return_url must be an absolute https URL. The browser comes back to it with checkout=success or checkout=cancelled appended; without one it comes back to the dashboard.
  • A checkout URL stops working at its expires_at. Ask for a new one.

A few changes are refused for a key even when it is full_access: anything that would turn off the team's single sign-on — removing the sso add-on, or a plan change that drops it — needs a signed-in admin, because it decides who may sign in.

Endpoints

Retrieve billing

GET /billing

The plan, this period's usage, payment state, add-ons and any pending change.

Request

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

Response 200

{
  "object": "billing",
  "plan": {
    "code": "pro_50k",
    "name": "Pro 50k",
    "monthly_price_cents": 2000,
    "included_emails": 50000,
    "overage_per_1000_cents": 60
  },
  "effective_plan_code": "pro_50k",
  "subscription": {
    "status": "active",
    "current_period_start": "2026-09-01T00:00:00.000Z",
    "current_period_end": "2026-10-01T00:00:00.000Z",
    "invoicing": "automatic",
    "latest_invoice_status": "paid",
    "trial_ends_at": null
  },
  "usage": {
    "period_start": "2026-09-01",
    "period_end": "2026-10-01",
    "emails": 12840,
    "included_emails": 50000,
    "emails_today": 412,
    "day_start": "2026-09-14",
    "daily_limit": 10000,
    "monthly_limit": 50000,
    "marketing_emails": 0,
    "marketing_included_emails": 10000,
    "marketing_contacts_included": 1000,
    "marketing_addon_code": null
  },
  "pay_as_you_go": {
    "enabled": false
  },
  "payment_method": {
    "present": true,
    "brand": "visa",
    "last4": "4242"
  },
  "addons": [],
  "pending_change": null,
  "payment_state": null,
  "checkout_available": false,
  "paid_plan": true,
  "storage_addon_purchase": "add_item",
  "inbox_seats": {
    "code": "inbox_seat",
    "quantity": 0,
    "included": 3
  },
  "dedicated_ip": {
    "recommended_min_daily": 5000,
    "avg_daily_sends_30d": 412
  }
}
  • payment_method says whether a card is on file and shows its brand and last four digits. The card itself never leaves the payment provider.
  • usage.emails_today counts today's sends against usage.daily_limit, the most this team can send today. The count starts again at 00:00 UTC. daily_limit is null when there is no daily limit.
  • usage.monthly_limit is the most this team can send this month before a send is refused. It can differ from usage.included_emails, the plan's allowance, when a limit was set for the team or the sending tier's cap is lower than the plan. null when nothing caps the month.
  • checkout_available is true when the team has no subscription yet, so POST /billing/checkout is the way to start one. paid_plan is the other question: a team can hold a subscription for inbox storage alone while still on the free plan.
  • usage.marketing_* is the marketing meter, its own pool: campaign recipients never count against usage.emails, and POST /emails never counts against marketing_emails.
  • inbox_seats counts Inbox mailboxes past the ones the plan includes; dedicated_ip puts the volume a dedicated IP is recommended from beside this team's own 30-day daily average.
  • storage_addon_purchase says how to buy inbox storage right now — add_item through POST /billing/addons, checkout through POST /billing/checkout with addon_code, held when the team already has it, or unavailable when it cannot be bought at the moment. Both purchase calls answer 503 service_unavailable while it is unavailable, before anything is charged or recorded.

List invoices

GET /billing/invoices

The team's invoices, newest first, with links to the hosted invoice and its PDF.

Request

curl -X GET "https://api.rasket.com/billing/invoices" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "in_1Q2w3E4r5T6y7U8i",
      "number": "ACME-0007",
      "status": "paid",
      "total_cents": 2000,
      "currency": "usd",
      "created_at": "2026-09-01T00:00:00.000Z",
      "hosted_invoice_url": "https://invoices.example.com/in_1Q2w3E4r5T6y7U8i",
      "invoice_pdf": "https://invoices.example.com/in_1Q2w3E4r5T6y7U8i.pdf"
    }
  ]
}
  • Read from the payment provider and cached for sixty seconds per team.

Start a subscription

POST /billing/checkout

A hosted checkout page for a plan, or for inbox storage on its own. Send the customer's browser to its `url`.

Body

  • plan_codestring

    The plan to start the subscription on, such as pro_50k or scale. Send this or addon_code, never both.

  • addon_codestring

    Buy an add-on on its own, leaving the team on the plan it is already on: inbox_storage_100gb or inbox_seat. Send this or plan_code, never both.

  • return_urlstring

    Where the browser comes back to, with checkout=success or checkout=cancelled appended. Absolute https only. Defaults to the dashboard page return_to names.

  • return_tostring

    Which dashboard page to come back to when there is no return_url: plan (the default), inbox or inbox_settings.

1 more field (quantity)
  • quantityinteger

    Units to buy, 1–100, with addon_code only. Defaults to 1; only inbox_seat is sold by the unit.

Request

curl -X POST "https://api.rasket.com/billing/checkout" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "plan_code": "pro_50k",
  "return_url": "https://app.example.com/settings/billing"
}'

Response 200

{
  "object": "checkout_session",
  "url": "https://checkout.example.com/c/pay/cs_live_a1B2c3D4",
  "expires_at": "2026-09-13T10:00:00.000Z"
}
  • The customer enters their card on the hosted page. Card details never pass through this API.
  • A return_url that is not absolute https is 422 validation_error.
  • Send exactly one of plan_code and addon_code. Neither is 422 missing_required_field; both is 422 invalid_parameter.
  • addon_code: "inbox_storage_100gb" buys 100 GB of inbox storage without a plan. The subscription it creates carries only that add-on, and POST /billing/plan can add a plan to it later.
  • A team that already has a subscription is 422 validation_error: it changes its plan with POST /billing/plan and buys add-ons with POST /billing/addons.
  • Called with an API key, no email address is sent to the payment provider: the customer types one on the hosted page.

Open the customer portal

POST /billing/portal

A hosted page for the payment method, address, tax ID, invoices and cancellation.

Request

curl -X POST "https://api.rasket.com/billing/portal" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "portal_session",
  "url": "https://billing.example.com/p/session/bps_a1B2c3D4"
}
  • Takes no body. Send the customer's browser to url.

Change the plan

POST /billing/plan

Move an existing subscription to another paid plan.

Body

  • plan_codestringRequired

    The plan to move to, such as pro_50k, pro_100k or scale.

Request

curl -X POST "https://api.rasket.com/billing/plan" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "plan_code": "scale"
}'

Response 200

{
  "object": "billing",
  "plan": {
    "code": "pro_50k",
    "name": "Pro 50k",
    "monthly_price_cents": 2000,
    "included_emails": 50000,
    "overage_per_1000_cents": 60
  },
  "effective_plan_code": "pro_50k",
  "subscription": {
    "status": "active",
    "current_period_start": "2026-09-01T00:00:00.000Z",
    "current_period_end": "2026-10-01T00:00:00.000Z",
    "invoicing": "automatic",
    "latest_invoice_status": "paid",
    "trial_ends_at": null
  },
  "usage": {
    "period_start": "2026-09-01",
    "period_end": "2026-10-01",
    "emails": 12840,
    "included_emails": 50000,
    "emails_today": 412,
    "day_start": "2026-09-14",
    "daily_limit": 10000,
    "monthly_limit": 50000,
    "marketing_emails": 0,
    "marketing_included_emails": 10000,
    "marketing_contacts_included": 1000,
    "marketing_addon_code": null
  },
  "pay_as_you_go": {
    "enabled": false
  },
  "payment_method": {
    "present": true,
    "brand": "visa",
    "last4": "4242"
  },
  "addons": [],
  "pending_change": null,
  "payment_state": null,
  "checkout_available": false,
  "paid_plan": true,
  "storage_addon_purchase": "add_item",
  "inbox_seats": {
    "code": "inbox_seat",
    "quantity": 0,
    "included": 3
  },
  "dedicated_ip": {
    "recommended_min_daily": 5000,
    "avg_daily_sends_30d": 412
  }
}
  • An upgrade takes effect at once, with prorations. A downgrade is scheduled for the end of the period and shows in pending_change.
  • Free to paid is a checkout, and paid to free is a cancellation in the portal, so both are refused here.
  • A change that would turn off the team's single sign-on is refused for an API key: a signed-in admin makes it.

Set pay-as-you-go

POST /billing/payg

Keep sending beyond the plan's included emails, billed per thousand, or stop at them.

Body

  • enabledbooleanRequired

    true to continue beyond the quota, false to stop at it.

Request

curl -X POST "https://api.rasket.com/billing/payg" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true
}'

Response 200

{
  "object": "billing_payg",
  "enabled": true,
  "overage_ceiling_emails": null
}
  • It is on by default once the project is on a paid plan with a payment method on file. Setting it here makes it your choice, and it is not changed for you again.
  • Switching it on needs a plan with an overage rate and a payment method on file; otherwise 422 validation_error names what is missing.
  • It raises only the monthly quota — never the daily cap or a restricted team's limits. There is no overage ceiling; the project's spend cap still applies.

Read the spend cap

GET /billing/spend-cap

The most this project may send this month, the rungs it warns at, and what it has sent.

Request

curl -X GET "https://api.rasket.com/billing/spend-cap" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "billing_spend_cap",
  "emails": 500000,
  "is_default": true,
  "alert_pcts": [80, 100],
  "used_emails": 412300,
  "sending_paused_at": null,
  "sending_paused_reason": null
}
  • used_emails counts billable recipients on both meters, so a campaign and a receipt draw on the same cap.
  • A project that has set no cap of its own is held to ten times its monthly allowance, which is what is_default reports.

Set the spend cap

POST /billing/spend-cap

Set how much this project may send in a month, and when to be warned.

Body

  • emailsintegerRequired

    The most this project may send in a month, in billable recipients. 0 pauses it on its next message.

  • alert_pctsinteger[]

    Whole percentages of the cap to be warned at, one to eight of them. Omit to leave the current rungs alone.

Request

curl -X POST "https://api.rasket.com/billing/spend-cap" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "emails": 500000,
  "alert_pcts": [80, 100]
}'

Response 200

{
  "object": "billing_spend_cap",
  "emails": 500000,
  "is_default": false,
  "alert_pcts": [80, 100],
  "used_emails": 412300,
  "sending_paused_at": null,
  "sending_paused_reason": null
}
  • Reaching the cap pauses this project's sending and leaves every other project on the account untouched.
  • Saving takes effect at once: a cap above the month's usage lifts a pause the cap caused, and a cap at or below it pauses the project there and then.
  • While it is paused, a send answers 429 monthly_quota_exceeded. Raising the cap starts it again, and so does the next month.

Ask for a higher sending limit

POST /limits/requests

Ask to send more. Small, safe increases are granted in the same request.

Body

  • requested_dailyinteger

    The daily limit you would like, in emails a day. The only ask that can be granted automatically.

  • requested_monthlyinteger

    The monthly limit you would like. Always read by a person: a monthly allowance is part of the plan.

  • expected_volumeinteger

    Emails a month you expect to send at peak.

  • use_casestringRequired

    What this project sends and why it needs more room. At most 1,000 characters.

Request

curl -X POST "https://api.rasket.com/limits/requests" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "requested_daily": 20000,
  "expected_volume": 400000,
  "use_case": "Receipts and sign-in links for a store that is about to run a sale."
}'

Response 201

{
  "object": "limit_request",
  "id": "0199f1d5-6a3e-7c21-9a2f-3b9e2c4d5e6f",
  "status": "approved",
  "requested_daily": 20000,
  "requested_monthly": null,
  "expected_volume": 400000,
  "use_case": "Receipts and sign-in links for a store that is about to run a sale.",
  "granted_daily": 20000,
  "granted_monthly": null,
  "decision_reason": null,
  "decided_at": "2026-09-14T10:31:00.000Z",
  "created_at": "2026-09-14T10:31:00.000Z"
}
  • An ask for a daily limit inside the band — at most twice what is in force, never past the ceiling of the project's sending tier, after its first week, and only while its delivery numbers are good — is approved in the same response and the new limit is already in force.
  • Anything else is pending: somebody here reads it, and decision_reason says why it was not automatic. A project whose sending is being held down is declined, with the reason.
  • Asking again while one is still pending answers with the ask already open rather than opening a second.

List limit requests

GET /limits/requests

The asks this project has made, newest first, and what became of each.

Request

curl -X GET "https://api.rasket.com/limits/requests" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "data": [
    {
      "object": "limit_request",
      "id": "0199f1d5-6a3e-7c21-9a2f-3b9e2c4d5e6f",
      "status": "approved",
      "requested_daily": 20000,
      "requested_monthly": null,
      "expected_volume": 400000,
      "use_case": "Receipts and sign-in links for a store that is about to run a sale.",
      "granted_daily": 20000,
      "granted_monthly": null,
      "decision_reason": null,
      "decided_at": "2026-09-14T10:31:00.000Z",
      "created_at": "2026-09-14T10:31:00.000Z"
    }
  ],
  "has_more": false
}

Set an add-on

POST /billing/addons

Buy an add-on, or set how many units of it the team holds.

Body

  • addon_codestringRequired

    extra_domains_100, dedicated_ip, sso, inbox_storage_100gb, inbox_seat, or one of the marketing plans: marketing_5k, marketing_10k, marketing_15k, marketing_25k, marketing_50k, marketing_100k, marketing_150k.

  • quantityinteger

    Units held after the call, 1–100. A set, not an increment. Defaults to 1.

2 more fields (region, expected_daily_volume)
  • regionstring

    dedicated_ip only: the region the pool should live in. Defaults to us-east-1.

  • expected_daily_volumeinteger

    Deprecated and ignored: a dedicated IP has no volume requirement.

Request

curl -X POST "https://api.rasket.com/billing/addons" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "addon_code": "extra_domains_100",
  "quantity": 1
}'

Response 200

{
  "object": "team_addon",
  "addon_code": "extra_domains_100",
  "name": "100 extra domains",
  "monthly_price_cents": 2000,
  "quantity": 1,
  "status": "active",
  "requested_at": "2026-09-12T10:00:00.000Z",
  "provisioned_at": "2026-09-12T10:00:00.000Z"
}
  • Every add-on is an item on the team's subscription, so a team without one is 422 validation_error carrying details.next: "checkout" — POST /billing/checkout starts a subscription, and for inbox_storage_100gb it can buy the add-on on its own.
  • Everything except inbox_storage_100gb also needs a paid plan, so a team that bought inbox storage on its own is still refused the rest of the catalogue.
  • dedicated_ip is billed from now and normally reaches active within the request; it stays provisioning only while no address is free. Warm-up then runs on its own, and GET /billing shows it.
  • For inbox_seat, quantity must equal the team's open mailboxes minus the ones the plan includes: seats follow the mailboxes.
  • A marketing plan replaces any other marketing plan the team holds, in the same call, and takes quantity 1.
  • sso needs the Scale plan or above, and is included on Enterprise.

Remove an add-on

DELETE /billing/addons/{addon_code}

Take an add-on off the subscription, with a proration.

Path parameters

  • addon_codestringRequired

    Any add-on code POST /billing/addons accepts.

Request

curl -X DELETE "https://api.rasket.com/billing/addons/extra_domains_100" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "team_addon",
  "addon_code": "extra_domains_100",
  "name": "100 extra domains",
  "monthly_price_cents": 2000,
  "quantity": 1,
  "status": "canceled",
  "requested_at": "2026-09-12T10:00:00.000Z",
  "provisioned_at": "2026-09-12T10:00:00.000Z"
}
  • Removing extra_domains_100 deletes no domain: the lower limit only refuses the next one you add. Removing a marketing plan likewise keeps the contacts you hold and refuses the next one past the plan's own limit.
  • Removing sso turns single sign-on off, so an API key is refused it with 403: a signed-in admin removes it.