---
title: "Open billing portal or checkout"
method: POST
path: "/v1/billing/portal"
tags: ["Billing"]
---

# Open billing portal or checkout

`POST /v1/billing/portal`

## Request

- Base URL: `https://api.mails.ai`
- URL: `https://api.mails.ai/v1/billing/portal`
- Auth: HTTP bearer

**Requires scope:** `manage`. Returns a Stripe Billing Portal URL when already subscribed, otherwise a Checkout URL to subscribe. 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.

## Request body

- BillingPortalRequest
  - `return_url` string, uri: 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.
  - `price_lookup_key` string: 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`.

## Response `201`

A redirect URL.

- object
  - `url` string, uri, required: 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.
  - `mock` boolean: True when the server runs with Stripe mocked, so no Stripe session was created; false for a real session.

## Other responses

- `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.

---

[API](https://skmtc.dev/mails/apis/mails-ai-api.md) · [All operations](https://skmtc.dev/mails/apis/mails-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mails/apis/mails-ai-api/revisions/4d4930a8c33b?raw)
