Billing & Usage
Check a project's tier and remaining headroom, cap monthly spend, read the tier catalog, and open checkout or the billing portal — pricing as an API your agent can reason about.
Billing is part of the API. A project sits on a tier with included quotas; beyond those quotas, usage either accrues overage or is throttled at a ceiling you set. You can read live usage and headroom, set or remove the spend ceiling, and start an upgrade — all programmatically, so an agent can self-throttle or escalate to a human before running a data-heavy workload.
Billing routes mount under /v1/admin/projects/:projectId/billing/* and
require developer credentials (a PAT or console session). Server keys
(amb_*_sk_*) are rejected with 403 BILLING_REQUIRES_DEVELOPER_AUTH —
billing is an owner action, not a runtime one.
Status
GET .../billing/status returns the stored and effective tiers, per-meter usage and cost,
the billing period and when it resets, projected overage, the spend ceiling
and how much of it is consumed, the live enforcement state, the
database limits block, and —
importantly for agents — human_action_required, a single field telling
you whether it's safe to proceed unattended.
Use effective_tier for automation that makes quota or feature decisions.
tier remains the persisted subscription/accounting value. The fields normally
match; for operator-comped projects effective_tier is enterprise because
runtime quota and spend-ceiling refusals are waived, while tier preserves the
stored subscription class. Check comped to distinguish that grant from a paid
Enterprise subscription.
enforcement is the live verdict the API itself acts on: when read_only
is true, metered write operations return 402 until the period
resets or the ceiling changes (see Spend ceiling);
over_quota_meters lists meters whose included quota is already consumed —
writes on those meters are refused on throttle mode (402 TIER_QUOTA_EXCEEDED, except mau, where new-user signups keep their
long-standing 429 MAU_CAP_REACHED shape).
Three overage figures, deliberately distinct so the status never overstates what you owe:
| Field | Meaning |
|---|---|
projected_overage_usd_this_month | Live estimate of overage from current usage vs. your included quotas. |
recorded_overage_usd_this_period | Overage tallied into your usage ledger for the current billing period. |
billed_overage_usd_this_period | Overage actually added to your Stripe invoice so far this period. |
metered_billing_live | Whether metered overage is being charged yet (see How overage is collected). |
human_action_required is one of:
| Value | Meaning |
|---|---|
none | Safe to proceed unattended. |
verify_email | The project owner hasn't verified their email; nothing unlocks first. |
approve_checkout | Free tier is near a quota or database cap; a human should approve an upgrade. |
set_ceiling | Paid tier is projecting significant overage with no ceiling set. |
An agent should call this before provisioning new features or pushing large workloads.
Spend ceiling
Cap the monthly bill with PUT .../billing/ceiling. When the current
period's overage cost reaches the ceiling, the project degrades to
read-only mode: metered write operations — tracking events, inserting
or updating collection rows, media uploads, push sends, new user signups —
return 402 SPEND_CEILING_REACHED with a machine-readable payload, while
reads (and deletes, which shrink usage) keep working. Pass
ceiling_usd: null to remove the cap (linear overage, no limit).
The same contract applies to tier quotas on throttle mode (the default):
once a meter's included quota is consumed, writes on that meter return
402 TIER_QUOTA_EXCEEDED with the identical payload shape (reason: "quota"). New-user signups keep their long-standing 429 MAU_CAP_REACHED
shape at the MAU quota. Enforcement takes effect within seconds of a
ceiling change and resets automatically when the billing period rolls.
So you're never surprised by either the bill or the read-only flip, two
control-plane webhook events fire as the ceiling fills — subscribe with
amba_control_webhooks_create (or billing.*):
| Event | Fires |
|---|---|
billing.ceiling_warning | At 80% of the ceiling, once per billing period. |
billing.ceiling_reached | At 100% — the project is now read-only, once per period. |
ceiling_usd must be a finite number between 0 and 100000, or null.
Database limits (free tier)
Every project runs on its own database. Free projects get a small one with two hard caps, so a prototype can never run up a bill:
| Limit | Free | Pro, Scale |
|---|---|---|
| Compute size | 0.25 compute units | Up to 1 unit |
| Compute per billing month | 10 CU-hours (about 40 hours awake at 0.25 units) | No cap |
| Database size | 512 MB (the whole database, including system tables) | No hard cap |
A database sleeps after a minute without queries and wakes on the next request, so an app used in bursts spends its compute hours on real traffic. The compute allowance resets on the 1st of each month (UTC).
At the compute cap the database pauses for the rest of the billing
period. Every call that touches the database returns
402 FREE_TIER_LIMIT_REACHED until the period resets or the project
upgrades; an upgrade resumes the database within seconds.
At the storage cap writes that add data return
402 FREE_TIER_LIMIT_REACHED. Reads and deletes keep working, so deleting
data brings the project back under the limit.
The 402 body names the limit, the reset date, and the one-call upgrade:
limit is database_compute or database_storage; resets_at is null
for storage, which is a size limit. Usage is measured hourly and shows in the
database block of billing status (compute.used_cu_hours,
compute.limit_cu_hours, storage.used_mb, period.resets_at,
limit_reached), in amba billing status, and on the console billing page.
At 80% of the compute allowance the project owner gets one email per billing
period, and human_action_required becomes approve_checkout.
Upgrade in one call with the amba_billing_upgrade MCP tool,
amba billing upgrade --tier pro, or POST .../billing/checkout (see
Upgrade & manage). An app with real users belongs on a
paid plan, where the database has no compute cap.
Tiers
The tier catalog (names, prices, included quotas, overage rates) is
available offline through the amba_billing_tiers MCP tool, so an agent
can reason about upgrades without a network call. It mirrors the pricing
page at amba.dev. The shipped tiers are
free, pro, scale, and enterprise.
Every tier includes every capability you build an app with. Collections, functions, push, gamification, economy, social, AI, sites, and sign-in are available on every tier, including free. Tiers differ by the quotas in the table below. Free projects also carry the free-tier limits on the surfaces that publish on Amba's domains, send messages through Amba's sending reputation, or spend Amba's money.
| Capability | Free | Pro | Scale | Enterprise |
|---|---|---|---|---|
| Price (monthly) | $0 | $20 | $200 | Custom |
| Price (annual, per month) | $0 | $16 | $160 | Custom |
| Monthly active users (MAU) | 1,000 | 25,000 | 250,000 | Custom |
| Engagement events / mo | 10,000 | 250,000 | 2,500,000 | Custom |
| Push notifications / mo | 1,000 | 50,000 | 500,000 | Custom |
| Database storage | 100 MB | 1 GB | 5 GB | Custom |
| Media storage | 250 MB | 1 GB | 25 GB | Custom |
| Database compute / mo | 10 CU-hours | No cap | No cap | Custom |
| Database size (hard cap) | 512 MB | No cap | No cap | Custom |
| All feature categories | Yes | Yes | Yes | Yes |
| Projects per account | 2 | Unlimited | Unlimited | Custom |
| Sleeps after inactivity | 14 days | Never | Never | Never |
Beyond the included quotas, usage on a paid tier accrues linear overage (unless you set a spend ceiling):
| Metered axis | Overage rate |
|---|---|
| MAU | $0.50 per 1,000 MAU |
| Engagement events | $0.50 per 10,000 events |
| Push | $0.50 per 10,000 push |
| Database storage | $1.50 per GB-month |
| Media storage | $0.10 per GB-month |
| Telemetry events | $0.10 per 1,000,000 |
| Agent tool calls | $0.50 per 10,000 calls |
Telemetry events — emitted via Amba.track(name, props, { telemetry: true }) —
are billed separately and far cheaper than engagement events because they
don't fan out to segments, workflows, or push. Use them for high-volume
analytics signals you don't need to drive engagement off.
Agent tool calls — tools/call requests against your project's own
app MCP — are priced like engagement events. Like
telemetry, they have no included quota; every successful call meters.
Free-tier limits
A free project gets everything it needs to build and test an app. The surfaces below run on Amba's own domains, sending reputation, phone number, registrar account, or compute, so on the free tier they are either paid features or capped per day. Upgrading the project lifts every one of them.
| Surface | Free tier | Limit id |
|---|---|---|
Hosted site on *.app.amba.host | Serves with a "Built with Amba · preview" bar and noindex | (no refusal) |
| Custom domains (sites and functions) | Paid plans | custom_domains |
| Domain purchase | Paid plans | domain_purchase |
| Sign-in email (magic link, email OTP) | 25 per day | auth_emails_per_day |
| Sign-in SMS codes | 10 per day across all numbers | sms_otp_per_day |
| Email to any recipient, custom templates | Paid plans (includes ctx.email.send in functions and /v1/client/email/send) | email_send, email_templates |
| Push campaigns (immediate, scheduled) | 3 per day; drafting is unlimited | push_campaign_sends_per_day |
| Direct pushes (test pushes, per-user schedules) | 100 per day | push_direct_sends_per_day |
| Function invocations | 10,000 per day (schedules, queues, and fan-out included), 50 ms CPU each | function_invocations_per_day |
| Function schedules | 2 schedules, at most hourly (5-field cron, minute field a single number) | function_schedules, function_schedule_interval |
| Queue messages | 1,000 per day | queue_messages_per_day |
| Tracked links | 50 new links per day | tracked_links_per_day |
| App builder | Paid plans | app_builder |
| Media storage | 250 MB kept; every upload registration needs size_bytes and the upload URL is capped at it; deleting media frees space | media_storage |
| Hosted site files | 50 MB across every site and kept deployment, at most 50 MB and 1,000 files per deploy; older deployments are dropped for room | site_storage, site_files |
Media and files on *.cdn.amba.host | Images, video, and audio serve inline; HTML, SVG, XML, and scripts download as attachments; every response carries a sandbox CSP | (no refusal) |
Function responses on *.fn.amba.host | JSON serves as-is to apps; every response carries a sandbox CSP, so HTML never runs as a page | (no refusal) |
AI requests always use your project's own provider keys, on every tier.
A refused call returns 402 with a machine-readable body. The message says
which limit was hit, why it exists, and the exact upgrade call:
Daily caps reset at 00:00 UTC. Projects created before these limits launched keep their previous behaviour.
When Amba cannot confirm a free project's limits for a moment (its daily counter
or plan lookup is briefly unreachable), a call that would send, charge, or start
work answers 503 LIMITS_UNAVAILABLE with a Retry-After header, and nothing
is sent or charged. Retry after the given seconds. Paid and comped projects never see this, and reads keep working.
The two-free-projects limit counts the projects you own and the free projects
you joined, on every path that adds one (create, POST /v1/admin/provision,
accepting an invite). It lifts once a project has an active paid subscription
(or the account is comped); starting a checkout and leaving it unpaid changes
nothing. App-factory children provisioned under a paying parent org do not
count against it.
Unclaimed accounts
An account is unclaimed until its owner proves they control its inbox. An
agent can create an account with amba_developer_signup before any human is
involved; it gets a sandbox-…@layers.com address with no inbox at all. A
password signup carries a real address that nobody has confirmed yet, and the
verify_token that signup returns does not count, because the caller already
holds it. Nobody can be reached about an unclaimed account's apps, so its free
projects follow a fixed timeline, and it draws new projects from a small daily
slice of platform capacity.
Three things prove the inbox, and each one ends the timeline at once:
- the claim link:
amba_developer_claimsends a one-click link to the owner's address (an account that already uses that address claims the same one); - signing in with an emailed code (
POST /v1/auth/developer/otp/request, then/v1/auth/developer/otp/verify); - signing in with GitHub when the GitHub account's verified primary email is the account address.
Each project runs its own clock. The clock starts at the latest of these moments: the account was created, the project was created, or the project later became subject to the timeline (free-tier limits turned on for it, a move to the Free plan, or a transfer into the account). A project created in an account that is already months old gets the whole timeline from day 0.
| Project clock | What happens |
|---|---|
| 0–6 days | Normal. |
| 7–13 days | Every admin API response carries an X-Amba-Notice header and every MCP tool result carries a notice with the one-call claim instruction. The notice names the project and its dates. |
| 14 days | The project goes on hold: reads keep working (including collection queries and session refresh), writes return 403 SANDBOX_CLAIM_REQUIRED, functions stop, and hosted sites stop serving. |
| 90 days, after 76 days on hold | The project is archived. Every request returns 403 SANDBOX_CLAIM_REQUIRED; billing, invites, and ownership transfer stay open. |
Archiving deletes nothing. The project's database is kept exactly as it was,
and no later step deletes it. While a project is archived its data cannot be
read or exported. A claim, an upgrade, or a transfer to a claimed account
brings it back in the same request, and every read and export (collection
rows, amba_users_export) works again at once. A project only reaches
the archive after a full 76-day hold, during which every write to it is
refused.
Claiming the account ends the timeline at any point and lifts a hold or an archive in the same request:
The owner clicks the emailed link (valid for 15 minutes) and the account takes their address. Upgrading a held project to a paid plan also lifts its hold.
When the day's capacity for unclaimed accounts is spent, a new project
returns 503 PLATFORM_CAPACITY with details.pool: "unproven" and the claim
call; signup still creates the account and returns project_unavailable in
place of project. Claim, then create the project with amba_projects_create.
Account emails (claim links, sign-in codes, invites) run on durable daily
budgets per account, per address, and platform-wide; a refusal is
429 EMAIL_BUDGET_EXCEEDED with details.scope and a Retry-After header.
One claim email goes to an address per 10 minutes, and asking again sooner
returns 429 CLAIM_EMAIL_RECENTLY_SENT while the first link keeps working.
How overage is collected
When a project's spend mode is overage_bill (rather than throttle),
requests past your included quotas are allowed through and the overage is
tallied into a per-period usage ledger. A daily reconciliation totals each
metered axis for the current billing period and adds the billable amount to
your upcoming invoice — so overage rolls into your next invoice rather
than being charged per-request.
Metered overage billing is rolling out behind a flag. While it's being enabled, usage is still
tracked and shown in GET .../billing/status (recorded_overage_usd_this_period), but nothing is
charged — metered_billing_live is false and billed_overage_usd_this_period stays 0. Once
enabled, the recorded overage is added to your invoice and metered_billing_live flips to true.
Set a spend ceiling if you want hard read-only enforcement (402) instead of
accruing charges.
These numbers come straight from the live tier catalog and are reproduced here for reference. If
they ever appear to differ from the amba_billing_tiers MCP tool or
amba.dev/#pricing, treat those as authoritative — they read the same
source.
Upgrade & manage
Two routes return hosted URLs you redirect the human to — these flows need a browser, so they're surfaced to a person rather than driven by an agent:
| Method | Path | Returns |
|---|---|---|
| GET | /admin/projects/:projectId/billing/status | Live billing state (above). |
| PUT | /admin/projects/:projectId/billing/ceiling | Set / clear the spend ceiling. |
| POST | /admin/projects/:projectId/billing/checkout | { url } — checkout for an upgrade. |
| POST | /admin/projects/:projectId/billing/portal | { url } — manage card, plan, invoices. |
POST .../checkout takes { tier: "pro" | "scale", interval: "month" | "year" }
and returns a checkout url. The amba_billing_upgrade MCP tool and
amba billing upgrade --tier pro make the same call and hand back the link
for the owner to open. If the project already has an active
subscription, it returns 409 SUBSCRIPTION_EXISTS — use the portal to
change plan instead. POST .../portal opens the billing portal so the
owner can update their card, change plan, or download invoices.
MCP tools
| Tool | Does |
|---|---|
amba_billing_status | Stored/effective tier, headroom, overage, and required action. |
amba_billing_tiers | The full tier catalog (offline, no API call). |
amba_billing_set_ceiling | Set or remove the monthly spend ceiling. |
amba_billing_upgrade | Start a paid-plan checkout; lifts the free database caps. |