{
    "product": "Cenvero Stratum",
    "generated_at": "2026-08-03T07:17:16+00:00",
    "format": "cenvero-docs-v1",
    "document_count": 1,
    "documents": [
        {
            "slug": "billing-integration",
            "title": "Billing Integration",
            "category": null,
            "url": "https://stratum.cenvero.com/docs/billing-integration",
            "headings": [
                {
                    "level": 1,
                    "text": "Billing Integration"
                },
                {
                    "level": 2,
                    "text": "How enforcement actually works"
                },
                {
                    "level": 2,
                    "text": "Get an API key"
                },
                {
                    "level": 2,
                    "text": "Endpoints"
                },
                {
                    "level": 3,
                    "text": "A worked flow"
                },
                {
                    "level": 2,
                    "text": "From the CLI"
                },
                {
                    "level": 2,
                    "text": "What this is not"
                },
                {
                    "level": 2,
                    "text": "See also"
                }
            ],
            "word_count": 877,
            "markdown": "# Billing Integration\n\nEndpoints **in the agent** so your billing system can suspend, resume, or rate-limit one of your customers — a **tenant** — when their invoice changes. It is part of the agent's normal API (REST plus the `cenvero-str-ctl` CLI); you don't build anything beyond the HTTP calls.\n\nThe intended shape is: your billing system already knows who has paid. When that answer changes, it makes one call per affected tenant. Nothing polls, and the agent never talks to your billing system.\n\n## How enforcement actually works\n\nWorth understanding before you wire it up, because it determines what \"suspended\" means for a customer and what you can promise them.\n\n**Suspending is a firewall action, not a switch.** When you suspend a tenant, the agent looks up every endpoint belonging to that tenant's networks and installs a **drop rule for each endpoint's address**. Traffic from those addresses stops being forwarded. Resuming removes exactly those rules and nothing else — they are tagged with the tenant's id, so a resume can never disturb a rule you wrote yourself.\n\nTwo properties follow, and both matter in practice:\n\n- **It is idempotent.** Suspending an already-suspended tenant is harmless — an endpoint that already has its drop rule is skipped. Your billing system can retry a failed call, or re-send the current state on a schedule, without special-casing anything.\n- **Existing connections are cut, not drained.** The rule applies to traffic, so a transfer in progress stops. There is no grace period. If you want one, delay the call in your billing system.\n\n**Rate-limiting is the bandwidth cap**, the same mechanism described in [Tenants & Bandwidth](/docs/tenants). It is a credit allowance that refills at the rate you set: TCP finds the new rate and settles there, UDP over the rate is dropped. Applying a limit to a running tenant takes effect immediately without disturbing established connections.\n\n**State is durable and cluster-wide.** The tenant's status and cap are persisted, so they survive an agent restart, and in a cluster the change is replicated to the other members. You call one node, not every node.\n\n**Suspension holds for endpoints created later.** Suspending a tenant that owns nothing yet is fine: there is no address to drop traffic from at that moment, but the suspension is remembered, and **any endpoint attached to that tenant afterwards is covered as it is attached.** So the order you do things in does not matter — you can suspend an empty tenant, provision it later, and its traffic still does not flow. You do not need to re-send the call.\n\n## Get an API key\n\nThe operator mints a key on the node; your billing system sends it as a bearer token.\n\n```bash\ncenvero-str-ctl apikeys mint \"billing\"     # → csk_xxxxxxxxxxxxxxxx  (shown once)\ncenvero-str-ctl apikeys list\ncenvero-str-ctl apikeys revoke <id>\n```\n\n```\nAuthorization: Bearer csk_xxxxxxxxxxxxxxxx\n```\n\nThe key is shown **once**, when it is minted — store it then. Mint a separate key for your billing system rather than reusing an operator key, so revoking it cannot lock you out of anything else.\n\n## Endpoints\n\nA tenant is addressed by its id. All return JSON.\n\n| Method · Path | What happens |\n|---|---|\n| `POST /api/v1/billing/tenants/{id}/suspend` | Installs a drop rule per tenant endpoint address. Traffic from the tenant stops. |\n| `POST /api/v1/billing/tenants/{id}/resume` | Removes exactly the rules the suspend installed. |\n| `POST /api/v1/billing/tenants/{id}/limit` | Caps the tenant's bandwidth. Body: `{\"rate_mbps\": 50}`. |\n| `POST /api/v1/billing/tenants/{id}/unlimit` | Removes the cap (unlimited). |\n| `GET  /api/v1/billing/tenants/{id}` | Current state: `{id, name, status, max_bandwidth_bps}`. |\n\n```bash\ncurl -X POST https://node:7070/api/v1/billing/tenants/t-abc123/suspend \\\n     -H \"Authorization: Bearer csk_xxxxxxxxxxxxxxxx\"\n```\n\nUnknown tenant → `404`.\n\n### A worked flow\n\nWhat a billing system typically does, in order:\n\n```bash\n# Invoice went unpaid — cut the customer off\nPOST /api/v1/billing/tenants/t-abc123/suspend\n\n# Confirm what the node now believes\nGET  /api/v1/billing/tenants/t-abc123\n# → {\"id\":\"t-abc123\",\"name\":\"acme\",\"status\":\"suspended\",\"max_bandwidth_bps\":0}\n\n# Payment arrived — restore them\nPOST /api/v1/billing/tenants/t-abc123/resume\n\n# Downgraded to a slower plan instead of being cut off\nPOST /api/v1/billing/tenants/t-abc123/limit    {\"rate_mbps\": 50}\n```\n\n**Read back after writing.** `GET` is the node's own view, and it is the answer to \"did that actually apply?\" — more reliable than inferring it from a `200`.\n\n## From the CLI\n\nThe same actions, for an operator or a script on the node:\n\n```bash\ncenvero-str-ctl billing suspend  <tenant-id>\ncenvero-str-ctl billing resume   <tenant-id>\ncenvero-str-ctl billing limit    <tenant-id> --rate-mbps 50\ncenvero-str-ctl billing unlimit  <tenant-id>\ncenvero-str-ctl billing status   <tenant-id>\n```\n\n## What this is not\n\n- **Not invoicing.** The agent has no concept of an invoice, a price, or a due date. It enforces what you tell it, and your billing system remains the source of truth.\n- **Not the customer panel's billing.** Orders, invoices and payment in the management panel are a separate thing entirely — see [Your account](/docs/your-account). These endpoints are for *your* system controlling *your* customers on *your* nodes.\n- **Not a usage meter.** For how much a tenant actually transferred, use the accounting and metrics surfaces — see [Monitoring & Observability](/docs/monitoring).\n\n## See also\n\n- [Tenants & Bandwidth](/docs/tenants) — what a tenant is, and how the bandwidth cap behaves.\n- [Monitoring & Observability](/docs/monitoring) — usage figures to bill against.\n- [API Reference](/docs/api) — the full endpoint surface and authentication.\n"
        }
    ]
}