Skip to content

How is my customer data protected?

Your contacts, your calls and your evidence are fenced off inside the database itself, so one bug in application code cannot hand another customer your rows.

Every contact, call, transcript and evidence row we hold for you carries your tenant id, and Postgres row-level security decides what a query may see before our code gets a vote. The service connects as a database role that owns no table and cannot bypass those policies, which is the whole point: the enforcement sits underneath the application rather than inside it. At startup the service inspects that role and refuses to serve if it is wrong, so a misconfigured deployment fails at boot instead of failing on your data.

Nothing in the codebase reads your contacts, your calls or your evidence without a tenant bound to the transaction, and a query that reaches those tables with no tenant bound raises instead of returning rows. The failure mode is an error on our side, never another customer's contacts on yours. A test that gates every deploy proves it on a reused connection: bind one tenant, run its work, hand the same pooled connection to the next caller with nothing bound, and the database raises rather than serving the last tenant's rows. A pooled connection is exactly where this breaks quietly, so that is the case the test pins.

Credentials you connect are held as envelope-encrypted blobs under AES-256-GCM, keyed from the secrets manager at deploy. The table stores ciphertext and an address, never a usable secret: the half that can decrypt cannot reach the database, the half that reads the database cannot decrypt, and CI fails a build that lets those two halves import each other. Our own provider secrets are injected at task launch and sit in no repository, no image and no CI variable.

The record behind your calls is append-only in the database rather than by policy. No role holds UPDATE or DELETE on the audit table, so the application tier cannot rewrite history even by accident, and a correction is a new row pointing at the one it corrects. Per-call evidence, consent records and signed attestations are kept on a five-year retention class, because the file that answers a question about a call from three years ago has to still be there. A recording carries its own expiry, stamped 90 days out at the moment it is written, and a nightly job deletes the audio when that date lands. Your Data Processing Addendum governs retention, and it is published in full before you sign it.

Access is the last layer. An API key is stored as a SHA-256 hash with a short display prefix, shown in full exactly once, scoped per route and rotatable in place; test and live credentials are separated at the credential level, and a test credential cannot originate a live dial. Passwords are argon2id with a deployment pepper, one comparison revokes every session on every device, and each staff action on the admin surface writes its own audit row. Send a security questionnaire to support@capstralabs.com and you get written answers about these controls from the people who built them.

Check it yourself, before anybody signs anything

The sandbox is free and needs no card. Build the agent, run the whole dial path with every compliance gate live, and read the file it writes.