The tiers gate what the engine will do. Nothing here takes money.
Three tiers, and every limit on them, live in the buyer's own config/api_keys.json — an agency edits that file, not this code, to rename a tier, raise a limit, set a price, or move a client up. Usage is counted from the engine's own files on every request, and a request that would cross a limit is refused whole, before any work happens, with the code, the tier, the limit, the usage and the exact config key to edit.
charges_processed: false — in mock mode and every other mode
no payment client exists in this buildNo charge is processed: billing is a pre-wire. Mock mode is the only supported mode and this build contains no payment client, so nothing can be billed in any mode — the tiers gate what the engine will do, they do not move money.
In the buyer's own install, their keys are what make the engine live — the connectors, the spend authorisations and the export all read config/api_keys.json, and billing's own state changes with it. What does not change is the money path: this build contains no payment client, so the tiers refuse work at the limit rather than billing anyone. Billing here is a pre-wire.
The monthly prices shipped in config/api_keys.json are example defaults for the buyer to edit. They are not Paladia's prices, and no charge is processed anywhere in this build.
3 tiers, read out of the config — not written into this page
Starter
default tierexample default in the buyer's config — edit billing.tiers.starter.monthly_price_usd; not Paladia's price and not a charge
A first client or two, one campaign at a time.
- Up to 5 client namespaces
- 6 campaigns per client
- 300 assets per client
- 2500 stored events per client
Agency
example default in the buyer's config — edit billing.tiers.agency.monthly_price_usd; not Paladia's price and not a charge
A working agency book, several clients in parallel.
- Up to 25 client namespaces
- 30 campaigns per client
- 2000 assets per client
- 25000 stored events per client
Network
example default in the buyer's config — edit billing.tiers.network.monthly_price_usd; not Paladia's price and not a charge
A multi-brand or franchise group running every client at once.
- Up to 100 client namespaces
- 200 campaigns per client
- 10000 assets per client
- 100000 stored events per client
Usage read out of the engine's own files
Full Frequency Co.
within every limitTorczon Digital
within every limitWhat each count is
counted from the engine's own files on every request — no hand-maintained counter exists
Three requests carry a tier limit
| Action | Route | Refusal code | HTTP |
|---|---|---|---|
| create_client | POST /api/clients | client_limit_reached | 402 |
| create_campaign | POST /api/campaigns | campaign_limit_reached | asset_limit_reached | 402 |
| store_events | POST /api/clients/{slug}/events | event_limit_reached | 402 |
The limits, and the key that owns each one
What this is not
- No charge is processed: billing is a pre-wire. Mock mode is the only supported mode and this build contains no payment client, so nothing can be billed in any mode — the tiers gate what the engine will do, they do not move money.
- The monthly prices shipped in config/api_keys.json are example defaults for the buyer to edit. They are not Paladia's prices, and no charge is processed anywhere in this build.
- A refused request is refused before any work happens: no campaign is generated, no event is stored, nothing is truncated to fit the remaining headroom.
- The API has no authentication in v0.1, so these limits are an honesty device for the buyer's own install, not a security boundary.
The gate was pushed past its limit, and these are its own answers
A campaign over the campaign limit
HTTP 402tier 'demo' (Demo tier (this keys file only)) allows 1 campaign per client; billing-demo-probe is already at 1 and this request would take it to 2. This campaign was not generated: no agents ran, no assets were written and nothing was billed.
Nothing was generated: the engine refuses rather than performing work above the tier's limit, and it never truncates a campaign or drops part of a batch to fit. No charge is processed.
{
"action": "create_campaign",
"billing_mode": "mock",
"charges_processed": false,
"client_slug": "billing-demo-probe",
"code": "campaign_limit_reached",
"config_key": "billing.tiers.demo.limits.campaigns_per_client",
"config_path": "config/api_keys.json",
"how_to_change": "Raise billing.tiers.demo.limits.campaigns_per_client in config/api_keys.json, or move this client to a higher tier under billing.assignment — a config edit, no code change.",
"limit": 1,
"limit_name": "campaigns_per_client",
"message": "tier 'demo' (Demo tier (this keys file only)) allows 1 campaign per client; billing-demo-probe is already at 1 and this request would take it to 2. This campaign was not generated: no agents ran, no assets were written and nothing was billed.",
"note": "Nothing was generated: the engine refuses rather than performing work above the tier's limit, and it never truncates a campaign or drops part of a batch to fit. No charge is processed.",
"projected": 2,
"refused": true,
"requested": 1,
"tier": "demo",
"tier_display_name": "Demo tier (this keys file only)",
"unit": "campaigns per client",
"used": 1
}A campaign whose projected assets do not fit
HTTP 402tier 'demo-assets' (Demo tier, asset gate (this keys file only)) allows 20 assets per client; billing-demo-probe is already at 18 and this request would take it to 54. This 4-week campaign would produce 36 assets (9 per week). It was not generated at all — the engine does not generate a shortened campaign to fit.
Nothing was generated: the engine refuses rather than performing work above the tier's limit, and it never truncates a campaign or drops part of a batch to fit. No charge is processed.
{
"action": "create_campaign",
"billing_mode": "mock",
"charges_processed": false,
"client_slug": "billing-demo-probe",
"code": "asset_limit_reached",
"config_key": "billing.tiers.demo-assets.limits.assets_per_client",
"config_path": "config/api_keys.json",
"how_to_change": "Raise billing.tiers.demo-assets.limits.assets_per_client in config/api_keys.json, or move this client to a higher tier under billing.assignment — a config edit, no code change.",
"limit": 20,
"limit_name": "assets_per_client",
"message": "tier 'demo-assets' (Demo tier, asset gate (this keys file only)) allows 20 assets per client; billing-demo-probe is already at 18 and this request would take it to 54. This 4-week campaign would produce 36 assets (9 per week). It was not generated at all — the engine does not generate a shortened campaign to fit.",
"note": "Nothing was generated: the engine refuses rather than performing work above the tier's limit, and it never truncates a campaign or drops part of a batch to fit. No charge is processed.",
"projected": 54,
"refused": true,
"requested": 36,
"tier": "demo-assets",
"tier_display_name": "Demo tier, asset gate (this keys file only)",
"unit": "assets per client",
"used": 18
}An event batch over the stored-event limit
HTTP 402tier 'demo' (Demo tier (this keys file only)) allows 2 events stored per client; billing-demo-probe is already at 0 and this request would take it to 3. This batch was not stored: attribution is all-or-nothing, so a batch that does not fit is refused whole rather than trimmed to the remaining headroom.
Nothing was stored: the engine refuses rather than performing work above the tier's limit, and it never truncates a campaign or drops part of a batch to fit. No charge is processed.
{
"action": "store_events",
"billing_mode": "mock",
"charges_processed": false,
"client_slug": "billing-demo-probe",
"code": "event_limit_reached",
"config_key": "billing.tiers.demo.limits.events_per_client",
"config_path": "config/api_keys.json",
"how_to_change": "Raise billing.tiers.demo.limits.events_per_client in config/api_keys.json, or move this client to a higher tier under billing.assignment — a config edit, no code change.",
"limit": 2,
"limit_name": "events_per_client",
"message": "tier 'demo' (Demo tier (this keys file only)) allows 2 events stored per client; billing-demo-probe is already at 0 and this request would take it to 3. This batch was not stored: attribution is all-or-nothing, so a batch that does not fit is refused whole rather than trimmed to the remaining headroom.",
"note": "Nothing was stored: the engine refuses rather than performing work above the tier's limit, and it never truncates a campaign or drops part of a batch to fit. No charge is processed.",
"projected": 3,
"refused": true,
"requested": 3,
"tier": "demo",
"tier_display_name": "Demo tier (this keys file only)",
"unit": "events stored per client",
"used": 0
}One more client namespace than the account allows
HTTP 402tier 'demo' (Demo tier (this keys file only)) allows 2 client namespaces; the account is already at 3 and this request would take it to 4. This namespace was not created.
Nothing was created: the engine refuses rather than performing work above the tier's limit, and it never truncates a campaign or drops part of a batch to fit. No charge is processed.
{
"action": "create_client",
"billing_mode": "mock",
"charges_processed": false,
"client_slug": null,
"code": "client_limit_reached",
"config_key": "billing.tiers.demo.limits.clients",
"config_path": "config/api_keys.json",
"how_to_change": "Raise billing.tiers.demo.limits.clients in config/api_keys.json, or move this client to a higher tier under billing.assignment — a config edit, no code change.",
"limit": 2,
"limit_name": "clients",
"message": "tier 'demo' (Demo tier (this keys file only)) allows 2 client namespaces; the account is already at 3 and this request would take it to 4. This namespace was not created.",
"note": "Nothing was created: the engine refuses rather than performing work above the tier's limit, and it never truncates a campaign or drops part of a batch to fit. No charge is processed.",
"projected": 4,
"refused": true,
"requested": 1,
"tier": "demo",
"tier_display_name": "Demo tier (this keys file only)",
"unit": "client namespaces",
"used": 3
}The same request, allowed after one line of config
The request that was refused a moment earlier
now allowedallowed — the engine's own verdict after the edit is allowed on tier demo-upgraded, where the identical request was refused a moment earlier on the lower tier. The brief is unchanged, so the campaign id is unchanged and it overwrites itself: the counts below do not move.
usage after the upgrade: 1 campaign(s), 18 asset(s), 0 event(s)
Where the limits change
billing.tiers.<tier>.limits.<limit> · billing.tiers.<tier>.monthly_price_usd · billing.assignment.<client-slug> · billing.default_tier · billing.enforce_limits
No charge is processed in any of this, in any tier: mock mode is the only supported mode and this build contains no payment client. The gate refuses work; it does not take money.
What this page shows, and what it does not
- Shown: the tiers, the limits and the usage the engine reports for this export, and the four refusals it produced when pushed past a limit. Every number on this page is a field of that export.
- Not shown: any charge, any invoice, any live subscription. charges_processed is false here, and no code path in this build can make it true — there is no payment client to call.
- The prices on the tier cards are the example values in the buyer's own config/api_keys.json. They are labelled as the buyer's defaults wherever they appear, because that is what they are.
- What makes it live is the buyer's own keys file in their own install. The demo you are reading ran in mock mode with no keys configured, which is why it can be shown at all.