Sub-accounts

A sub-account is a workspace your organization creates for one of your own customers — and pays for. Build your own product on Telenow (your login, your admin, your payment gateways) and create one sub-account per customer: each gets its own agents, numbers, calls, knowledge bases, webhooks and its own API key, fully separated from every other customer's, while every charge comes out of your wallet.

Your organization (the parent)Each sub-account
Wallet & credits✅ Yours — top up here❌ None of its own — every charge is posted to the parent's wallet
PricingYour rates and negotiated platform feeExactly the parent's
Invoice / Billing pageShows the parent's spend and every sub-account'sShows only what that sub-account spent
API keyYour keysIts own key, returned when you create it
LimitsYour own usage limitsSet by the parent: live calls, monthly spend, total spend, suspended
WebhooksReceives its own events and every sub-account'sReceives its own

Creating a sub-account is quiet: it is not a sign-up, so its users get no Telenow emails, OTPs or welcome credit, and nobody from Telenow contacts them.

Who can use these endpoints: the parent's owner or admin — with an owner/admin API key, or signed in. A developer or viewer key gets 403: a sub-account spends your money and carries a credential. A sub-account's own key gets 403 too — a sub-account cannot have sub-accounts.

Endpoints

The same operations exist on both surfaces. Use the Integration API with your API key from your backend; the dashboard routes are what the Workplace → Sub-accounts page uses.

MethodIntegration API (X-API-Key, flat JSON)Dashboard API ({ success, data })Purpose
GET/api/v1/sub-accounts/api/orgs/{orgId}/sub-accountsList, newest first
POST/api/v1/sub-accounts/api/orgs/{orgId}/sub-accountsCreate — returns the sub-account and its API key
GET/api/v1/sub-accounts/{id}/api/orgs/{orgId}/sub-accounts/{id}One sub-account
PATCH/api/v1/sub-accounts/{id}/api/orgs/{orgId}/sub-accounts/{id}Rename, set your reference, change limits
POST/api/v1/sub-accounts/{id}/suspend…/{id}/suspendRefuse new calls; end live ones
POST/api/v1/sub-accounts/{id}/resume…/{id}/resumeLift a suspension
GET/api/v1/sub-accounts/{id}/usage…/{id}/usageCalls, minutes and spend for a window
POST/api/v1/sub-accounts/{id}/keys…/{id}/keysMint a new key (revokes the others by default)
DELETE/api/v1/sub-accounts/{id}…/{id}Close for good

A sub-account id is its organization id. Another organization's id returns 404.

Create a sub-account

curl -X POST https://api.telenow.ai/api/v1/sub-accounts \
  -H "X-API-Key: vai_live_…" -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Dental",
    "externalId": "cust_8412",
    "limits": {
      "maxConcurrentCalls": 5,
      "monthlySpendLimitUsd": 200,
      "totalSpendLimitUsd": null
    }
  }'

Returns 201 Created:

{
  "subAccount": {
    "id": "6f1c…",
    "name": "Acme Dental",
    "externalId": "cust_8412",
    "status": "active",
    "limits": { "maxConcurrentCalls": 5, "monthlySpendLimitUsd": 200.0, "totalSpendLimitUsd": null },
    "suspendedAt": null,
    "suspendedReason": null,
    "closedAt": null,
    "createdAt": "2026-10-16T09:12:44.120Z",
    "updatedAt": "2026-10-16T09:12:44.120Z",
    "usage": { "monthToDateSpendUsd": 0.0, "activeCalls": 0 }
  },
  "apiKey": {
    "id": "a41e…",
    "key": "vai_live_…",
    "lastFour": "Xk2Q",
    "name": "Sub-account key",
    "role": "admin",
    "createdAt": "2026-10-16T09:12:44.120Z"
  }
}

apiKey.key is shown exactly once. Store it encrypted on your server; it is the sub-account's credential for every other Telenow API — agents, numbers, calls, campaigns, webhooks, knowledge bases — exactly as your own key is for you. Never send it to a browser: mint a client token with it instead.

FieldTypeDefaultNotes
namestring—Required. Up to 160 characters.
externalIdstringnoneYour id for this customer, unique among your sub-accounts (409 if reused). Up to 200 characters. Find a sub-account by it with ?externalId=.
limitsobjectno limitsSee Limits.
keyNamestringSub-account keyName of the key minted with it.
keyRolestringadminadmin, developer or viewer. Never owner.

Unknown fields are rejected with 400 rather than ignored — a misspelt limit would otherwise create a sub-account with no limits.

The sub-account copies your country, currency, timezone, consent enforcement, compliance mode and data-residency region when it is created.

Limits

FieldMeaning
maxConcurrentCallsLive calls at once — inbound, outbound, web and campaign calls together.
monthlySpendLimitUsdUSD this sub-account may spend in the current billing month (the same calendar month, India time, as your own cap and invoice).
totalSpendLimitUsdUSD this sub-account may spend, ever.

