Carriers (BYOC)
Carriers & trunks API
Two ways to run calls over infrastructure you control:
- Bring your own carrier (BYOC) — Plivo, Twilio, Vobiz, Exotel, Vonage, or Tata Tele Smartflo account credentials, so calls run over your account and rates while Telenow handles the agent pipeline, media, and recording. Managed under
/api/orgs/{orgId}/carriers. - SIP trunks — your own telco gateway / PBX / wholesale carrier, terminated by Telenow's native SIP gateway. Managed under
/api/orgs/{orgId}/trunks.
Both are organization-scoped and authenticated with a dashboard JWT (these routes are not on the API-key surface). RBAC is enforced server-side: listing needs membership; create/sync/update/validate/DID changes need the developer, admin, or owner role; delete needs admin or owner. Every response uses the standard envelope (conventions).
Carriers (BYOC)
| Method | Path | Purpose |
|---|---|---|
GET | /api/orgs/{orgId}/carriers | List connected carriers ({ connections, total }) |
POST | /api/orgs/{orgId}/carriers | Add carrier credentials (auto-syncs numbers on create) |
POST | /api/orgs/{orgId}/carriers/{id}/sync | Re-pull the carrier's owned numbers |
DELETE | /api/orgs/{orgId}/carriers/{id} | Disconnect (soft-delete) the carrier |
Adding a carrier
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/carriers \
-H "Authorization: Bearer <jwt>" -H "X-Org-Id: {orgId}" \
-H "Content-Type: application/json" \
-d '{ "provider": "plivo", "label": "Acme Production", "authId": "MA…", "authToken": "…" }'
| Field | Required | Per-provider meaning |
|---|---|---|
provider | Yes | One of plivo, twilio, vobiz, exotel, vonage, smartflo. |
label | — | A friendly name shown in the dashboard. |
authId | Yes | Plivo/Vobiz: Auth ID. Twilio: Account SID. Exotel/Vonage: API key. Smartflo: your Smartflo account identifier — it is not sent to Tata, and is only used to recognise the same account on a re-connect. |
authToken | Yes | Plivo/Vobiz: Auth Token. Twilio: Auth Token. Exotel: API token. Vonage: API secret. Smartflo: the API token from Smartflo → API Connect. |
accountSid | Exotel only | The Exotel Account SID (third credential part — required for Exotel). |
subdomain | Exotel only | API cluster host: api.exotel.com (Singapore, default) or api.in.exotel.com (Mumbai). |
applicationId | Vonage only | The Vonage Voice Application UUID. |
clickToCallKeys | Smartflo only | Object mapping each number to its Click to Call Support API key — {"+918041234567": "…"}. Tata scopes this key per number, not per account: it selects both the caller-ID DID and the Voice-Streaming destination, so one account holds several. Required for outbound on a given number; an inbound-only connection can omit it entirely. |
baseUrl | Smartflo only | API host override. Blank ⇒ api-smartflo.tatateleservices.com. |
Credentials are stored encrypted and never returned by the API. On create, Telenow runs an initial sync and imports the account's numbers as source='byoc' rows; they then appear in GET /api/voice/numbers and can be assigned to agents like any other number (Phone numbers).
Outbound credentials. Outbound dials and live-call transfers use your credentials on Plivo, Twilio, Vobiz, Exotel, and Smartflo BYOC connections — the dial path resolves the owning connection's credentials for any number imported as BYOC. Vonage BYOC numbers take inbound on your account, but their outbound voice control is still signed by the platform Vonage application unless the connection carries a matching private key. See Telephony providers.
Smartflo is BYOC-only, and needs portal steps we cannot perform. There is no platform Smartflo account. Tata publishes no API that can point a DID at a voice bot, so after
POST /carriersimports your numbers they stay marked needs manual routing until you bind each one in the Smartflo portal. Outbound additionally needs a Click-to-Call key per number, generated there. Full walkthrough: Tata Tele Smartflo setup.Smartflo API tokens expire and cannot be refreshed — 1, 7, 15, 30, or 90 days, fixed at creation. Re-
POSTthe same account's credentials to update the connection in place (it adopts rather than duplicating).
Sync
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/carriers/{id}/sync \
-H "Authorization: Bearer <jwt>" -H "X-Org-Id: {orgId}"
Reconciles Telenow with the carrier (for example, picking up numbers added directly in the carrier console). Returns { imported, updated, total_remote }. Run it after making changes on the carrier side.
SIP trunks
For your own SIP infrastructure. Concepts are in SIP trunking.
| Method | Path | Purpose |
|---|---|---|
GET | /api/orgs/{orgId}/trunks/config | The platform's SIP endpoint to point trunks at |
GET | /api/orgs/{orgId}/trunks | List trunks with their DIDs ({ trunks, total }) |
POST | /api/orgs/{orgId}/trunks | Create a trunk |
PUT | /api/orgs/{orgId}/trunks/{id} | Update a trunk |
DELETE | /api/orgs/{orgId}/trunks/{id} | Delete a trunk (releases its DIDs) |
POST | /api/orgs/{orgId}/trunks/{id}/validate | SIP OPTIONS reachability ping |
POST | /api/orgs/{orgId}/trunks/{id}/numbers | Attach a DID (manual E.164) |
DELETE | /api/orgs/{orgId}/trunks/{id}/numbers/{numId} | Detach / release a DID |
Gateway config
GET …/trunks/config returns what to configure on your side — the values are derived from the deployment's settings, not hardcoded:
{
"success": true,
"data": {
"enabled": true,
"host": "sip.telenow.ai",
"ip": "203.0.113.10",
"port": 5060,
"transport": "udp",
"codecs": ["PCMU", "PCMA"],
"dtmf": "rfc2833"
}
}
enabled reflects the deployment's SIP_ENABLED flag — when false, trunk calls won't connect until the operator turns the gateway on.
Create / update a trunk
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/trunks \
-H "Authorization: Bearer <jwt>" -H "X-Org-Id: {orgId}" \
-H "Content-Type: application/json" \
-d '{
"name": "Contoso PRI East",
"direction": "both",
"inboundIpAcl": ["203.0.113.10", "198.51.100.0/28"],
"terminationUri": "sip.yourtelco.com:5060",
"authUsername": "telenow",
"authPassword": "…",
"defaultCallerId": "+14155550142",
"status": "active",
"dialFormat": "e164",
"countryCode": "91",
"fromDomain": "sip.yourtelco.com",
"paiEnabled": false
}'
| Field | Required | Notes |
|---|---|---|
name | Yes | Trunk label. |
direction | — | inbound, outbound, or both (default both). |
inboundIpAcl | For inbound | Array of source IPs/CIDRs; inbound INVITEs are authorized by source IP. |
terminationUri | For outbound | host[:port] Telenow dials for outbound (port defaults to 5060). |
authUsername / authPassword | — | Digest credentials, used only when your trunk challenges our outbound INVITEs. Stored encrypted. On update, omit them to leave the stored values unchanged; send an empty string to clear them. |
defaultCallerId | — | Caller ID used when an outbound leg has none. |
status | — | active or disabled (update only; default active). |
dialFormat | — | How the dialled number and caller ID are rendered into the outbound INVITE: e164 (default on create — keep the leading +), digits (legacy — strip everything but digits), national (drop the country code), national_0 (drop it and prepend 0). The two national forms only strip from a number written internationally (+… or 00…), so an already-national number is never truncated. |
countryCode | For the national formats | Calling code without +, e.g. 91. Must be sent together with dialFormat. |
fromDomain | — | Host used in the From (and P-Asserted-Identity) URI instead of the platform's own SIP host. Bare hostname or IP, optionally :port. |
paiEnabled | — | Send an RFC 3325 P-Asserted-Identity header (default false). |
Create defaults dialFormat to e164, which is what most carriers expect.
Trunks created before this field existed carry digits and are never migrated —
send dialFormat explicitly if you want to move one.
On update, every dial-plan field behaves like the credentials above: each is
preserved unless the request mentions it. A PUT that omits dialFormat leaves
the stored format alone — it is not reset to the create default — so a
partial update can never move a trunk's wire format behind your back.
countryCode is the one exception: it is validated against dialFormat, so
sending it without dialFormat is a 400 rather than being paired with a
guess.
POST returns 201 with { id }. PUT returns { success: true }. Trunk rows expose has_auth (whether digest credentials are set) but never the credentials themselves.
Validate
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/trunks/{id}/validate \
-H "Authorization: Bearer <jwt>" -H "X-Org-Id: {orgId}"
Sends a real SIP OPTIONS datagram to the trunk's termination URI and waits up to 4 seconds. Returns { reachable, latencyMs, detail } — reachable is true/false and latencyMs is the round-trip when reachable. An inbound-only trunk (no termination URI) returns reachable: false with guidance to verify your carrier points at the platform endpoint. This is the same check the dashboard's import wizard runs on its last step.
DIDs on a trunk
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/trunks/{id}/numbers \
-H "Authorization: Bearer <jwt>" -H "X-Org-Id: {orgId}" \
-H "Content-Type: application/json" \
-d '{ "e164": "+16465550198", "agentId": "agent-uuid" }'
DIDs are entered by hand — there's no search/purchase for trunk numbers. e164 is required (digits are normalized to + E.164); agentId is optional and binds the agent immediately. Returns 201 with { id, e164 }. Passing agentId for a number a member already holds for inbound returns 409 Conflict (inbound exclusivity). DELETE …/numbers/{numId} releases the DID.
Related
- Phone numbers API — search, buy, assign, compliance
- Telephony providers — the carrier comparison and credentials
- SIP trunking — the full trunk setup walkthrough