Billing & Monetization
What this is
Section titled “What this is”Most platforms let you pay them. Axowl billing points the other way: your organization charges its own end users, through the same identity core that authenticates them.
Three structural properties set it apart:
- Per-app-group collection. Each app group (brand) runs its own plans and charges its own users under its own name. One organization, many collecting brands.
- Consolidated statements. Every brand rolls up into one organization-level statement — the whole company in one book.
- Sealed invoices. A period statement is sealed the moment it closes. Every line traces back to the ledger entry it billed, and anyone can verify the seal independently — see Invoices and Seals & integrity.
Money settles to your own Stripe account. Axowl takes a rate — it never holds your funds.
The schedule
Section titled “The schedule”One rate on billed volume. No per-invoice fee, no monthly minimum. Stripe processing passes through at cost.
| Plan | Take rate | App groups that collect | Volume cap | Consolidated + verify API |
|---|---|---|---|---|
| Free | 0.7% | 1 | $5,000 / month | — |
| Pro | 0.7% | 1 | No cap | — |
| Pro+ | 0.6% | Unlimited | No cap | — |
| Business | 0.5% | Unlimited | No cap | ✓ |
| Enterprise | from 0.4% | Unlimited | No cap | ✓ |
Numbers in this table are rendered from PricingConstants.Monetization on the live pricing page — if the two ever disagree, the constants win.
Design decisions worth knowing:
- Free is metered by volume, not by features. The full engine — plans, seats, usage, sealed invoices — works on Free. Past the cap, new charges pause until you upgrade; existing subscriptions keep collecting.
- Sealing is not gated. Every invoice on every plan is sealed — that is how the product works. Business gates the organization roll-up, the public verify API, and audit export.
- Downgrades never cut a live subscription. Dropping below a plan blocks new collection that the lower plan wouldn’t allow; charges already running are grace-period protected.
The collection switch — ✅ built
Section titled “The collection switch — ✅ built”Each app group has a collection switch: Dashboard → Applications → your app group → Payments → “Billing collection”. Turning it on is plan-gated — how many app groups may collect at once comes from the schedule above (Free/Pro: 1, Pro+ and up: unlimited). Past the limit the server answers 403 { "error": "UPGRADE_REQUIRED", "feature": "billing.collect" }. Turning it off is always allowed, and a downgrade never flips the switch on a group that is already collecting — only new enables are blocked.
Every flip is a sealed audit event: appgroup.monetization.enabled / appgroup.monetization.disabled.
Turning collection on or off is billing, so it follows the owner carve-out: the organization owner, or a member explicitly granted view.billing, may flip it. Editing the app group itself is not enough — an admin without that grant gets 403 PERMISSION_DENIED.
Reading what you collected — ✅ built
Section titled “Reading what you collected — ✅ built”Collection lands in your organization’s cash account on the settlement ledger, one line per sale. Because those lines are keyed by the brand that sold, the org-level total needs its own call:
GET /api/org/{slug}/billing/earnings{ "configured": true, "cashBalance": 1985.40, "brands": [ { "brand": "acme-cloud", "cashBalance": 1985.40, "lines": 42 } ]}This is the opposite direction from /billing/credit, which is what your organization pays Axowl. Cash and credit are separate books by design — this endpoint only ever reports settled money. Owner-only, same gate as the switch.
The policy API — ✅ built
Section titled “The policy API — ✅ built”Payment engines read the org’s billing policy before charging on its behalf — Axowl publishes the policy; the engine executes it.
GET /api/public/v1/monetizationX-Api-Key: ah_live_…{ "takeRatePercent": 0.7, "currency": "USD", "perInvoiceFixedFeeUsd": 0, "monthlyVolumeCapUsd": 5000, "collectingAppGroups": { "limit": 1, "enabled": 1 }, "consolidatedEnabled": false, "appGroups": [ { "id": "…", "name": "…", "monetizationEnabled": true } ]}monthlyVolumeCapUsd is null on paid plans; collectingAppGroups.limit of -1 means unlimited. Unknown or missing plans resolve fail-closed to the Free rules.
A payment engine that holds a brand→org binding rather than the org’s own key asks by org id instead — same response shape:
POST /api/public/v1/orgs/monetizationX-Api-Key: ah_live_…Content-Type: application/json
{ "orgId": "…" }Verifying a sealed invoice — ✅ built
Section titled “Verifying a sealed invoice — ✅ built”Anyone holding an invoice id can verify the sealed statement — no API key, no Axowl account:
GET /api/public/v1/invoices/{id}/verifyThe endpoint recomputes the statement hash from the stored lines and compares it to the value sealed at issue time. It returns the verdict and both hashes — never amounts (it is an anonymous surface). Statements still open for the current period report "status": "open" and seal when the period closes. Available when the issuing org is on the Business plan or higher; below that the endpoint answers 403 UPGRADE_REQUIRED.
How the rate is collected — ✅ built (engine side)
Section titled “How the rate is collected — ✅ built (engine side)”Settlement runs on Stripe Connect. When a consumer pays for a catalog product, the charge routes to the brand’s connected account as a destination charge, and the take rate is withheld at the source as a Stripe Connect application fee — never billed separately, and never debited from your Axowl credit ledger (cash and credit are separate books by design). The engine reads this page’s policy API at charge time; if the policy cannot be read, the charge is refused rather than guessed. Past the Free volume cap, new charges are refused with upgrade_required — running subscriptions are unaffected.
Related
Section titled “Related”- Invoices — the sealed period statement itself.
- Seals & integrity — what a seal proves and how to verify one.
- Public API — where the pricing and verify endpoints live as they ship.