MOCK MODE — no API keys9 platforms2 client namespaces0/9 provider keys configuredkill-switch disarmedengine ok · v0.1.06 agents · pipeline complete
Billing pre-wire

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.

mock mode — tiers enforced locally, no charge processed

charges_processed: false — in mock mode and every other mode

no payment client exists in this build

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.

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.

Billing state
mock
mock mode — tiers enforced locally, no charge processed
Limits enforced
yes
4 limit dimensions, each a config key
Prices
buyer defaults
example values in the buyer's keys file — not Paladia's prices
Config
config/api_keys.json
5 config keys the buyer owns

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.

What the buyer's own keys file defines

3 tiers, read out of the config — not written into this page

Every price below is an example default the buyer edits in their own file. None of them is Paladia's price, and none of them is a charge: the tiers decide what the engine will do, not what anyone pays.

Starter

default tier
USD 99/month

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

assets per client
300
campaigns per client
6
client namespaces
5
events stored per client
2500
  • Up to 5 client namespaces
  • 6 campaigns per client
  • 300 assets per client
  • 2500 stored events per client

Agency

USD 299/month

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.

assets per client
2000
campaigns per client
30
client namespaces
25
events stored per client
25000
  • Up to 25 client namespaces
  • 30 campaigns per client
  • 2000 assets per client
  • 25000 stored events per client

Network

USD 899/month

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.

assets per client
10000
campaigns per client
200
client namespaces
100
events stored per client
100000
  • Up to 100 client namespaces
  • 200 campaigns per client
  • 10000 assets per client
  • 100000 stored events per client
Counted, not remembered

Usage read out of the engine's own files

the engine's own files: one count per namespace directory, per campaign in that client's index.json, per asset those campaigns really carry, per row in that client's events.json An account of 2 namespace(s) sits on the starter tier, whose limit is 5.

Full Frequency Co.

within every limit
full-frequency-co
tier Starterbilling.assignment.full-frequency-co1 campaign(s) stored
campaigns per client1 of 6 · 5 left · 16.7%
assets per client36 of 300 · 264 left · 12%
events stored per client96 of 2500 · 2404 left · 3.8%

Torczon Digital

within every limit
torczon-digital
tier Starterbilling.assignment.torczon-digital1 campaign(s) stored
campaigns per client1 of 6 · 5 left · 16.7%
assets per client36 of 300 · 264 left · 12%
events stored per client102 of 2500 · 2398 left · 4.1%

What each count is

assets
the assets each stored campaign really carries
campaigns
one per campaign stored in that client's own index.json
clients
one per data/clients/<slug>/ directory on disk
events
the rows in that client's own events.json

counted from the engine's own files on every request — no hand-maintained counter exists

Where the gate bites

Three requests carry a tier limit

Each one is refused with a flat, structured 402 — the same shape as the attribution ingest's refusals — and the refusal happens before any work, so nothing is generated, stored or truncated to fit.
ActionRouteRefusal codeHTTP
create_clientPOST /api/clientsclient_limit_reached402
create_campaignPOST /api/campaignscampaign_limit_reached | asset_limit_reached402
store_eventsPOST /api/clients/{slug}/eventsevent_limit_reached402

The limits, and the key that owns each one

assets_per_client
billing.tiers.<tier>.limits.assets_per_client
campaigns_per_client
billing.tiers.<tier>.limits.campaigns_per_client
clients
billing.tiers.<tier>.limits.clients
events_per_client
billing.tiers.<tier>.limits.events_per_client

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.
Produced by the engine, not written for the page

The gate was pushed past its limit, and these are its own answers

The export runs the engine against a throwaway keys file with deliberately tight limits — a throwaway keys file with deliberately tight limits (this export only) — and a throwaway namespace (data/clients/billing-demo-probe (throwaway, removed with the export's temp dir)). The four payloads below are what LimitReached.as_dict() returned, at HTTP 402, copied into this page by the exporter.
Before any work
0 / 0
campaigns / assets counted
After one real campaign
1 / 18
the 2-week campaign it really wrote
After the refusals
1 / 18
unchanged: a refused request does no work
Refusal payloads kept
4
each one the gate's own body

A campaign over the campaign limit

HTTP 402
campaign_limit_reachedtier demolimit 1 campaigns per clientused 1 → projected 2

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.

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.

config_key
billing.tiers.demo.limits.campaigns_per_client
charges_processed
false
{
  "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 402
asset_limit_reachedtier demo-assetslimit 20 assets per clientused 18 → projected 54

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.

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.

config_key
billing.tiers.demo-assets.limits.assets_per_client
charges_processed
false
{
  "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 402
event_limit_reachedtier demolimit 2 events stored per clientused 0 → projected 3

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.

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.

config_key
billing.tiers.demo.limits.events_per_client
charges_processed
false
{
  "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 402
client_limit_reachedtier demolimit 2 client namespacesused 3 → projected 4

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.

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.

config_key
billing.tiers.demo.limits.clients
charges_processed
false
{
  "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
}
A buyer's edit, no code change

The same request, allowed after one line of config

billing.assignment placed the client on the upgraded tier in the buyer's own keys file. Nothing was recompiled, restarted or redeployed, and this is the engine's own verdict from that moment.

The request that was refused a moment earlier

now allowed
verdict code
allowed
tier now
demo-upgraded
campaigns on the tier
1 used of 5 allowed
assets on the tier
18 used of 200 allowed, 18 projected

allowed — 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

assets_per_client
200
campaigns_per_client
5
clients
5
events_per_client
50

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.