AI Voice Agents are live on our Tier 2 rails at $0.10 per minute. See the product
REST JSON Webhooks

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.
Carrier cabling lit teal inside a data center rack

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.

EndpointWhat it does
GET /v1/numbers/availableSearch unowned inventory by rate center, state, NPA, prefix, number type, or vanity pattern. Cursor paginated.
POST /v1/numbers/reservationsHold a number for a short window so a user can finish a signup flow without losing it. Idempotent.
POST /v1/numbersBuy a number, optionally converting a reservation, and assign it to a trunk or routing target in the same call. Idempotent.
GET /v1/numbersList 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/portsList 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.

EndpointWhat it does
POST /v1/callsOriginate a call, naming the from number, the destination, the route preference, and any recording or agent options. Idempotent.
GET /v1/callsList 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}/hangupEnd a live call. Returns 409 if the call has already cleared.
POST /v1/calls/{id}/transferTransfer 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.

EndpointWhat it does
GET /v1/recordingsList 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-urlMint 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.

EndpointWhat it does
POST /v1/transcriptionsRequest a transcription for a recording, optionally with summary and sentiment. Returns 202 and completes asynchronously. Idempotent.
GET /v1/transcriptionsList 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.

EndpointWhat it does
POST /v1/messagesSend an SMS or MMS from a number on your account. Returns 202 on acceptance. Idempotent.
GET /v1/messagesList 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}/receiptsFetch carrier delivery receipts for a message, including failure reason codes.
GET /v1/messages/consentRead 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.

EndpointWhat it does
POST /v1/routing/campaignsCreate a campaign with its overflow target, duplicate window, and suppression lists. Idempotent.
GET /v1/routing/campaignsList campaigns with their live concurrency and cap consumption.
POST /v1/routing/buyersCreate a buyer with its bid terms, caps, dayparting, concurrency limit, and acceptance criteria. Idempotent.
POST /v1/routing/publishersCreate a publisher with its payout terms and attribution identifiers. Idempotent.
GET /v1/routing/bidsList 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.

EndpointWhat it does
POST /v1/sub-accountsCreate a sub-account carrying your own tenant identifier as its external reference. Idempotent.
GET /v1/sub-accountsList 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}/usageUsage 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.

EndpointWhat it does
GET /v1/cdrsQuery 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-exportsCreate a scheduled export to Amazon S3, Google Cloud Storage, or an HTTPS endpoint, hourly or daily. Idempotent.
GET /v1/cdr-exportsList 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.

EndpointWhat it does
POST /v1/agentsCreate an agent: instructions, voice, permitted tools, and handoff conditions. Idempotent.
GET /v1/agentsList agents with their version and current status.
POST /v1/agents/{id}/sessionsStart a session, either placing an outbound call or attaching the agent to an inbound call or queue. Idempotent.
GET /v1/agent-sessionsList 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 typeWhen it fires
call.initiatedA call leg has been created and is being set up. Carries the call id, direction, from, and to.
call.answeredThe far end answered. Carries the answer timestamp, which is where talk time starts.
call.completedThe leg cleared. Carries billable seconds, SIP response, hangup cause, route, carrier, attestation, and the applied rate.
recording.availableMedia is stored and fetchable. Carries the recording id, duration, and the call it belongs to.
transcription.availableTranscript, speaker labels, and any summary or sentiment output are ready.
message.receivedAn inbound SMS or MMS arrived on one of your numbers.
message.deliveredA carrier delivery receipt arrived, either a delivery or a failure with a reason code.
number.portedA port completed and the number is live on our network.
bid.wonA 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-After in 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"
  }
}

StatusMeaning
200 OKThe request succeeded and the body carries the resource or a paginated collection.
201 CreatedA write succeeded and created a resource. The body carries it.
202 AcceptedWork was accepted and completes asynchronously. The outcome arrives as a webhook. Used by message send and transcription requests.
400 Bad RequestThe request could not be parsed, or a required parameter is missing or malformed.
401 UnauthorizedNo bearer token, an expired token, or a token that does not belong to this account.
403 ForbiddenAuthenticated but not permitted: a scope the token does not carry, or a resource in another account.
404 Not FoundNo such resource, or it is outside the scope of this token.
409 ConflictThe resource is not in a state that allows the operation, such as hanging up a call that has already cleared.
422 Unprocessable EntityWell 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 RequestsRate limited. Retry-After carries the number of seconds to wait.
5xxOur 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.