Real envelopes for the generated { data: {} } shells
The numbered ops in this section fill in the response envelope the page’s
autogen blocks render as {}. Each opener names its route so the generator
keeps this body across rebuilds (see comment block above the lifecycle).
1. List subaccounts
GET /api/v1/subaccounts2. Get one subaccount
GET /api/v1/subaccounts/{id}3. List the child’s API keys
GET /api/v1/subaccounts/{id}/api-keys4. Read branding
GET /api/v1/subaccounts/{id}/branding5. List channel budgets
GET /api/v1/subaccounts/{id}/channel-budgetsGET lists what the POST/PUT writes: one
budget per channel.
6. List custom domains
GET /api/v1/subaccounts/{id}/domains7. Read the canonical CNAME target
GET /api/v1/subaccounts/{id}/domains/cname-target8. Download a branded invoice run
GET /api/v1/subaccounts/{id}/invoice.htmlContent-Type: text/html and Content-Disposition: attachment. Build the
invoice shell from the same usage statement the JSON reads return — the
wires below mirror the statement rows the HTML embeds.
9. Read notification sender email
GET /api/v1/subaccounts/{id}/notification-email10. Export usage as CSV
GET /api/v1/subaccounts/{id}/usage/export.csvTOTAL footer. Headers:
date,channel,direction,quantity,base_cost,marked_up_price,margin_earned,currency.
?period=YYYY-MM defaults to the current UTC month. The mirrored JSON
below is the same figures as rows on the statement the CSV is derived
from.
11. Read live usage counters
GET /api/v1/subaccounts/{id}/usage/live12. Read the usage statement JSON
GET /api/v1/subaccounts/{id}/usage/statement13. Meter the calling org’s BYO fee
GET /api/v1/subaccounts/byo-fee-meter14. List the rate-card library
GET /api/v1/subaccounts/rate-cards15. Consolidated usage rollup across the tree
GET /api/v1/subaccounts/usage-rollup16. Export the rollup as CSV
GET /api/v1/subaccounts/usage-rollup/export.csvTOTAL footer. The mirrored JSON below is the aggregated figure
the CSV’s footer reads.
17. Create subaccount (full envelope)
POST /api/v1/subaccounts18. Provision a subaccount in one call
POST /api/v1/subaccounts/provision19. Update the subaccount row
PUT /api/v1/subaccounts/{id}20. Replace the child’s assigned rate card
PUT /api/v1/subaccounts/{id}/assigned-rate-card21. Update branding
PUT /api/v1/subaccounts/{id}/branding22. Update channel budgets
PUT /api/v1/subaccounts/{id}/channel-budgets23. Update notification sender email
PUT /api/v1/subaccounts/{id}/notification-email24. Create an API key for the child
POST /api/v1/subaccounts/{id}/api-keystype: "secret" is a server-to-server key
(dv_live_sk_*); type: "public" is a browser-safe key.
25. Create the channel-budget policy
POST /api/v1/subaccounts/{id}/channel-budgetsblock the next send, or alert only).
26. Attach a custom domain
POST /api/v1/subaccounts/{id}/domainscname_target, then re-check with
POST /{id}/domains/{domainId}/verify until status flips to
verified.
The lifecycle at a glance
A subaccount is a child organization under your parent org. It startspending the moment you create it, and it only becomes billing-active when
you finalize. Create it, optionally attach branding and a custom domain,
fund it, verify ownership of the domain, then flip it to active — and read
back usage, pricing, and status. Every role gate on this page is owner or
admin on the parent organization, except suspend/reactivate (owner
only). Worked responses below use empty-path-safe ids, not shell echoes.
Worked sequence: create → fund → finalize
1. Create the subaccount
POST /api/v1/subaccounts takes a minimal body — a name is enough; plan,
limits, and reseller pricing can follow as separate calls. The response
carries the new org id and the one-time plaintext default API key.
api_key before you discard the response — it is shown once.
Until finalization the subaccount is pending: reads work, but billing
metering does not start.
2. Fund the wallet
Push initial credits from the parent wallet; pass aclient_reference_id
so a retried top-up replays instead of double-charging.
3. Attach a custom domain (optional)
White-label hosts claim a domain and re-check its CNAME before finalizing.cname_target, then re-check. status flips to
verified when the CNAME resolves; the SSL certificate provisions
asynchronously after that.
4. Finalize
Flip the subaccount toactive. Owner or admin on the parent. The call is
idempotent — a rerun on an already-active row is a 200 no-op, so a
retried “Launch” click is safe.
POST /api/v1/subaccounts/provision runs create → pricing →
branding → funding → finalize as one atomic call. Reach for it when
onboarding is driven by your own portal instead of a human wizard.
Read back: usage, pricing, and status
Once active, the reads your reseller portal makes most:GET /{id} returns the org row (subscription_status, plan, parent
hierarchy) — the status check the finalize step above flips. GET /{id}/usage/statement?period=YYYY-MM returns the billing-grade per-day ×
meter rows the rollup endpoint folds; GET /{id}/usage/export.csv streams
the same figures as a CSV attachment to invoice your end-customer.
Rating: rate deck, rate cards, and MCCMNC overrides
Subaccount pricing is a flatreseller_margin_pct over wholesale, plus
optional per-channel/destination markup cells (the rate deck) or a
named rate card from the parent org’s price-book library. For specific
mobile operators, layer per-MCCMNC overrides — the send-path resolver
prices an SMS segment against them before falling through to the wholesale
deck.
Read the assigned deck — an empty rate_deck means every usage line prices
at the flat margin:
lane_cells and/or deck_cells); cards without cells never
persist:
POST returns the written row; GET /{id}/mccmnc-overrides lists them cursor-paginated (meta.cursor,
meta.has_more, meta.total):
Subaccount ops: read back, update, and inheritance semantics
GET /api/v1/subaccounts/{id} returns the child org row with its parent
hierarchy attached; PUT /api/v1/subaccounts/{id} is the PATCH-style
read-modify write for the display name, plan, and white-label policy.
Both are owner or admin on the parent organization.
The parent block names the organization the child inherited from —
inherits_from_parent: true whenever any white-label field was pulled
rather than set directly. When a parent supplies a value, the org id it
was pulled from appears on inherited_from_organization_id and
inherited_fields maps each inherited field to that source org, exactly
the resolution chain the Branding resolver
documents. A child that sets its own overrides returns
inherits_from_parent: false, inherited_from_organization_id: null,
and inherited_fields: null.
null
on a policy field clears the override and re-opens it to inheritance, so
the parent’s value flows down on the next read:
The hierarchy from an empty list
A brand-new parent org starts with zero children. The scaffold reads that state, creates the first subaccount, then reads it back to confirm the hierarchy is one level deep:GET /{id} resolves the parent link — the loop your
portal runs when a fresh reseller account onboards its first end-customer:
Error shapes worth branching on
Every failure on this page returns the standard{ "error": { "code", "message", "status" }, "meta": { "request_id", "timestamp" } }
envelope. Three classes are specific to the reseller hierarchy:
409 CONFLICT on a hierarchy write as terminal — retrying the same
nesting replays the cycle. 402 INSUFFICIENT_BALANCE is recoverable: fund
the parent, replay the transfer with the SAME client_reference_id, and
the idempotent guard returns one debit, not two. 403 FORBIDDEN means
your portal’s key needs elevating to the parent’s owner/admin role;
422 VALIDATION_ERROR means the request never reached the write and is
safe to correct and resend.
Worked reseller provisioning chain
Use the parent organization’s API key for provisioning. The child key created in step 3 is for requests made by that subaccount; it cannot create or administer sibling subaccounts. AddX-Test-Mode: true when using Clerk session authentication against the sandbox. For API-key authentication, use a dv_test_sk_* key to enter sandbox mode; X-Test-Mode is ignored for dv_live_sk_* keys.
The examples use org_child_01J8S7Q2K3M4N5P6Q7R8S9T0UV as the child id. Replace it with the id returned by step 1. Provisioning is intentionally explicit: create the pending child, finalize it, then apply credentials and controls.
1. Create the pending subaccount
Setpending_setup to true so the child stays out of the active billing state until the provisioning chain is complete. The parent key must have owner or admin access.
Request
2. Finalize the subaccount
Finalize after the child’s required setup is ready. This idempotent call changessubscription_status from pending to active.
Request
3. Create a child API key
Use the parent key to mint a child key. The plaintextkey is returned once and is not recoverable from the list endpoint.
Request
4. Apply channel budgets
Use the parent key to manage the child’s controls.budgets is a full-set replacement: send the complete desired array, including period: "monthly" and a breach action.
Request
5. Read lifetime usage
Read counters from the parent key to monitor the child independently of a billing period.6. Read the consolidated usage rollup
Use the parent key to retrieve the whole reseller tree for a billing period.period is YYYY-MM and defaults to the current UTC month.
7. Suspend and reactivate the child
Both lifecycle operations use the parent key and return the resulting state. Suspending stops send activity without deleting the child; reactivation restoresactive status. Both calls are idempotent.
The same parent-key distinction applies to the reseller controls around this chain. Use
POST /api/v1/subaccounts/{id}/mccmnc-overrides for an operator-specific sell rate, PUT /api/v1/subaccounts/{id}/rate-deck for differentiated channel pricing, POST /api/v1/subaccounts/{id}/domains followed by POST /api/v1/subaccounts/{id}/domains/{domainId}/verify for custom domains, and GET /api/v1/subaccounts/{id}/invoice.html for an invoice export. These endpoints return the configured tenant-owned controls; they do not change provider routing.Worked recipes
The recipes below survive regeneration: they are mirrored here in
api-reference/endpoints/_overlay/subaccounts.mdx and re-spliced by the doc generator. Do not hand-edit the generated operation sections; correct samples in this overlay and regenerate.Recipe 1: Create and provision a subaccount
Create the child org, read it back once it is active, then mint the key its integration will use.pending. Finalize it (or let your provisioning flow call POST /api/v1/subaccounts/provision) and the same read now reports active:
Recipe 2: White-label a subaccount
Set the child’s branding, read it back normalized, then verify the custom domain the branded dashboard is served from.GET branding returns the same row with inherited values folded in: anything you did not set resolves from the parent org and flips inherits_from_parent to true for those fields. Colors come back uppercase-normalized.
Attach the custom domain and read the CNAME target it must point at:
CNAME record for hub.rahal.example pointing at domains.orbit.devotel.io, then re-check with POST /api/v1/subaccounts/{id}/domains/{domainId}/verify. When status flips to verified, the SSL certificate provisions within a few minutes.
Recipe 3: The reseller billing loop
Cap what the child can spend, read back the tier pricing you set, and pull the branded invoice.threshold_cents is the monthly spend cap for the channel; action: "block" hard-stops sends at the cap, "alert" keeps sending and notifies you. GET reseller pricing returns the margin tiers applied to the child’s usage:
GET invoice.html does not return JSON: it streams a standalone HTML document, so save it to a file. The response carries Content-Type: text/html; charset=utf-8 and Content-Disposition: attachment; filename="invoice-<period>.html".
invoice.html in a browser: it is the branded statement for the period, with per-day line items and totals, ready to forward to your end-customer.
Suspended subaccounts
Writes against a suspended subaccount return403 with a dedicated code so your portal can distinguish “paused by the reseller” from “missing permission”:
Reseller orientation and the worked chain
27. Reseller orientation: subaccount vs tenant
Your organization is one tenant; a subaccount is a child organization provisioned under it, with its own id (org_sub_*), its own API keys, branding, and spend caps. Billing rolls up to you, the reseller parent: the child’s usage is marked up at your margin and folded into your statement, while the child sees only its own traffic. You administer children with your parent key; the child’s keys act only on the child, so an end-customer integration can never touch your organization or its other subaccounts. A subaccount inherits your defaults (rate card, notification sender, compliance settings) until you override them per child. Treat the child id the create call returns as the handle for everything that follows: key minting, usage, budgets, and suspension all take {id}.
28. Worked chain: create, mint a scoped key, send, read back usage
Run these four round trips in order to take a new end-customer from provisioning to live traffic. Substitute your own ids and keep the child key from round trip 2; it is shown once. Round trip 1 — Create the subaccountPOST /api/v1/subaccountsdata.id is the {id} every later step takes. billing_email receives the child’s invoices; monthly_spend_cap_cents hard-stops the child’s send traffic at the cap.
subscription_status starts pending and flips to active once the child is finalized (step 4 of the lifecycle above). The samples below assume a finalized child.
Round trip 2 — Mint a scoped API key for the child
POST /api/v1/subaccounts/{id}/api-keysplaintextKey and nothing else: it is scoped to this subaccount and to the scopes you listed, and it is returned exactly once.
X-API-Key header; no parent credential and no extra parameter are needed. The response meta names the subaccount the send was billed to.
POST /api/v1/messages/smsmeta.subaccount_id echoes the child the key belongs to; the same id appears on the usage rows in round trip 4, so reconciliation ties sends to the child without extra bookkeeping.
Round trip 4 — Read back per-subaccount usage
Call with your parent key to audit one child’s spend for a period; the child’s own key sees the same rows for itself.
GET /api/v1/subaccounts/{id}/usagemeta.cursor until has_more is false; total counts the rows for the period. For the margin math on the same rows, use GET /api/v1/subaccounts/{id}/usage/statement (sample 12 above).
29. Suspend and reactivate
Suspend halts a child’s send traffic without deleting its data; billing ownership stays with you. Reactivate is the inverse and is a no-op on an already-active child, so retries are safe.POST /api/v1/subaccounts/{id}/suspend403 subaccount_suspended, so a portal can tell “paused by the reseller” apart from a bad key:
POST /api/v1/subaccounts/org_sub_rahal_00x9/reactivate; the response mirrors the suspend shape with "status": "active".
30. SDKs without typed subaccount helpers
No SDK ships typed methods for the subaccount routes yet, so the typed tabs on this page use plainfetch against the same paths. In Python, Go, Ruby, and PHP use the client’s generic request escape hatch instead; it signs the request exactly like the typed helpers and returns the same { data, meta } envelope.