Billing & keys REST API
The billing API is plain JSON over HTTPS.
| Base URL | https://api.moqom.cloud |
| Auth | Authorization: Bearer mqk_<16 hex>_<secret> |
| Content type | application/json (requests and responses) |
| Caching | Every response is Cache-Control: no-store |
| Request bodies | At most 1 MiB. Unknown fields are rejected with 400. |
| Money | Integers in micro-dollars plus a display string (see below) |
Conventions
Section titled “Conventions”Every amount is an object:
{ "micros": 50000000, "display": "$50.000000" }micros is millionths of a US dollar (1 USD = 1,000,000 micros), so sub-cent usage charges
are exact. Request bodies take cents (amount_cents, below_cents).
Errors
Section titled “Errors”Errors are a JSON object with a single message:
{ "error": "insufficient credit: top up to admit new sessions" }| Status | When |
|---|---|
400 |
Invalid request: malformed JSON, unknown field, out-of-range value |
401 |
Missing, malformed, unknown or revoked key — or a key of the wrong kind |
402 |
Payment required (tenant never paid) or insufficient credit |
403 |
Tenant suspended, archived or deleted |
404 |
Not found |
409 |
Conflict: already exists, or a status change that is not allowed |
429 |
Rate limited (signup) |
502 |
Upstream payment provider (Stripe) error |
503 |
Payments are not configured on this deployment |
500 |
Internal error (message is always "internal error") |
See the full error codes table.
Key kinds
Section titled “Key kinds”Routes marked backend key require a key of kind backend; a relay key gets 401 on
them. POST /v1/usage takes a relay key — see the Usage reporting API.
Route summary
Section titled “Route summary”| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/v1/rates |
none | Public rate card |
POST |
/v1/signup |
none | New tenant, first API key, first checkout |
GET |
/v1/billing |
backend key | Balance, status, recent ledger |
POST |
/v1/billing/checkout |
backend key | Open a top-up checkout |
POST |
/v1/billing/portal |
backend key | Open the Stripe customer portal |
PUT |
/v1/billing/auto-top-up |
backend key | Configure automatic top-ups |
GET |
/v1/keys |
backend key | List keys |
POST |
/v1/keys |
backend key | Issue a key (secret shown once) |
DELETE |
/v1/keys/{id} |
backend key | Revoke a key |
POST |
/v1/usage |
relay key | Report usage (reference) |
POST |
/v1/stripe/webhook |
Stripe signature | Reserved for Stripe — not for customer use |
GET /v1/rates
Section titled “GET /v1/rates”The rate card in force. No authentication.
curl -sS https://api.moqom.cloud/v1/rates{ "currency": "usd", "model": "prepaid, cost-plus, capped", "egress_cost_per_gib": { "micros": 120000, "display": "$0.120000" }, "markup_basis_points": 1500, "platform_fee_per_gib": { "micros": 10000, "display": "$0.010000" }, "ceiling_per_1000_participant_minutes": { "host": { "audio": { "micros": 980000, "display": "$0.980000" }, "hd": { "micros": 3980000, "display": "$3.980000" }, "full_hd": { "micros": 8980000, "display": "$8.980000" }, "2k": { "micros": 15980000, "display": "$15.980000" }, "2k_plus": { "micros": 35980000, "display": "$35.980000" } }, "audience": { "audio": { "micros": 980000, "display": "$0.980000" }, "hd": { "micros": 3980000, "display": "$3.980000" }, "full_hd": { "micros": 8980000, "display": "$8.980000" }, "2k": { "micros": 15980000, "display": "$15.980000" }, "2k_plus": { "micros": 35980000, "display": "$35.980000" } }, "broadcast_audience": { "audio": { "micros": 580000, "display": "$0.580000" }, "hd": { "micros": 1980000, "display": "$1.980000" }, "full_hd": { "micros": 4580000, "display": "$4.580000" }, "2k": { "micros": 7980000, "display": "$7.980000" }, "2k_plus": { "micros": 17980000, "display": "$17.980000" } } }}| Field | Meaning |
|---|---|
egress_cost_per_gib |
MOQOM’s bandwidth cost basis per GiB of relay egress |
markup_basis_points |
Markup on cost; 1500 = 15% |
platform_fee_per_gib |
Fee per GiB of egress, charged whoever hosts the relay |
ceiling_per_1000_participant_minutes |
The cap, by role and tier |
The values above are illustrative; the live response is authoritative. See Pricing model.
POST /v1/signup
Section titled “POST /v1/signup”Creates a tenant, its first backend API key (named default), and a checkout for the first
top-up. The tenant is pending until that payment succeeds. No authentication; limited to 5
signups per hour per client address (429 beyond that).
| Field | Type | Rules |
|---|---|---|
email |
string | A valid address, at most 254 characters |
name |
string | 1–200 characters |
curl -sS https://api.moqom.cloud/v1/signup \ -H 'Content-Type: application/json' \ -d '{"email":"dev@example.com","name":"Example Inc"}'201 Created
{ "tenant": "t_3f9a0c1d2e4b5a69", "status": "pending", "api_key": "mqk_9c1e2f3a4b5d6e7f_x2VtQ…", "key_id": "9c1e2f3a4b5d6e7f", "checkout_url": "https://checkout.stripe.com/c/pay/cs_…", "note": "The API key is shown once. Store it now; it cannot be recovered, only revoked and replaced."}checkout_url is for the default first top-up. If it could not be opened, it is empty and you
can open one later with POST /v1/billing/checkout using the new key.
GET /v1/billing
Section titled “GET /v1/billing”Balance, status and recent ledger for the key’s tenant. Backend key.
curl -sS https://api.moqom.cloud/v1/billing \ -H "Authorization: Bearer $MOQOM_API_KEY"200 OK
{ "tenant": { "id": "t_3f9a0c1d2e4b5a69", "name": "Example Inc", "email": "dev@example.com", "status": "active", "has_card": true, "auto_top_up": { "enabled": false, "below": { "micros": 0, "display": "$0.000000" }, "amount": { "micros": 0, "display": "$0.000000" } }, "created_at": "2026-10-01T17:03:11Z" }, "balance": { "micros": 41237500, "display": "$41.237500" }, "admitted": true, "recent": [ { "kind": "usage", "amount": { "micros": -3980, "display": "-$0.003980" }, "capped": true, "reference": "live/42", "at": "2026-10-06T09:16:02Z" } ]}| Field | Meaning |
|---|---|
tenant.status |
pending, active, suspended, archived, deleted |
tenant.has_card |
A saved payment method exists (needed for auto top-up) |
tenant.last_top_up_error |
Present when the last automatic top-up failed |
balance |
Prepaid credit remaining |
admitted |
Whether new sessions and tokens are admitted now |
recent[] |
Up to 50 recent ledger entries: kind, amount, capped, reference, detail, at |
POST /v1/billing/checkout
Section titled “POST /v1/billing/checkout”Opens a hosted checkout page for buying credit. Backend key.
| Field | Type | Rules |
|---|---|---|
amount_cents |
integer | Whole cents, from $10.00 (1000) to $10,000.00 (1000000) |
curl -sS https://api.moqom.cloud/v1/billing/checkout \ -H "Authorization: Bearer $MOQOM_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"amount_cents": 5000}'200 OK
{ "checkout_url": "https://checkout.stripe.com/c/pay/cs_…" }After payment the customer returns to moqom.cloud; credit is added when the payment succeeds.
Errors: 400 out of range, 403 tenant archived/deleted, 502 Stripe error, 503 payments not
configured.
POST /v1/billing/portal
Section titled “POST /v1/billing/portal”Opens the Stripe customer portal (receipts, saved cards). Backend key. No body.
curl -sS -X POST https://api.moqom.cloud/v1/billing/portal \ -H "Authorization: Bearer $MOQOM_API_KEY"200 OK
{ "portal_url": "https://billing.stripe.com/p/session/…" }400 if the tenant has never paid (there is nothing to manage yet).
PUT /v1/billing/auto-top-up
Section titled “PUT /v1/billing/auto-top-up”Configures automatic top-ups: when a usage charge takes the balance below below_cents, MOQOM
charges the saved card amount_cents. Backend key.
| Field | Type | Rules |
|---|---|---|
enabled |
boolean | |
below_cents |
integer | Threshold, not negative |
amount_cents |
integer | When enabled: $10.00–$10,000.00 |
curl -sS -X PUT https://api.moqom.cloud/v1/billing/auto-top-up \ -H "Authorization: Bearer $MOQOM_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"enabled": true, "below_cents": 1000, "amount_cents": 5000}'200 OK — the updated tenant:
{ "id": "t_3f9a0c1d2e4b5a69", "name": "Example Inc", "email": "dev@example.com", "status": "active", "has_card": true, "auto_top_up": { "enabled": true, "below": { "micros": 10000000, "display": "$10.000000" }, "amount": { "micros": 50000000, "display": "$50.000000" } }, "created_at": "2026-10-01T17:03:11Z"}Auto top-up needs a saved card, which is stored when you pay a checkout. See Top-ups & auto top-up.
GET /v1/keys
Section titled “GET /v1/keys”Lists the tenant’s keys, including revoked ones. Secrets are never returned. Backend key.
curl -sS https://api.moqom.cloud/v1/keys -H "Authorization: Bearer $MOQOM_API_KEY"200 OK
{ "keys": [ { "id": "9c1e2f3a4b5d6e7f", "name": "default", "kind": "backend", "created_at": "2026-10-01T17:03:11Z" }, { "id": "5b7e0d2a9c3f1e48", "name": "relay-us-east-1", "kind": "relay", "created_at": "2026-10-06T09:12:44Z" }, { "id": "0a1b2c3d4e5f6a7b", "name": "old-ci", "kind": "backend", "created_at": "2026-09-02T10:00:00Z", "revoked_at": "2026-10-02T08:30:00Z" } ]}POST /v1/keys
Section titled “POST /v1/keys”Issues a new key. The plaintext api_key appears only in this response. Backend key.
| Field | Type | Rules |
|---|---|---|
name |
string | 1–100 characters |
kind |
string | backend (default) or relay |
curl -sS https://api.moqom.cloud/v1/keys \ -H "Authorization: Bearer $MOQOM_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"name":"prod-backend","kind":"backend"}'201 Created
{ "key": { "id": "7f6e5d4c3b2a1908", "name": "prod-backend", "kind": "backend", "created_at": "2026-10-06T09:20:00Z" }, "api_key": "mqk_7f6e5d4c3b2a1908_Q1wE…"}DELETE /v1/keys/{id}
Section titled “DELETE /v1/keys/{id}”Revokes a key immediately. Backend key.
curl -sS -X DELETE https://api.moqom.cloud/v1/keys/0a1b2c3d4e5f6a7b \ -H "Authorization: Bearer $MOQOM_API_KEY"204 No Content
You cannot revoke the key the request is authenticated with (400): issue a replacement first,
then revoke the old key using the new one. 404 if no such key exists in your tenant.
POST /v1/keys/{id}/rotate
Section titled “POST /v1/keys/{id}/rotate”Rotates a key with no gap in service. Backend key. Issues a replacement with the same name and
kind, and keeps the old key working for overlap_seconds (default 86400, at most 604800) while
you roll the new one out. Live sessions are never affected: client tokens don’t depend on the API
key that minted them.
curl -sS -X POST https://api.moqom.cloud/v1/keys/0a1b2c3d4e5f6a7b/rotate \ -H "Authorization: Bearer $MOQOM_API_KEY" \ -d '{"overlap_seconds": 3600}'201 Created
{ "key": { "id": "9f8e7d6c5b4a3921", "name": "production", "kind": "backend", "created_at": "2026-10-06T13:00:00Z" }, "api_key": "mqk_9f8e7d6c5b4a3921_…", "old_key_retires": "2026-10-06T14:00:00Z"}The new key is shown once. Until old_key_retires, GET /v1/keys lists the old key with
retires_at; afterwards with revoked_at. Revoking a retiring key early is allowed. Rotating
the key the request is authenticated with is allowed too.
POST /v1/stripe/webhook
Section titled “POST /v1/stripe/webhook”MOQOM’s receiver for Stripe events, verified by Stripe’s signature. It is listed here only so you recognise it; it is not part of the customer API.