> ## 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.

# Security and privacy

> How workspaces are kept apart, how credentials, secrets and signed links work, what is kept about calls, and how to report a concern.

This page describes how the Sempera API keeps a workspace, and the people who call it, apart from everyone else, as far as the API's documented behaviour shows. It is a developer's view. It claims no certification, and it does not replace the [privacy policy](https://sempera.org/legal/privacy/), which lists what Sempera keeps, for how long, and which categories of provider handle it.

## What you can rely on

| Guarantee | What it means for an integration |
| - | - |
| One workspace per credential | A key belongs to one workspace and can read and write only inside it. A browser token belongs to one conversation. |
| Two locks, default deny | A request passes only when the key holds the route's scope and the identity behind it holds the role the action needs. |
| Another workspace's id looks like a missing id | An id from another workspace answers `404 not_found`, word for word the same as an id that never existed. A response cannot be used to find out what other customers have. |
| Secret values are not read back | Tool credentials, workspace secrets and signing keys are accepted once and never returned. |
| Errors carry no internals | An error never holds a stack trace, a secret or an internal hostname. |

## Workspace isolation

Every credential belongs to exactly one workspace, and everything it can read or write is inside that workspace ([API overview](https://docs.sempera.org/api-overview.md)). `GET /v1/workspaces` shows a key exactly one. Lookups check ownership before they answer: a recording, a secret or a key that belongs to another workspace is reported as not found, and nothing is signed or revealed for it. A tool or connection name is checked only inside its own workspace, so a name used elsewhere is never disclosed ([Call recordings](https://docs.sempera.org/recordings.md), [Secrets and request signing](https://docs.sempera.org/secrets-and-signing.md), [Migration and changelog](https://docs.sempera.org/changelog.md)).

When you build an integration, treat `not_found` on an id you were given as "not yours or not there", and do not retry it with a different credential in the hope of a different answer.

## Credentials

For API keys, the `Authorization` header value is `Bearer <api-key>`. Replace `<api-key>` with your key and never put that value in a URL or log.

* **API keys.** Send `Authorization: Bearer sempera_sk_...`. The secret is shown once, when the key is created. Sempera stores only a one-way hash of it, as the privacy policy describes. `DELETE /v1/api-keys/{id}` revokes a key immediately. No other header or scheme is recognised, and a key presented anywhere outside `/v1` is ignored ([Authentication and credentials](https://docs.sempera.org/authentication.md)).
* **Kinds cap what a key may hold.** A runtime or CI key is never more than a viewer, whoever minted it, and a CI key is read-only. A key is never minted above the role of the person who minted it.
* **Browsers never hold an API key.** Your server calls `POST /v1/sessions` and hands the browser a token bound to one room. It expires in 30 minutes and lets the browser do nothing but join that conversation ([Authentication and credentials](https://docs.sempera.org/authentication.md), [Sessions and calls](https://docs.sempera.org/sessions-and-calls.md)).
* **People sign in to the App, not the API.** The App's session cookie is host-only, `HttpOnly` and `Secure`, carries a CSRF check on writes, and is not a credential for the API host ([Authentication and credentials](https://docs.sempera.org/authentication.md)).
* **Rate limits** are 600 requests a minute per key and 60 a minute per address for routes that need no credential ([Errors and limits](https://docs.sempera.org/errors-and-limits.md)).

If a key may have leaked, revoke it first. An owner can do that with `DELETE /v1/api-keys/{id}` or in the Sempera App, then mint a replacement.

## Secrets in tools and connections

A tool's credential is a reference to a named workspace secret, never a value. A connection's provider credentials are never returned by any read, a published agent version never shows a secret, and the App does not display a secret again after it is saved ([Tools and integrations](https://docs.sempera.org/tools-and-integrations.md), [Secrets and request signing](https://docs.sempera.org/secrets-and-signing.md)). A secret you delete takes its stored value with it.

## Signed requests and signed events

* **Outbound events** carry `X-Sempera-Signature`, an HMAC-SHA256 over `{t}.{body}` with a five-minute tolerance. Verify it with a constant-time comparison before you trust the body. The recipe and a known-answer vector are in [Webhooks](https://docs.sempera.org/webhooks.md).
* **Delivery is guarded.** Before an event is sent, the destination host is resolved and the connection is pinned to the address that was checked. An address inside a private network is refused and a redirect counts as a failed attempt. An endpoint URL with a private address literal, embedded credentials or a bad port is refused when you save it ([Webhooks](https://docs.sempera.org/webhooks.md), [Migration and changelog](https://docs.sempera.org/changelog.md)).
* **Requests Sempera makes in your name** can also carry a short-lived signed token that you verify against a published key set. It is an owner setting ([Secrets and request signing](https://docs.sempera.org/secrets-and-signing.md)).

## Calls, recordings and consent

* **Disclosure.** Every agent says plainly that it is an AI when a caller asks whether it is a person, and never claims to be one. Whether it also says it is an AI virtual assistant at the start of every call is the agent's AI disclosure setting, off by default and chosen by the workspace owner, who is responsible for the disclosure laws that apply to their calls. When calls are recorded, the opening always carries the recording notice, whatever that setting says ([Migration and changelog](https://docs.sempera.org/changelog.md)).
* **Recording is the workspace owner's choice.** Whether calls are recorded, how long a recording is kept, legal holds and deletion are owner settings in the App. The API reports the state of each recording ([Call recordings](https://docs.sempera.org/recordings.md)).
* **Playback is gated.** `GET /v1/recordings/{recording_id}` signs a link valid for 5 minutes, only for a recording your workspace owns. The link works for the signed-in member it was issued to and nobody else. An API key never receives audio. Every link issued and every play is recorded in the workspace's audit log.
* **Outbound calls need a stated basis.** `POST /v1/calls` records a `consent_basis`, refuses a number on the workspace's do-not-contact list, and respects the call limits the owner set ([Sessions and calls](https://docs.sempera.org/sessions-and-calls.md), [Owner features in the App](https://docs.sempera.org/app-features.md)).
* **Deleted means gone from the reads.** An export never contains a deleted transcript, and the stored bodies of `call.completed` and `call.outcomes` deliveries lose the words read from a call once its transcript is deleted ([Owner features in the App](https://docs.sempera.org/app-features.md), [Migration and changelog](https://docs.sempera.org/changelog.md)).

## Public pages hold no workspace data

These pages are public and need no key. Every example in them is invented: a demonstration agent, made-up ids and digests, numbers in the reserved fictional range, and placeholders where a secret would go. If you find a real customer name, number or key in any page, report it as described below.

## Reporting a problem

Write to [hello@sempera.org](mailto:hello@sempera.org). That mailbox is the contact named in the privacy policy and in the OpenAPI document. Include the `X-Request-Id` of the response you are asking about, which every response carries, and describe what you saw. Do not send secrets, call audio or personal information in the first message. This page promises no response time and no reward programme.

## What this page does not claim

It does not claim any compliance certification or audit report. It does not state where stored data physically sits: the privacy policy names the categories of provider involved and says what is and is not guaranteed about location. It does not describe internal systems, and it is not a substitute for the terms you accepted.


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