import { createClient } from "@mailsai/sdk";
const client = createClient(); // reads MAILS_API_KEY
const result = await client.billing.portal(
"https://app.acme.com/settings/billing",
);
console.log(result);from mailsai import create_client
client = create_client() # reads MAILS_API_KEY
result = client.open_billing_portal(
"https://app.acme.com/settings/billing",
)
print(result)curl -X POST 'https://api.mails.ai/v1/billing/portal' \
-H "Authorization: Bearer $MAILS_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"return_url": "https://app.acme.com/settings/billing"
}'mails billing portal{ "url": "https://example.com", "mock": false }201 A redirect URL.
400 Invalid request — malformed JSON or a field failed
validation. Also feature_not_enabled when this
server has a feature switched off (custom domains
before they are available).
401 Missing or invalid API key.
403 The API key lacks the required scope
(insufficient_scope), or the token belongs to an
app connected by sign-in and this operation is
closed to connected apps
(connected_app_not_allowed).
404 Resource not found in this workspace, or the agent
a send names is archived (agent_archived).
409 Conflict — duplicate resource, an in-progress
idempotent request, a draft or scheduled message
that started sending while it was being changed, or
a thread deleted for good while one of its messages
is being sent (duplicate_resource); or an agent
name more than one agent has (agent_name_conflict:
use the agent's id); or a status change on an
archived agent, since archiving cannot be undone
(agent_archived).
500 Internal server error.Open billing portal or checkout
import { createClient } from "@mailsai/sdk";
const client = createClient(); // reads MAILS_API_KEY
const result = await client.billing.portal(
"https://app.acme.com/settings/billing",
);
console.log(result);from mailsai import create_client
client = create_client() # reads MAILS_API_KEY
result = client.open_billing_portal(
"https://app.acme.com/settings/billing",
)
print(result)curl -X POST 'https://api.mails.ai/v1/billing/portal' \
-H "Authorization: Bearer $MAILS_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"return_url": "https://app.acme.com/settings/billing"
}'mails billing portal{ "url": "https://example.com", "mock": false }201 A redirect URL.
400 Invalid request — malformed JSON or a field failed
validation. Also feature_not_enabled when this
server has a feature switched off (custom domains
before they are available).
401 Missing or invalid API key.
403 The API key lacks the required scope
(insufficient_scope), or the token belongs to an
app connected by sign-in and this operation is
closed to connected apps
(connected_app_not_allowed).
404 Resource not found in this workspace, or the agent
a send names is archived (agent_archived).
409 Conflict — duplicate resource, an in-progress
idempotent request, a draft or scheduled message
that started sending while it was being changed, or
a thread deleted for good while one of its messages
is being sent (duplicate_resource); or an agent
name more than one agent has (agent_name_conflict:
use the agent's id); or a status change on an
archived agent, since archiving cannot be undone
(agent_archived).
500 Internal server error.Body Parameters
Where the customer lands after leaving the billing portal, or after finishing or cancelling checkout: an absolute URL. Left out, it is the dashboard's /billing page.
The plan checkout subscribes to: mailsai_pro_monthly (the default), mailsai_scale_monthly, mailsai_pro_yearly or mailsai_scale_yearly. Used only when the workspace has no subscription yet; any other value is a 400 invalid_param_value.
A workspace on an active paid plan with no Stripe subscription on file gets 409 billing_state_inconsistent instead of a Checkout URL, since subscribing again would bill it twice: contact support@mails.ai. Closed to apps connected by sign-in (403 connected_app_not_allowed): use the dashboard, an API key or the mails CLI.
Response
The page to open: a Stripe Billing Portal session when the workspace has a subscription, otherwise a Stripe Checkout session for price_lookup_key. Both lead back to return_url. When mock is true, it is return_url itself with mock query parameters.
True when the server runs with Stripe mocked, so no Stripe session was created; false for a real session.
Was this page helpful?