Security and data
What Sphoro Voice holds, what separates your account from anybody else's, how long each kind of thing is kept, and what is never stored at all.
What isolates one account from another
Every request carries a credential, and the credential — not a parameter in the request — decides which account's data it can reach. There is no request shape that reaches across accounts, which is why an id from another account reads as "not found" rather than "forbidden": the object is not merely refused, it is not visible.
| Agents, calls, transcripts, recordings, analytics | Scoped to the account the key belongs to. |
| Knowledge bases | Held in a separate service, under a credential minted for your account alone. A fault in this platform cannot reach another account's documents, because the credential it holds cannot. |
| Projects | Not an isolation boundary. A cost label inside one account. See projects. |
You can see that for yourself: ask for an object that is not yours and the answer is
404, not 403.
# What this key can actually do.
curl -s https://voice.sphoro.com/v1/agents \
-H "Authorization: Bearer $SPHORO_API_KEY"
# An id from somewhere else: not found, rather than forbidden.
curl -s -o /dev/null -w '%{http_code}\n' https://voice.sphoro.com/v1/agents/agt_000000000000000 \
-H "Authorization: Bearer $SPHORO_API_KEY"Credentials
| Credential | Lives | Belongs |
|---|---|---|
| API key | Until you revoke it, or its expiry | On your server, in a secret store. Never in a page, an app bundle, or a repository. |
| Call token | Minutes, scoped to one call | In a browser. This is the only credential that should ever reach one. |
| Webhook signing secret | Until rotated | On your server, used to verify what we send you. |
POST /v1/realtime/tokens is to make shipping a key unnecessary — see
talk from a browser.Keys carry scopes, and the right key for a job is the narrowest one that does it: a dashboard that reads calls does not need to be able to create agents. See get an API key.
In transit
- Every API request is HTTPS. There is no plaintext endpoint.
- Webhook endpoints must be
https— anhttpURL is refused, because delivering a call transcript over it would put a conversation on the wire in the clear. - Every request the platform makes on your behalf — a knowledge-base crawl, a journey's API node, a webhook delivery — goes through a guard that validates the resolved address immediately before connecting. A URL you supply is input, and it does not get to name a private address.
What is kept, and for how long
| Data | Kept |
|---|---|
| Call records — times, numbers, direction, end reason, cost | For the life of the account. |
| Transcripts | With the call record. |
| Summaries and extractions | With the call record. |
| Audio recordings | Only if record_calls is on, and then for the
retention window agreed with your account. |
| Knowledge-base documents | Until you delete them. |
| Suppression list and consent records | Indefinitely — deleting a do-not-call entry is not a thing you want to happen automatically. |
| API keys | Stored hashed. The key itself is shown once, at creation, and cannot be retrieved afterwards. |
Retention is set per account rather than per agent, because it is a policy rather than a setting. Tell us what your obligations are and it is configured to them.
What is never stored
- Audio, unless you asked for it. With
record_callsoff, the audio of a call passes through and is not written down. The transcript is, because transcription is how the agent hears the caller at all. - The audio of a bridged call, ever. On a
webrtcagent the media runs directly between the two clients and does not reach this deployment. See the realtime protocol. - Anything on a
simpleagent's call. A person holds that call from their browser; no recogniser, model or synthesiser is opened for it, and there is no transcript because nothing on this side heard it. - Your API key in readable form.
Where calls are processed
Your account runs in one region, and the speech and language vendors behind it are part of that picture — a model call leaves the platform even when the call does not leave the region. If data residency is a requirement rather than a preference, two things are worth knowing:
- The model chain speaks the OpenAI wire format, and several presets are self-hosted runtimes. A deployment that must keep transcripts inside its own network can point the model at one. See vendors.
- Dedicated and in-region deployments are available. Talk to us before you build, not after.
Recording, consent and the law
Outbound calling has its own rules, and they are enforced rather than documented: the suppression list, consent records, and calling hours in the timezone the number actually belongs to. See compliance.
If something goes wrong
If you believe a credential has leaked, revoke it in the portal first and tell us second — revocation is immediate and does not need us. If you have found a vulnerability, get in touch before disclosing it anywhere else.