Docs · Reference
Plans and limits
What each plan allows, how usage is counted, what happens at a limit, and every error code a limit returns.
Plans measure email volume and AI calls, not inboxes. The numbers below are the same ones the pricing page shows and GET /platform/plans returns. No plan adds a footer to your mail.
| Limit | Free | Builder ($19) | Team ($79) | Scale |
|---|---|---|---|---|
| Inboxes | 10 | 100 | 1,000 | Unlimited |
| Emails a month (in + out) | 3,000 | 25,000 | 150,000 | Agreed |
| Sends a day (workspace cap) | 100 | 2,000 | 10,000 | Agreed |
| Custom domains | 1 | 10 | 50 | Agreed |
| AI categorisations a month | 1,000 | 25,000 | 150,000 | Agreed |
| Reply drafts a month | 0 | 1,000 | 10,000 | Agreed |
| Seats | 1 | 3 | 10 | Agreed |
| Mail retention | 30 days | 1 year | 2 years | Unlimited or agreed |
| Past the monthly limits | Stops | Overage | Overage | Overage |
#How usage is counted
- Emails are every message an inbox receives plus every message it sends, each counted once however many recipients it has and however often a delivery is retried. Bounce reports about your own sends and our sign-in emails don’t count.
- AI categorisations are JEV calls on inbound mail (one per message). Nothing is counted when AI processing is off for the workspace.
- Reply drafts count each successful
POST /v1/messages/:id/draft-reply. - Inboxes count the inboxes you haven’t deleted. Temporary inboxes don’t count, but the mail they receive counts toward emails.
- Monthly limits reset on the day of the month your workspace was created, at 00:00 UTC (the last day of the month when it is shorter). The daily send cap resets at 00:00 UTC.
#What happens at a limit
Incoming mail is never dropped for being over a limit. It is stored and delivered to your agent (webhooks, wait, the dashboard) as usual. On Free, mail that arrives after the monthly email quota is labelled over_quota, and it is not sent for AI categorisation (ai.enrichment_skipped is "quota").
On paid plans, emails and AI categorisations past the monthly limits are counted as overage: $1 per 1,000 extra emails and $0.50 per 1,000 extra categorisations. Payments aren’t live yet, so overage is measured and shown on your dashboard but not charged. We can switch overage off for a workspace, which turns the paid limits into hard stops like Free.
| Status & code | When |
|---|---|
403 plan_limit_inboxes | POST /v1/inboxes when the plan’s inboxes are all in use. Replaying an existing client_id still returns that inbox. |
403 plan_limit_domains | Adding a custom domain when the plan’s domains are all in use. |
402 plan_limit_emails | Send or reply when the monthly email quota is used up on Free, or on a paid plan with overage off. Receiving keeps working. |
402 plan_limit_drafts | POST /v1/messages/:id/draft-reply when the monthly reply drafts are used up (Free includes none). |
403 plan_limit_seats | Adding a member when the plan’s seats are all taken. |
429 org_daily_limit_exceeded | The workspace’s sends for today (the plan’s sends a day) are used up. Resets at 00:00 UTC. |
429 daily_send_limit_exceeded | This inbox’s own daily send limit is used up. Resets at 00:00 UTC. |
Handle 402 by pausing sends until the next period or asking for an upgrade; retrying won’t help. Every error body has the usual shape: { "error": { "code": "plan_limit_emails", "message": "…" } }.
#Retention
Each plan keeps mail for a set time: 30 days on Free, 1 year on Builder, 2 years on Team, unlimited or agreed on Scale. Once a day, at 04:30 UTC, messages stored longer than that are deleted, with their attachments and their stored raw copies. Threads left without messages go too. Deleted mail can’t be recovered, and no webhook event is sent for it.
- Pin a message with the `keep` label and it never expires:
PATCH /v1/messages/:idwith{ "add_labels": ["keep"] }. Remove the label to let retention apply again. - Choose a shorter retention for the whole workspace under Settings, or with
PATCH /platform/org { "retention_days": 30 }from a dashboard session (owners only). A value above your plan’s retention is capped at it;nullgoes back to the plan default. - Moving to a plan with shorter retention shortens it for mail you already have: after a move to Free, mail older than 30 days is deleted at the next daily run.
- Mail that is still waiting to be sent is never deleted. Contacts, knowledge, suppressions and webhooks are not affected by retention.
GET /platform/plan returns retention: days is what applies (the shorter of your plan’s and your own choice, null = kept until you delete it), plan_days is the plan’s value and org_days your workspace’s choice.
#Checking your plan
The dashboard reads these routes with your session cookie; they aren’t available to API keys.
| Route | Returns |
|---|---|
GET /platform/plans | Every plan with price_monthly, price_annual and limits (null = unlimited), plus overage_usd. Public, no sign-in. |
GET /platform/plan | Your plan (key, name, price, effective limits), the current period (start, end), usage (emails, emails_in, emails_out, ai_categorizations, drafts, inboxes, domains, seats), overage (emails, ai_categorizations, reply_drafts, estimated_usd), allow_overage and retention (days, plan_days, org_days). |
POST /platform/plan/upgrade-request | Body { "plan": "builder", "message": "…" } → 202. Emails us your request and current usage; we reply to your sign-in address within one business day. At most 5 a day. |
GET /platform/usage?days=30 | Sent and received per UTC day, today’s counts, and the daily cap (limits.daily_send_limit). |
{
"plan": { "key": "free", "name": "Free", "price_monthly": 0, "limits": { "inboxes": 10, "emails_per_month": 3000, "sends_per_day": 100, "custom_domains": 1, "ai_categorizations_per_month": 1000, "reply_drafts_per_month": 0, "seats": 1, "retention_days": 30 } },
"period": { "start": "2026-09-15T00:00:00.000Z", "end": "2026-10-15T00:00:00.000Z" },
"usage": { "emails": 2098, "emails_in": 1486, "emails_out": 612, "ai_categorizations": 731, "drafts": 0, "inboxes": 3, "domains": 0, "seats": 1 },
"overage": { "emails": 0, "ai_categorizations": 0, "reply_drafts": 0, "estimated_usd": 0 },
"allow_overage": false,
"retention": { "days": 30, "plan_days": 30, "org_days": null }
}