app.sempera.org/api/*): they take a signed-in session, and a Sempera API key is honoured only on /v1 (Authentication and credentials, API overview). They are described here so an integration knows what the App does around the API, what the customer sees, and which /v1 reads cover the same ground. Nothing on this page is a route you can call with a key.
Exporting call data
Where: Conversations (Export) and Settings, Data and privacy. Who: workspace owners only. An owner exports one agent’s phone calls, narrowed by the same filters the Conversations list has, as JSON or CSV.
The file is built in the background, so a large export never holds a request open:
- One export at a time per agent; asking for a second while one is being built is refused with the export already underway named.
- At most 10,000 calls and 64 MiB. Past either, the export fails with a sentence that says to narrow the date range.
- When it is ready, the workspace’s verified owners get an email with a link to Data and privacy in the App. The link opens a page; it is never a link to the file and carries no token, so a forwarded email opens nothing without an owner’s own session.
- The file leaves only through an owner-gated download in the App, with the same owner, agent-scope and workspace checks as the export itself. It is never a public URL, and it is served
no-store. - A file can be downloaded for seven days, then it expires and is removed.
calls. A CSV export has one row per call, with the columns id, started_at, duration_s, caller, outcome, summary, recording, transcript, turns, qc_status, qc_tags, deleted_at, delete_reason and conversation, a byte order mark and CRLF line ends so a spreadsheet opens it as UTF-8. A cell that starts with =, +, -, @, a tab or a carriage return gets an apostrophe in front, so a spreadsheet shows it and never runs it.
Requests, builds and downloads are recorded in the workspace’s audit log as call.export_requested, call.exported and call.export_downloaded, with counts and filters and never a number, a search string or a word of a transcript.
Reading calls from code is GET /v1/calls and GET /v1/calls/{call_id} (Sessions and calls).
Outbound batches: start time, CSV recipients, scheduler
Where: an agent’s Outbound page. A batch is a list of people the business has permission to reach. It carries a name, a consent basis in words (at least 12 characters, kept on the record), a calling window read in each person’s own time zone (default 09:00 to 18:00), and its limits: at most 500 recipients, 1 to 20 at once (default 3), 1 to 5 attempts (default 3), a wait of a minute to a week before a retry (default an hour). A call batch also needs an owner’s approval before it can start. Before anyone is reached the workspace’s do-not-contact list is checked, and a number on it is recorded as blocked and never called. Start time. A new batch takes an optional “Start sending at”, a date and time read in the batch’s time zone (or carrying its own offset). It may be at most five minutes in the past and at most a year ahead. Nothing is due before it, and after it each person is still reached only inside their own window. The batch says when it sends. CSV recipients. People can be added from a CSV file as well as typed. The file is at most 256 KiB, and it is only read: checking a file stores nothing and contacts nobody.- With a header row, the number column is the one named
number,phone,phone_number,phone number,mobile,cellorto; atimezone,time zone,time_zoneortzcolumn sets each person’s zone; on a call batch aprecall_dtmforkeyscolumn sets the keys pressed when the call connects; every other named column becomes a{field}the message can use. Without a header, the columns are number, name and note. - Every row goes through the same check a typed person does. A row that fails, or that has a cell starting the way a spreadsheet formula does (
=,+,-,@, a tab or a carriage return; a phone-shaped number is not one), is left out and reported with its row number and the reason, never the cell. A number listed twice is contacted once. Rows past the 500th are left out with a note.
POST /v1/calls. When a cap is reached nobody is called and the answer says which limit and when it lifts: a batch pass leaves the rest pending, a single call is refused with 409, and POST /v1/calls answers 429 rate_limited with retry_after_s in the error. A cap that cannot be read stops calling: a batch pauses, a single call is refused, and POST /v1/calls answers 503 service_unavailable. A call turned back before it was dialled gives its slot back. Texts are not calls and are not counted.
Calls at once. A workspace’s own limit on calls at once and on minutes a day, the one checked when a preview call starts, also applies to every call the Outbound page dials, a batch pass or a single call. It is the limit of the workspace the agent belongs to, whoever pressed the button. It is checked just before the dial, and each outbound call counts against it while it lasts, and in Calls started today on Settings, Usage and balance (below). At the limit nobody is called: a batch pass leaves the rest pending and the page says why the batch waits, and a single call is refused with 409 and the reason. The refusal is audited with the reason (a batch pass in its own pass record), and so is a single call refused because it is outside the person’s calling hours.
From the API: POST /v1/calls places one outbound call (Sessions and calls). The compatibility read GET /v2/batches/list lists an agent’s batches with their recipients and progress; it does not carry the start time.
Usage this billing period
Where: Settings, Usage and balance (This billing period). Who: any member of the workspace. The card sets the workspace’s inbound calls and minutes beside its outbound calls and minutes for the current billing period: the subscription’s own current period when there is one, otherwise the UTC calendar month, and it says which.- Inbound counts every phone call the workspace’s agents answered in the period, once each: the calls the carrier reported and the calls answered on Sempera’s own voice service. A call seen both ways is counted once, by the same rule that sends one
call.completed(Webhooks). A call answered on Sempera’s own voice service is counted once it is announced, usually within a minute or two of ending; one not announced within a day of ending is not counted. Test, Try it live and simulator sessions are never counted. - Outbound counts the calls the workspace placed, how many were answered, and the minutes from answer to hang-up.
- A call with no measured length is counted as a call and named, never as zero minutes.
- Every figure is an estimate of use, not an invoice. The plan’s published figures are shown beside them as published; no overage charge is worked out, and inbound and outbound minutes are not combined against the plan’s included minutes.
/v1 read for these figures.
Today’s use, on the same page, shows Calls started today (the UTC day): each call the workspace’s limit on calls at once let through, whether a session started from the App or POST /v1/sessions, or an outbound call the Outbound page dials (a batch pass or a single call). An outbound call counts once it passes that check, even when the line then turns out busy or nobody answers, and each retry counts again; a call refused at the limit does not count. Calls answered on the workspace’s phone numbers are not in this count.
Buying a phone number
Where: Channels, Phone numbers, Buy number. Workspace owners only. Opening the dialog buys nothing. It shows Country and verifies whether a payment method is saved for this workspace. A failed verification says the status is unknown, never that no method exists. Add payment method opens the existing hosted billing settings with a return link; returning to the App refreshes the status. If billing is not configured, the App reports that refusal instead of displaying a card form. Buying still requires an active paid plan, a verified saved payment method, the workspace’s own agent, the carrier purchase gate, explicit cost confirmation and CSRF protection. Neither a saved method nor a paid plan alone authorizes a purchase. No card details or processor customer identifiers are returned to the dialog. These are session-only App controls, not developer-key API endpoints.Tool analytics
Where: an agent’s Tools page. For each tool the agent called, over the last 7, 30 or 90 days (default 30):
The page also totals every tool. Tools letting callers down come first, then the busiest. The figures are counted from the tool calls recorded in the agent’s conversations (previews, simulations and phone calls): request and response bodies never reach the rollup, and a call with no time at all cannot be placed in a window, so it is left out and counted.
Other pages: a tool’s own test lives on its Test call tab, and the tool form can read a pasted
curl command into its fields. The command is only read, never run: files, certificates, substitutions and pipes are refused, every credential in it becomes a named secret reference and its value is dropped, and nothing is saved until the owner reviews and saves. GET /v1/tools lists the tools (Tools and integrations); there is no API read for these analytics.
Code snippets in the App
The Tools page covers the whole owner road for the snippetssnippets.md describes, on the same records: write a snippet in one editor (name, description, code, inputs, result shape, timeout) that says which draft a save writes and that published versions stay as published; test the draft or any version on sample input shaped from its inputs; publish behind a dialog that says the number is frozen; discard a draft; read the version history, newest first, with each version’s code and hash, and which tools pin it.
In the tool builder, a Code snippet tool has a pin picker: a snippet and one of its published versions, and a preview of what the model fills in. An existing tool shows what it runs today. Its version changes only after “Move the pin” and an explicit confirmation, and Save stays closed until then. Writing a snippet needs the operator role or above; pinning it in a tool needs the owner role, as POST /v1/tools does.
Staging and analytics: App routes, not /v1
Two App features are served by the App’s own routes and have no public /v1 counterpart yet. They take the signed-in owner’s session, not an API key, carry no stability promise and are not in openapi.json. Build nothing against them; if you need the data over the API, say so at hello@sempera.org.
- Staging. An agent’s Environments page keeps one staged snapshot of the draft between development and production. Owners publish the draft (or an earlier version) to staging, try it live against it, restage an earlier snapshot, discard it, promote it, or roll production back. Promotion is the ordinary publish from the staged snapshot, and a snapshot is pinned to the production version it was staged over: once production moves it is stale and cannot be promoted. By default staging never reaches a caller: a phone number set to staging answers with production. Only when Sempera’s call service has its pinned-version switches on (
SEMPERA_PINNED_VERSIONSand the agent’s own serve-staging-numbers setting) does such a number answer with the staged snapshot, once Sempera’s servers have compiled and sealed it, and any problem falls back to production; the environments view’snumbers_notesays which applies. The routes live under/api/agent/environments. Read-only listings of what each environment holds are on/v2/agents/{uuid}/environments(MCP for coding agents). - Analytics. The workspace and agent Insights pages (Traffic, saved Reports with CSV download, and Boards of report tiles) read the App’s routes under
/api/analyticsand/api/insights. An old/analyticsaddress on an agent opens Insights.