Tonedocs

Changelog

Every change to the /v1 contract. Additive only — see the versioning policy for what that guarantees.

Changes are additive: nothing listed here removes or retypes anything you already receive. See Versioning for what that commits us to.

Subscribe by watching openapi.json — it is regenerated from the code and is the machine-readable record of everything below.


2026-10-14 — Transfers to a person

Added

  • An agent can hand a live call to a person at your business. Set up who may be rung with /v1/transfer-destinations (each one verified by a call that speaks a code), and when, with callSettings.transfer on the agent. Runs on Plivo and Exotel numbers. See Transfers to a person.
  • transfer on the call — GET /v1/calls/{id}, the call list and every call webhook carry the latest attempt: status, outcome, destination label, times, how long the person talked and the brief they heard. null when the agent never tried. GET /v1/calls/{id}/transfers lists every attempt.
  • Three webhook events: call.transfer_started, call.transferred and call.transfer_failed, each with the attempt as data.transfer and a links.handoff page for the person taking the call. See Webhooks.
  • GET /v1/calls/{id}/transfers/{transferId}/recording and …/return-recording — the person's part of the call, and the agent's part after an attempt nobody took, each recorded separately.
  • A transcript role, human_agent, for the person's lines after a handoff.
  • Warm handoff: callSettings.transfer.handoff: "warm" keeps the caller with the agent until the person has pressed 1, on Plivo numbers. Such an attempt is mode: "consult", passes through the status accepted, and carries acceptedAt. See Warm handoff.
  • A second person: destinations[].fallbackDestinationId names who to try when nobody at a destination takes the call, rung at once on Plivo numbers. See A second person.
  • Transfers on your own SIP trunk: the person is rung over the call's own trunk, always warm, and their minutes bill at ₹1.50. Their part is recorded and transcribed as on Plivo. See On your own SIP trunk.
  • Transfers on BYO calls: your bot asks with a transfer frame on its Tone Media Streams socket and is told how the attempt goes; trigger: "byo" on the attempt. POST /v1/calls/{id}/transfer takes handoff for a BYO call. See Handing the call to a person.
  • POST /v1/calls/{id}/transfer — your backend asks the agent on a live call to hand it to a person; trigger: "api" on the attempt. A new error code, call_not_live, when no agent is on the call.

Nothing you already receive changed. On a transferred call, the time the caller and the person talk bills at ₹2.00 a minute; see Billing.


2026-10-11 — New agents default to a Murf voice

Changed

  • An agent created without ttsModel and ttsVoice now gets a Murf voice that speaks every language in its languages, female like the previous default. If no Murf voice speaks them all, it gets bulbul:v3 / simran as before. An agent that names its voice is unaffected. See Voices.

2026-10-11 — Lower per-minute prices: Developer and Enterprise plans

Changed

  • The list rate is ₹5.00/min (was ₹6.00): agent ₹3.00 + telephony ₹0.50 + platform ₹1.50. This is the Developer plan, which every organization is on unless it has an Enterprise rate.
  • Enterprise, from ₹4.00/min and lower at higher volume, is a rate agreed per organization. ratePaisePerMinute on GET /v1/wallet reports the rate you are on.
  • The component rates moved with it: bring your own voice ₹2.00/min (was ₹2.50), your own SIP trunk ₹4.50/min (was ₹5.50), your own trunk with your own voice ₹1.50/min (was ₹2.00). See Billing.

Nothing in the API's shape changed. Calls already billed keep the rate they were billed at — every call records its own ratePaisePerMinute.


2026-10-06 — A readable evidence PDF

Changed

  • GET /v1/calls/{id}/evidence.pdf is redesigned as one page: a verdict, a timeline of what happened when (in IST, to the second), what each compliance check found, the call's details, and the tamper-evident seal. It is still read only from the sealed record. GET /v1/calls/{id}/evidence is unchanged.

2026-10-06 — Promotional calls are checked against India's DND register

Added

  • dnd_register, a pre-dial check that can block. A promotional call to a number on India's DND register is refused with blocked_dnd, unless an explicit consent for that number is on record. Transactional and service calls are not checked: DND does not govern them, and their numbers are never sent to the lookup. A lookup that cannot answer records a warning and the call goes ahead. See Compliance.
  • source: "vendor" on check rows answered by the third-party DND lookup.
  • Promotional campaigns have their list checked at launch. Listed numbers are excluded and counted under dnd_scrub.

Changed

  • dnd_register replaces carrier_dnd on calls from today. carrier_dnd rows on earlier calls keep their meaning: advisory, never a block.
  • The a2p (robocall declaration) check applies to promotional calls only, for now. Transactional, service and collections calls record it as not_applicable, and a call's compliance is no longer flagged for it. Calls placed earlier keep their recorded warn, but it no longer flags them either.
  • In test mode, only +915555000005 is on the DND register. Before, every number ending in 0 also read as listed. That was harmless while the check only warned, but would now block one test number in ten. See Test mode.

2026-10-06 — Transactional campaigns skip the frequency cap

