> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sempera.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Owner features in the App

> Call-data exports, outbound batch scheduling and CSV recipients, per-tool analytics, code snippets in the App; features with no API route, and the `/v1` reads that cover the same ground.

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](https://docs.sempera.org/authentication.md), [API overview](https://docs.sempera.org/api-overview.md)). 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.

| Filter | Rule |
| - | - |
| Date window | An absolute start and end; either may be left open. A call with no start time is never left out by the window |
| Length | `any`, under 1 minute, 1 to 5, 5 to 15, or over 15 minutes. A call with no measured length is in none of the length buckets |
| Search | Up to 200 characters, matched the way the list matches it (the caller as shown, the call id and the outcome's own sentence) |

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](https://docs.sempera.org/sessions-and-calls.md)).

## 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](https://docs.sempera.org/sessions-and-calls.md)). 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](https://docs.sempera.org/webhooks.md)). 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):

| Figure | Meaning |
| - | - |
| Calls, succeeded, failed, refused | What happened to each call of the tool. A refusal is a call the tool's rules stopped before it ran |
| Success share | Succeeded calls over all calls, in percent |
| Time taken | The median and the 95th percentile, counting only calls that actually went out; a refusal, or a preview answer that nothing sent, is left out. The card says how many calls the two figures rest on |
| Why the rest did not succeed | Each reason with its count, from one fixed vocabulary of plain sentences; an unknown cause reads as "something else went wrong" and is never echoed |
| Last problem | The most recent reason, with when |
| Trend | One bucket per day on the business's own calendar (its time zone, or UTC when none is set): calls and how many did not succeed |

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](https://docs.sempera.org/tools-and-integrations.md)); 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](mailto: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](https://docs.sempera.org/mcp.md)).
* **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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.