payment_required
HTTP 402 retryable: false REST and MCP
The workspace is authenticated and permitted to make this call, but the capability it asks for is gated behind an entitlement that is not currently active.
Why it happens
Entitlement and permission are separate gates and they fail with separate codes. A 403 means the credential is not allowed to do this; a 402 means nothing about the credential is wrong and the workspace simply does not have the capability. The entitlement check reads live state each time: a paid subscription, an operational grant, or the complimentary live allowance. The free allowance is one live inbox and 100 email units a month per verified owner identity, claimed by that identity's first eligible workspace, and creating a second workspace does not mint a second allowance.
Every condition below was read in the implementation at the file and line given. They are the branches that raise this code, not a summary of what the name suggests.
- Sending live mail with no paid subscription, no operational grant, and no remaining complimentary live entitlement.
packages/domain/src/billing.ts:702 - Creating a live inbox when the workspace already holds as many live inboxes as its limit allows.
packages/domain/src/billing.ts:730 - Creating or verifying a custom domain without a verified paid subscription. Paid plans are not purchasable yet, so this gate is currently closed for everyone.
packages/domain/src/custom-domains.ts:148 - Creating a live project with neither the go-live inbox entitlement nor a paid subscription.
packages/domain/src/services.ts:598 - Opening a billing portal session when no billing account exists for the workspace, or when its livemode does not match the server's.
packages/domain/src/billing.ts:325
How to fix it
- Check GET /billing/entitlement first. It reports what the workspace currently holds and is the only authoritative answer.
- For custom domains, there is no self-serve route today. Builder at $19/mo and Team at $79/mo are proposed plans at proposed prices and Stripe checkout is disabled, so a paid subscription cannot currently be started.
- If you are at the live inbox limit, delete an inbox you no longer use rather than creating another workspace. The complimentary allowance is per verified owner identity, so a new workspace does not renew it.
- Use a test project while you build. Test inboxes are free, capped at three active per workspace, and never consume email units.
Why it works this way
Receiving is not gated by this. When an allowance runs out, sending stops and mail that already arrived stays readable, because deleting or hiding received mail over a billing state would destroy data the customer already has.
Messages you will see
The message field is written for a human reading a log and is not a contract
value. Branch on code instead. These are the exact strings this code is sent
with today, listed so that a search for one of them lands here.
Where it appears
These are the documented surfaces that raise it. The REST base is https://api.emailforagents.ai/v1, and the remote MCP endpoint is https://mcp.emailforagents.ai/mcp.
POST /projects/{project_id}/inboxesPOST /projects/{project_id}/inboxes/{inbox_id}/messagesPOST /projects/{project_id}/domainsGET /billing/entitlement
The response
Read request_id off the body or the x-request-id header before
doing anything else. It is the only handle on one specific attempt, and for a 503 it is
the only handle at all, because the underlying detail stays server-side.
HTTP/1.1 402
content-type: application/json
x-request-id: req_...
{
"error": {
"code": "payment_required",
"message": "A current paid subscription is required to resume sending.",
"request_id": "req_...",
"retryable": false
}
}
Verified against packages/domain/src/billing.ts:702. The status comes from ERROR_STATUS in packages/contracts/src/errors.ts, which is the one
place a code is mapped to a status.