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.
For sales by period, by app and by product, use the revenue call (owner or admin). It reads the same settlement ledger, across every brand of the organization:
GET /api/org/{slug}/marketplace/revenue{ "gatewayOk": true, "koreanPackage": false, "payments": 4, "thisMonth": { "grossUsd": 285.02, "netUsd": 268.77 }, "thisYear": { "grossUsd": 285.02, "netUsd": 268.77 }, "total": { "grossUsd": 285.02, "netUsd": 268.77 }, "byDay": [ { "period": "2026-09-30", "grossUsd": 285.02, "netUsd": 268.77 } ], "byMonth": [ { "period": "2026-09", "grossUsd": 285.02, "netUsd": 268.77 } ], "byYear": [ { "period": "2026", "grossUsd": 285.02, "netUsd": 268.77 } ], "byApp": [ { "key": "appgroup_…", "name": "Shiftboard", "thisMonth": 285.02, "thisYear": 285.02, "total": 285.02, "netTotal": 268.77, "payments": 4 } ], "byProduct": [ { "appKey": "appgroup_…", "appName": "Shiftboard", "productName": "Setup pack", "thisMonth": 200.00, "thisYear": 200.00, "total": 200.00, "netTotal": 188.60, "payments": 1 } ]}grossUsd is what the buyer paid. netUsd is that amount after the take rate and the marketplace commission; the card processing fee is deducted by your own Stripe account and is not in these numbers. Dates are the payment dates, in UTC. byDay covers the last 30 days and byMonth the last 12 months; byYear starts at the first payment. A marketplace payment is grouped under the app group it was made for; a sale outside the marketplace, such as a retail voucher, is grouped under its brand (key = brand: + the brand slug). productName is the product as it was at payment time, so a plan change leaves earlier payments under the old name.
When payments cannot be read, or the answer is incomplete, the call returns gatewayOk: false with zeros. It never fills in an estimate. Sales are settled in US dollars; koreanPackage only tells the console whether to offer a currency 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": [ { "key": "appgroup_…", "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.
appGroups[].key is the App Group public key (appgroup_…) — the one identifier to join on. It is what the customer copies from Applications > Settings, what POST /brands/{slug}/bind-app-group stores, and what GET /catalog/by-app-group/{key} is keyed by. Do not keep a second spelling of this binding alongside it.
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 payment is charged on the seller’s own Stripe account (a direct 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.
Marketplace commission — ✅ built
Section titled “Marketplace commission — ✅ built”When another organization buys your app on marketplace.axowl.com, Axowl adds a marketplace commission of 5%. It is separate from the take rate above and is added on top of it. It applies to the amount actually paid after vouchers and discounts, on every payment. For a monthly plan, that means every month. The value comes from PricingConstants.Marketplace.CommissionPercent, the same constant the pricing page shows.
Like the take rate, it is withheld at the source as part of the Stripe Connect application fee. In the ledger it is a separate line from the take rate, so the two can always be told apart. Marketplace Subscription plans are real Stripe subscriptions that charge monthly. A voucher discounts the first invoice only. A refund reverses the seller’s lines of that payment in proportion to the amount refunded; the take rate and the commission already charged are not returned. How buying and installing work is in Applications → Selling on the marketplace.
The seller is the merchant — ✅ built
Section titled “The seller is the merchant — ✅ built”Since 2026-09-29 every card payment that belongs to a seller is charged on the seller’s own Stripe account: a marketplace app, a catalog product (one-time or monthly), a voucher purchase, and a consumer topping up a brand’s credit.
| Who | |
|---|---|
| Merchant for the payment (the name on the buyer’s card statement) | The seller |
| Refunds | Paid from the seller’s Stripe balance. The seller issues them in its own Stripe dashboard |
| Card disputes (chargebacks) and the dispute fee | Taken from the seller’s Stripe balance. The seller answers the dispute in its own Stripe dashboard |
| Card processing fee | Charged by Stripe on the seller’s account, at Stripe’s rate for the seller’s country. Axowl does not collect it |
| What reaches Axowl | The take rate, plus the 5% marketplace commission on marketplace sales |
| A balance the seller cannot repay | Stripe’s loss. It is not taken from Axowl |
What this means when you sell:
- Set up card payments once. The payout account step in Applications → Payments creates a Stripe account that can take card payments. An account created before 2026-09-29 could only receive transfers; the same step replaces it. Until Stripe has verified the new account, checkout answers
409 brand_not_payablewith areasonofnot_started,upgrade_requiredornot_verified. - When a buyer disputes a payment on a monthly plan, the subscription on that card is ended so the card is not charged again, and you are told by email.
- Taxes on what you sell are yours to handle, as the merchant.
Axowl’s own charges — plans, add-ons and credit top-ups — are not part of this: for those, Axowl is the merchant.
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.