Skip to main content
Some of what a workspace owner does in the Sempera App has no route in this API. The routes behind these features are the App’s own (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.
Deleted content is never exported. A transcript that was deleted, or is past its retention window and waiting for the next sweep, is listed with its state and no words. If a transcript is deleted after the file was built, the file is withdrawn and removed, and its download answers that the file is gone. A JSON export is one document: a header naming what the export covers, then 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, cell or to; a timezone, time zone, time_zone or tz column sets each person’s zone; on a call batch a precall_dtmf or keys column 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.
Scheduler. Whether a started batch sends on its own is a setting of the deployment. When it is on, a pass runs once a minute and advances every running batch whose start time has come and that has someone due. When it is off, a batch moves only when the owner presses “Send the next few now”. The scheduler has no way of dialling of its own: it runs the same pass the button runs, with the same do-not-contact check, the same outbound call limits, the same per-person window and the same audit record (which notes whether the button or the scheduler ran the pass). It never touches a draft, and skips a paused or stopped batch. A pause or stop pressed while a pass is placing calls takes effect before the next person. A restart never dials anyone twice. A person marked as being dialled when a pass was interrupted is recorded as failed, with a note that the call may already have rung, and is not called again. The Outbound page shows whether started batches send on their own here and how often, why a running batch is waiting (an outbound call limit, or the workspace’s limit on calls at once), and what the last pass sent and who ran it. Outbound call limits. An owner can cap the calls the workspace places per UTC hour and per UTC day (Settings, Usage and balance). No cap is set by default. The platform may set a ceiling for every workspace, and the stricter of the two applies. Every road that places a call spends one call from the cap after the do-not-contact check and before the dial: a batch pass, a single call from the Outbound page, and 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.
There is no /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 snippets snippets.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_VERSIONS and 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’s numbers_note says 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/analytics and /api/insights. An old /analytics address on an agent opens Insights.