API reference
A REST API over HTTPS with bearer-token auth, JSON in and out, cursor pagination, and idempotent writes. Numbers, calls, recordings, transcriptions, messages, routing, sub-accounts, CDRs, and AI Voice Agents.
How the API works
Six conventions hold across every endpoint on this page.
HTTPS and versioning
REST over HTTPS only. The major version is in the path, as in /v1/. Fields are added over time and are not removed without notice, so parse defensively and ignore keys you do not recognize.
Bearer-token auth
Every request carries Authorization: Bearer and your token. Tokens are scoped to an account and can be limited to read-only or to a subset of resources. Sub-account access is a property of the token, not a parameter.
JSON bodies
Requests and responses are JSON. Timestamps are RFC 3339 in UTC. Telephone numbers are E.164 everywhere, both directions. Monetary values are strings so nothing is lost to floating point.
Cursor pagination
Collections return data, has_more, and next_cursor. Pass the cursor back for the next page. Cursors are opaque; do not construct them or assume they encode an offset.
Idempotency on writes
Send Idempotency-Key on any POST. A repeat of the same key returns the original response instead of creating a second resource. One key per logical operation, reused across retries.
Standard status codes
2xx for success, 4xx for something you can fix, 5xx for something we broke. Errors carry a machine-readable type and code alongside the message. See error format.
This page documents the shape of the API. The host you call, the authoritative schemas, and your credentials are issued during onboarding.
Authenticate, then read JSON
One header, one request, one paginated collection back. Examples use api.solvedtele.com as a placeholder host.
curl https://api.solvedtele.com/v1/calls?limit=1 \
-H "Authorization: Bearer $SOLVEDTELE_TOKEN"
# a write, made safe to retry
curl -X POST https://api.solvedtele.com/v1/messages \
-H "Authorization: Bearer $SOLVEDTELE_TOKEN" \
-H "Idempotency-Key: 7f1c9a2e-4b60-4e19-9c2f-0d3a" \
-H "Content-Type: application/json" \
-d '{"from":"+18665551212",
"to":"+14045550143",
"body":"Your appointment is confirmed."}'
{
"data": [
{
"id": "call_01J9K2M1Q8ZB",
"direction": "outbound",
"from": "+18665551212",
"to": "+14045550143",
"status": "completed",
"started_at": "2026-09-18T14:19:46Z",
"answered_at": "2026-09-18T14:19:58Z",
"billable_seconds": 129,
"attestation": "A",
"sub_account_id": "sub_2201",
"recording_id": "rec_01J9K2M9WD4C",
"applied_rate": "0.0100",
"currency": "USD"
}
],
"has_more": true,
"next_cursor": "Y3Vyc29yOjE3NTgxODI1Mjc"
}
Credentials come from onboarding
There is no self-serve key generator, and that is deliberate. A token on this network can place calls onto the PSTN.
- Issued with your trunk. You get an API token, a trunk hostname, and either SIP credentials or an IP allowlist entry, together.
- Sandbox first. Sandbox takes the same requests, returns the same object shapes, and fires the same webhooks, against a test number pool.
- Scoped and revocable. Restrict a token to read-only or to a subset of resources, and issue separate tokens per service so a rotation is not an outage.
- Guard-railed. Spend caps, velocity limits, and destination allowlists apply to API traffic exactly as they apply to SIP. See security.
Resources: the call path
Numbers, calls, recordings, transcriptions, and messages. The objects a conversation produces.
Numbers
Inventory search through to release, plus port state. This is the group platform customers put inside their own signup flow.
| Endpoint | What it does |
|---|---|
GET /v1/numbers/available | Search unowned inventory by rate center, state, NPA, prefix, number type, or vanity pattern. Cursor paginated. |
POST /v1/numbers/reservations | Hold a number for a short window so a user can finish a signup flow without losing it. Idempotent. |
POST /v1/numbers | Buy a number, optionally converting a reservation, and assign it to a trunk or routing target in the same call. Idempotent. |
GET /v1/numbers | List numbers on the account, filterable by sub-account, type, state, and assignment. |
GET /v1/numbers/{id} | Fetch one number with its assignment, E911 record, CNAM setting, and monthly rate. |
DELETE /v1/numbers/{id} | Release a number back to inventory. The monthly charge stops at the next cycle. Not reversible. |
GET /v1/ports | List port requests with their current state, submitted date, and FOC date where one has been issued. |
GET /v1/ports/{id} | Fetch one port request, including rejection reasons and the numbers in scope on a partial port. |
Calls
For platforms without their own media stack. If you originate from your own softswitch over a SIP trunk, you may never touch this group.
| Endpoint | What it does |
|---|---|
POST /v1/calls | Originate a call, naming the from number, the destination, the route preference, and any recording or agent options. Idempotent. |
GET /v1/calls | List calls filtered by time range, direction, status, sub-account, or number. Cursor paginated. |
GET /v1/calls/{id} | Fetch one call with timestamps, billable seconds, SIP response, hangup cause, route, carrier, and attestation. |
POST /v1/calls/{id}/hangup | End a live call. Returns 409 if the call has already cleared. |
POST /v1/calls/{id}/transfer | Transfer a live call to a number, a queue, or the routing engine, with or without an announcement leg. |
Recordings
Capture runs in the network, so recordings exist whether or not your application was up when the call happened.
| Endpoint | What it does |
|---|---|
GET /v1/recordings | List recordings by call, date range, or sub-account. Cursor paginated. |
GET /v1/recordings/{id} | Fetch recording metadata: duration, channels, format, storage state, and retention expiry. |
POST /v1/recordings/{id}/download-url | Mint a time-limited signed URL so your own front end can stream or download media without proxying it. |
DELETE /v1/recordings/{id} | Delete the media. The CDR that references it is retained on its own schedule. |
Transcriptions
Transcription runs against a stored recording and returns speaker labels and timestamps. Redaction is applied at no additional charge.
| Endpoint | What it does |
|---|---|
POST /v1/transcriptions | Request a transcription for a recording, optionally with summary and sentiment. Returns 202 and completes asynchronously. Idempotent. |
GET /v1/transcriptions | List transcriptions by recording, call, or date range. Cursor paginated. |
GET /v1/transcriptions/{id} | Fetch the transcript with speaker labels, word timings, and any summary or sentiment output. |
Messages
A successful send is an acceptance, not a delivery. The outcome arrives separately as a receipt.
| Endpoint | What it does |
|---|---|
POST /v1/messages | Send an SMS or MMS from a number on your account. Returns 202 on acceptance. Idempotent. |
GET /v1/messages | List messages by number, direction, date range, or sub-account. Cursor paginated. |
GET /v1/messages/{id} | Fetch one message with its current delivery state, segment count, and applied rate. |
GET /v1/messages/{id}/receipts | Fetch carrier delivery receipts for a message, including failure reason codes. |
GET /v1/messages/consent | Read STOP, START, and HELP consent state for a destination on a given sending number. |
Resources: the business layer
Routing, sub-accounts, CDRs, and agents. The objects you bill from and resell on.
Routing
The routing engine under AgentTech Dialer and its Lead Marketplace, exposed as objects you can create and read.
| Endpoint | What it does |
|---|---|
POST /v1/routing/campaigns | Create a campaign with its overflow target, duplicate window, and suppression lists. Idempotent. |
GET /v1/routing/campaigns | List campaigns with their live concurrency and cap consumption. |
POST /v1/routing/buyers | Create a buyer with its bid terms, caps, dayparting, concurrency limit, and acceptance criteria. Idempotent. |
POST /v1/routing/publishers | Create a publisher with its payout terms and attribution identifiers. Idempotent. |
GET /v1/routing/bids | List bid events for a campaign or a call, including losing bids and decline reasons. |
GET /v1/routing/decisions/{call_id} | Fetch the routing decision for a call: the attributes passed, the buyers offered, and why the winner won. |
Sub-accounts
How a platform separates its own tenants. Numbers, trunks, and routing live per sub-account; rating and settlement stay at the parent.
| Endpoint | What it does |
|---|---|
POST /v1/sub-accounts | Create a sub-account carrying your own tenant identifier as its external reference. Idempotent. |
GET /v1/sub-accounts | List sub-accounts with their status and external reference. |
GET /v1/sub-accounts/{id} | Fetch one sub-account with its numbers, trunks, and configured limits. |
GET /v1/sub-accounts/{id}/usage | Usage totals for a period, broken out by service, so you can invoice your own customer from it. |
CDRs
Every leg, with the route, the carrier, the duration, and the rate that actually applied.
| Endpoint | What it does |
|---|---|
GET /v1/cdrs | Query call detail records by time range, direction, sub-account, number, route, or hangup cause. Cursor paginated. |
GET /v1/cdrs/{id} | Fetch a single record with the full set of fields, including the applied rate and the attestation level. |
POST /v1/cdr-exports | Create a scheduled export to Amazon S3, Google Cloud Storage, or an HTTPS endpoint, hourly or daily. Idempotent. |
GET /v1/cdr-exports | List scheduled exports with their destination, cadence, and last successful run. |
DELETE /v1/cdr-exports/{id} | Stop a scheduled export. Files already written are left in place. |
AI Voice Agents
Agents are configuration; sessions are the calls they run. Talk time bills at $0.10 per minute on top of standard connectivity.
| Endpoint | What it does |
|---|---|
POST /v1/agents | Create an agent: instructions, voice, permitted tools, and handoff conditions. Idempotent. |
GET /v1/agents | List agents with their version and current status. |
POST /v1/agents/{id}/sessions | Start a session, either placing an outbound call or attaching the agent to an inbound call or queue. Idempotent. |
GET /v1/agent-sessions | List sessions by agent, date range, or outcome. Cursor paginated. |
GET /v1/agent-sessions/{id} | Fetch one session with its transcript reference, tool calls, transfer result, and talk-time minutes. |
Webhooks
Register an HTTPS endpoint, subscribe it to the types you care about, and return a 2xx quickly. Deliveries are signed and retried with exponential backoff.
| Event type | When it fires |
|---|---|
call.initiated | A call leg has been created and is being set up. Carries the call id, direction, from, and to. |
call.answered | The far end answered. Carries the answer timestamp, which is where talk time starts. |
call.completed | The leg cleared. Carries billable seconds, SIP response, hangup cause, route, carrier, attestation, and the applied rate. |
recording.available | Media is stored and fetchable. Carries the recording id, duration, and the call it belongs to. |
transcription.available | Transcript, speaker labels, and any summary or sentiment output are ready. |
message.received | An inbound SMS or MMS arrived on one of your numbers. |
message.delivered | A carrier delivery receipt arrived, either a delivery or a failure with a reason code. |
number.ported | A port completed and the number is live on our network. |
bid.won | A buyer took a call in real-time bidding. Carries the campaign, the buyer, and the winning bid. |
Signing
Each delivery carries X-Solvedtele-Signature with a timestamp and an HMAC over the timestamp and the raw body, keyed with your endpoint secret. Verify against the raw bytes before parsing, compare in constant time, and reject anything outside a five-minute tolerance.
Delivery semantics
At least once, not exactly once, and not in order. Key on the event id and discard duplicates. A recording.available can arrive before the call.completed for the same call, so reconcile on the call id rather than on arrival order.
A full payload example is in the documentation, under payload shape.
Rate limiting and errors
One ceiling, one error envelope, and status codes that mean what they usually mean.
Rate limiting
- The standard ceiling is 600 requests per minute per account, shared across sub-accounts.
- A 429 carries
Retry-Afterin seconds. Back off on it rather than retrying immediately. - Bulk reads are paginated rather than throttled harder. Pull CDRs by cursor, or schedule an export instead.
- Higher limits are available on request. Describe the traffic shape, not just the peak number.
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": {
"type": "invalid_request",
"code": "number_not_available",
"message": "That number was taken by another
account before the purchase completed.",
"param": "phone_number",
"request_id": "req_01J9K2P5F3XQ"
}
}
| Status | Meaning |
|---|---|
200 OK | The request succeeded and the body carries the resource or a paginated collection. |
201 Created | A write succeeded and created a resource. The body carries it. |
202 Accepted | Work was accepted and completes asynchronously. The outcome arrives as a webhook. Used by message send and transcription requests. |
400 Bad Request | The request could not be parsed, or a required parameter is missing or malformed. |
401 Unauthorized | No bearer token, an expired token, or a token that does not belong to this account. |
403 Forbidden | Authenticated but not permitted: a scope the token does not carry, or a resource in another account. |
404 Not Found | No such resource, or it is outside the scope of this token. |
409 Conflict | The resource is not in a state that allows the operation, such as hanging up a call that has already cleared. |
422 Unprocessable Entity | Well formed and understood, but rejected by a rule: a number taken between search and purchase, a destination outside your allowlist, a cap already consumed. |
429 Too Many Requests | Rate limited. Retry-After carries the number of seconds to wait. |
5xx | Our fault. Safe to retry with the same idempotency key; a retried write will not duplicate. |
Quote the request_id when you report a problem. It is the fastest way for us to find the request in our logs.
FAQs
API questions
Where do I get a token?
From onboarding. Tokens are issued with your trunk credentials rather than generated from a self-serve form, because an account on a Tier 2 network is an authenticated path onto the PSTN. Ask for a sandbox token at the same time so you can build before your first live call.
Is this a live specification I can generate a client from?
This page documents the shape of the API: the resources, the endpoint paths, the conventions, and the event catalog. The authoritative request and response schemas, along with the host you call, come with your credentials at onboarding.
Do I have to use the API at all?
No. Many accounts point an existing PBX, softswitch, or dialer at a SIP trunk and never make an API call. The API is how platforms automate provisioning, routing, and reporting inside their own product.
How do idempotency keys work?
Send an Idempotency-Key header on any write. If the same key arrives again within the retention window, you get the original response back rather than a second resource. Generate one key per logical operation, not per HTTP attempt, and reuse it across retries.
How does pagination work?
Cursor based. A collection response carries a has_more flag and a next_cursor. Pass the cursor back to get the next page. Do not construct cursors yourself or assume they encode an offset; they are opaque and their format can change.
What are the rate limits?
The standard ceiling is 600 requests per minute per account, with bulk read endpoints paginated rather than throttled harder. A 429 carries a Retry-After header. Higher limits are available on request; tell us the shape of the traffic rather than just the number.
Can I white-label everything driven through the API?
Yes, and most platform customers do. Numbers are provisioned under your brand, routing and recording are driven by your calls, and CDRs come back split by your own tenant identifiers. Your users never see our name.
Something else? Contact us
Ready to build against it?
Onboarding issues a sandbox token and a production token in the same conversation.