Each one: absent = unchanged (on create: no limit), null = no limit, a number = that limit, 0 = stopped. Spend is what the sub-account's calls and purchases were charged, the same figure that left your wallet.

When a limit is reached, the sub-account's new calls are refused the way an out-of-credit account's are: an outbound or API call gets 403 (or 429 for the live-call limit), a campaign waits and retries, and an inbound call is turned away busy before it is answered on Twilio, Plivo, SIP trunks and WhatsApp calling. On Vobiz and Vonage a refused inbound call follows the number's missed-call setting (forward or message), as it does for any account; on Exotel and Smartflo the call is dropped when its audio stream opens. Calls already live when a spend limit or a suspension takes effect are ended within the next spend-guard tick (a suspension through this API ends them at once).

A sub-account is also refused when your account would be: your wallet out of credits (prepaid), your account suspended, or your monthly spend cap reached — your cap counts every sub-account's spend. What a sub-account is told in those cases never includes your balance, cap or spend.

Update, suspend, resume

curl -X PATCH https://api.telenow.ai/api/v1/sub-accounts/6f1c… \
  -H "X-API-Key: vai_live_…" -H "Content-Type: application/json" \
  -d '{ "limits": { "monthlySpendLimitUsd": 500, "maxConcurrentCalls": null } }'

PATCH takes name, externalId (null clears it) and limits; an empty body is 400. A closed sub-account can't be changed (409).

curl -X POST https://api.telenow.ai/api/v1/sub-accounts/6f1c…/suspend \
  -H "X-API-Key: vai_live_…" -H "Content-Type: application/json" \
  -d '{ "reason": "Invoice overdue" }'

Suspending refuses every new call at once and ends the sub-account's live calls. The optional reason (up to 500 characters) is appended to the refusal its calls get. …/resume lifts it. A typical product suspends a customer's sub-account when that customer's own balance with you runs out, and resumes it when they top up.

Usage

curl "https://api.telenow.ai/api/v1/sub-accounts/6f1c…/usage?from=2026-10-01T00:00:00Z" \
  -H "X-API-Key: vai_live_…"
{
  "subAccountId": "6f1c…",
  "from": "2026-10-01T00:00:00Z",
  "to": "2026-10-16T09:20:00Z",
  "calls": 412,
  "billableSeconds": 51840,
  "spendUsd": 63.12,
  "activeCalls": 2,
  "monthToDateSpendUsd": 63.12,
  "totalSpendUsd": 180.4,
  "limits": { "maxConcurrentCalls": 5, "monthlySpendLimitUsd": 200.0, "totalSpendLimitUsd": null },
  "status": "active"
}

from and to are RFC 3339 instants; they default to the start of the current billing month and now. calls and billableSeconds count calls that started in the window; spendUsd is what was charged in it — a call is charged when it settles, shortly after it ends. Use these figures to bill your customer.

For per-call detail, call GET /api/v1/calls with the sub-account's key, or subscribe your own endpoint to call.ended — see Webhooks.

List

GET /api/v1/sub-accounts — query: status (active, suspended, closed), externalId (exact), limit (default 50, max 200), offset.

{ "subAccounts": [ { "id": "6f1c…", "name": "Acme Dental", "status": "active", "usage": { "monthToDateSpendUsd": 63.12, "activeCalls": 2 }, "…": "…" } ], "total": 1, "limit": 50, "offset": 0 }

Keys

curl -X POST https://api.telenow.ai/api/v1/sub-accounts/6f1c…/keys \
  -H "X-API-Key: vai_live_…" -H "Content-Type: application/json" \
  -d '{ "keyRole": "admin", "revokeExisting": true }'

Mints a new key (returned once, as on create) and — unless you send "revokeExisting": false — revokes every other key of that sub-account in the same step. Use it to rotate, or if a key leaked. Returns { "subAccountId", "revokedKeys", "apiKey" } with 201.

A sub-account's keys act as your organization's owner inside that sub-account. If that person leaves, rotate each sub-account's key to re-attach it to the current owner.

Close

DELETE /api/v1/sub-accounts/{id} closes it for good: its keys are revoked, new calls are refused, live ones are ended, running campaigns are paused and the workspace is deleted. It is refused with 409 while the sub-account still holds phone numbers — their rent is charged to you, so release them first (with the sub-account's key). Closing twice is harmless.

Webhooks

Every event a sub-account raises carries subAccountId (its id) and is delivered to:

  • the sub-account's own endpoints, as usual, and
  • every org-wide endpoint of the parent (one with agentId: null) whose events match.

So one endpoint on your own organization receives every customer's calls; route them by subAccountId. Your agent-scoped endpoints never receive a sub-account's events. The parent's copy appears in your delivery log. See Webhook events.

Errors

StatusWhen
400Invalid field, unknown field, empty update, bad timestamp.
403Not an owner/admin (person or key role); a sub-account's key; a demo workspace.
404Not one of your sub-accounts.
409externalId already used; changing or closing a closed one; closing while it holds numbers.