Changed

  • The 7-day frequency cap no longer applies to transactional campaigns. Before, a transactional campaign left out anyone your organization had reached in the last 7 days, so a customer who ordered twice in a week missed the second confirmation call. Promotional and service campaigns are still capped, and their recipients still land in the frequency_cap bucket of the pre-flight. See Campaigns.

2026-10-05 — GST tax invoices for top-ups

Added

  • A GST tax invoice for every top-up, emailed as a PDF to your organization's owners and admins and listed on the dashboard's Billing page. It carries your billing name, address and GSTIN, and charges CGST and SGST inside Madhya Pradesh or IGST anywhere else. Its QR code opens a page where anyone holding the invoice can check it and download the original. See Billing.
  • billing_details_required (422): a top-up started before your organization saved the billing name, address and state the invoice needs. Top-ups are started from the dashboard, which asks for them the first time. See the error catalog.

2026-10-05 — Calls stop at an empty wallet, campaigns stay inside your quota

Fixed

  • POST /v1/calls refuses a live call the wallet cannot pay for, with 402 insufficient_funds: when the balance is below ₹12, or while the wallet is on hold for a payment dispute. The error catalog and the billing page already said a call fails rather than being placed at zero; it is now true. details carries balancePaise and minBalancePaise. Test-mode calls are unaffected.
  • A campaign pauses instead of failing recipients when the wallet runs dry. It pauses with pausedReason: "low_balance" (and emits campaign.paused) even if you set no autoPauseBelowPaise, and the recipient it was about to call keeps its attempt.
  • A campaign never dials past your concurrency quota. Each tick it dials at most the agent calls GET /v1/limits says are free, counting everything else running for your organization. Before, a maxConcurrent above the quota had its overflow refused and those recipients settled failed.

2026-10-02 — List totals, and searching calls by number

Added

  • search on GET /v1/calls and GET /v1/calls/export.csv — digits of a phone number, found at either end of a call: the number it came from or the one it went to. Part of a number matches anywhere in it, and formatting is ignored — +91 98765 43210, 09876543210 and 4321 all work.

  • meta.pagination.total on the first page of every list — calls, campaigns and their recipients, compliance checks, evidence records, consent records, the suppression list and the wallet statement: how many results match your filters, counted up to 10,000. Until now only the suppression list and number search sent one.

  • meta.pagination.totalCapped, sent with total: true when more than 10,000 results match. The suppression list's total is now counted the same way.

  • Paging on two lists that stopped at a fixed number. GET /v1/integrations/webhooks/deliveries returned the newest 50 deliveries and GET /v1/campaigns/{id}/compliance the newest 500 checks, with no way to read further. Both take limit and cursor now and carry a total. Called without them, they return what they always did.

Fixed

  • Paging through rows created together. Rows written by one request — a bulk suppression upload, a campaign's recipient list — could be skipped by the next page (newest-first lists) or shown again (a campaign's recipients, oldest first). Every page now continues exactly where the last one stopped. See Pagination.

2026-10-01 — Choosing and buying numbers

Added

  • GET /v1/numbers/carriers — the carriers selling numbers right now. Pass one's provider to GET /v1/numbers/available or GET /v1/numbers/regions to see that carrier alone.
  • A count on number search. The first page of GET /v1/numbers/available carries meta.pagination.total when every carrier searched can count what it lists.
  • When a number renews. Every offer carries renewalCycle and renewsAt, and every number renewalCycle. anniversary renews monthly on the day it was bought; calendar_month — VoiceLink, for numbers bought from today — runs its first period to the end of the month it was bought in, at the full monthly rental, and renews on the 1st. See Rent.
  • confirmedTotalPaise on POST /v1/numbers/purchase, and the error it can return, price_changed: send the total your customer reviewed, and a price that has moved since is refused instead of charged.

2026-08-25 — First published

The initial /v1 contract: 92 operations an API key can call, 12 webhook events, and 32 error codes.

Added

  • Agents — create, publish, roll back, and read any published version. Every call pins the version that ran it.
  • Phone numbers — search carrier inventory, buy, route to a Tone agent or your own stack, and read the rental state.
  • Calls — place outbound, receive inbound, and read the detail record, transcript and recording.
  • Campaigns — 20 operations covering recipients, pre-flight, launch, pacing, retries and outcomes.
  • Compliance — the pre-dial gate as a standalone verdict API, a consent ledger, suppression lists, outcome ingestion and evidence packs.
  • Knowledge bases — documents an agent answers from, with a retrieval preview that runs the same path a live turn runs.
  • Wallet — balance, ledger and usage by category.
  • Webhooks — endpoint management, per-endpoint secrets with 24-hour rotation overlap, delivery history and replay.
  • Limits — request budget and both concurrency pools.

Conventions established

  • Idempotency-Key, required on number purchase and campaign launch.
  • Rate-limit headers on every keyed response, successes included.
  • doc_url on every error, pointing at the catalog.
  • Every enum published as an open set — see Versioning.

How an entry gets written

The contract is diffed on every change:

pnpm --filter backend openapi:diff --markdown

That produces the mechanical half — what was added, and whether anything was breaking. The prose is written by hand, because "new field agentVersion" is not the same as "a call now records which version of the agent ran it, so a finished call stays explainable after the prompt changes".

Was this page helpful?

On this page