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.
  • Sending newslettersGuidesUse a campaign, or include an unsubscribe link.
  • 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.
  • Sign-up formsGuidesPop-ups and embedded forms on your site, and the rules that pick one.
  • TemplatesGuidesFifty-seven starters sorted by goal, and how to keep a campaign as a template.
  • 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.
  • Mail appsGuidesComing soon: your Rasket address in Apple Mail, Outlook and the Gmail app.
  • Inbox APIGuidesOpen, read and answer support conversations from your app, with the same key.
  • Support inbox in your appGuidesA Contact us form that opens a conversation your team answers, in five minutes.
  • Replies to your emailsGuidesOne field on a send, and the customer's reply opens a conversation.
  • Let an agent triageGuidesAn AI agent reads, notes and drafts over MCP; a person sends.
  • 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.
  • IntegrationsGuidesEvery integration and its status, and Rasket on Vercel, Netlify and Cloudflare.
  • 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.
  • WordPressGuidesThe Rasket plugin: every email your site sends, through Rasket, with a safe fallback.
  • NotionGuidesTurn a Notion database of people into contacts in a segment, once or every hour.
  • Migrating to RasketGuidesBring contacts, unsubscribes, lists and templates from Mailchimp, Klaviyo and more.
  • PipedreamGuidesRasket actions and instant sources for Pipedream workflows and Connect apps.
  • FramerGuidesThe Rasket plugin: sign-up forms on a Framer site, and Framer forms into contacts.
  • MakeGuidesComing soon: send email and react to email events from a Make scenario.
  • monday.comGuidesTurn board items into contacts and send a template when a board changes.
  • ClickUpGuidesReady-made Zapier and Make automations between ClickUp tasks and Rasket.
  • SlackGuidesEmail alerts in the Slack channels you choose, and /rasket for numbers and look-ups.
  • Google SheetsGuidesComing soon: turn a sheet tab of people into contacts in a segment.
  • AirtableGuidesComing soon: turn an Airtable table of people into contacts in a segment.
  • ShopifyGuidesRasket for Shopify: customers with their consent, store events and ready-made flows.
  • 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.
  • InboxAPI referenceChannels, conversations, messages, notes and search in your support inbox.

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,
    "daily_limit_reason": null,
    "monthly_limit": 50000,
    "contacts_included": 4500,
    "billable_contacts": 1840,
    "peak_billable_contacts": 2210,
    "contact_overage_per_1000_cents": 880,
    "estimated_contact_overage_cents": 0
  },
  "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.daily_limit_reason says why daily_limit is lower than the plan's while a temporary limit sets it: restricted while sending is limited because too much mail bounced or was reported as spam, new_account for a new account's first 24 hours, and new_account_ramp for the rest of its first week, when the limit doubles each clean day. It is null otherwise.
  • 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.emails counts everything sent this month: the API, campaigns and flows draw from the one allowance in usage.included_emails.
  • usage.contacts_included is how many billable contacts the plan includes, and usage.billable_contacts how many the team has now. A contact counts when it is subscribed to at least one topic and not suppressed.
  • usage.peak_billable_contacts is the most the team has held this billing period so far. With pay-as-you-go on, each started 1,000 of that peak past the plan costs usage.contact_overage_per_1000_cents, and usage.estimated_contact_overage_cents is what the period would bill if it ended now. With it off, or with a null rate, new contacts past the plan are refused.
  • 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,
    "daily_limit_reason": null,
    "monthly_limit": 50000,
    "contacts_included": 4500,
    "billable_contacts": 1840,
    "peak_billable_contacts": 2210,
    "contact_overage_per_1000_cents": 880,
    "estimated_contact_overage_cents": 0
  },
  "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,
  "sms": {
    "messages": 9000,
    "is_default": true,
    "default_messages": 9000,
    "used_messages": 1240
  }
}
  • 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.

  • smsinteger | null

    The most messages this project may text in a month with pay-as-you-go on; reaching it refuses the next text and leaves email alone. Omit to leave it alone, or send null to return to the default of ten times the included texts.

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,
  "sms": {
    "messages": 9000,
    "is_default": true,
    "default_messages": 9000,
    "used_messages": 1240
  }
}
  • 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 or inbox_seat.

  • 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.
  • 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 sso turns single sign-on off, so an API key is refused it with 403: a signed-in admin removes it.