What we store, and what we never see.
Your device indexes and encrypts before anything leaves it. The server receives ciphertext and tokens, and returns them when you search.
The server stores
- Encrypted transcripts, metadata, notes and recordings
- HMAC tokens for keywords, contacts and tags
- Embeddings of redacted text
- The day of each call and a duration bucket
- Wrapped keys and public keys
The server never sees
- Transcript text or individual words
- Phone numbers or contact names
- Tag names, notes or rule definitions
- Your keys, unwrapped
- Decrypted recordings
Where plaintext goes.
Only for what you choose: a call you upload for transcription, a call you ask about, and the calls your translation rules cover. These routes stream straight through and never write to disk, storage or logs.
Audio of a call you upload. Where the provider issues single-use tokens, your browser sends the audio to it directly and our server never sees it; otherwise it streams through our server. With your own key, your device always sends it directly.
That one call and your question, after you agree in a dialog that names the receiver. The transcript is wrapped as untrusted data, so instructions inside it aren't followed.
The transcript of each call your translation rules cover, as it uploads, so you can search it in the languages you chose. You set the rules in Settings → Providers, and the upload screen says it will happen; there is no prompt per call. With no rules nothing is sent. The transcript is wrapped as untrusted data, and the translation comes back to your device and is encrypted with the call. Your searches never leave your device.
A transcript a remote AI tool sends for you is visible to our server in transit, sealed at once to your inbox public key, and the plaintext is discarded. The app says so next to each such item.
What each provider keeps
The terms each adapter works under, as documented in its README in the repository.
| Provider | Retention |
|---|---|
| ElevenLabs | With zero retention turned on, requests carry enable_logging=false. ElevenLabs accepts it only on Enterprise plans; with it off, ElevenLabs keeps request logs under its standard policy. |
| Deepgram | Requests carry mip_opt_out=true, opting out of Deepgram's Model Improvement Program; Deepgram then keeps the data only as long as needed to process the request. |
| OpenAI (transcription) | No per-request retention flag. API inputs aren't used for training by default; zero data retention is an account-level agreement with OpenAI. |
| Soniox | No no-retention flag. The adapter deletes the uploaded file and the transcription as soon as it has the transcript; Soniox's 30-day expiry is the backstop. |
| Anthropic | No per-request retention flag; retention follows the instance's agreement with Anthropic. |
| OpenAI (questions) | Requests send store: false, so the Responses API doesn't store them. |
| OpenAI-compatible, local | Ollama, LM Studio or a local Whisper server: requests stay on the network you run them on. |
Your keys.
One root key is created on your first device and never leaves a device unwrapped. Every other key comes from it.
Unlocking.
Each method keeps its own wrapped copy of the root key. Sign-up can't finish until you create a recovery code and type part of it back.
- Passkey
- Face ID, Touch ID, Windows Hello or a security key. The PRF extension yields 32 bytes the server never sees.
- Recovery code
- 8 groups of 4 characters, shown once. 128 bits of randomness, so no slow derivation is needed.
- Passphrase
- Only where the browser lacks PRF. Argon2id at 64 MiB, at least 12 characters.
- Locking
- After 15 idle minutes and on tab close. Keys and decrypted text are dropped from memory.
Linking a device.
The local agent or another browser receives the root key straight from your existing device, encrypted to a key made for it. The server only relays.
- In Settings › Devices, choose Link a device to get a short code and a QR code.
- The new device makes a key pair and posts its public key.
- Both devices show a 6-digit fingerprint. You confirm they match.
- The trusted device sends the root key encrypted to the new public key.
- The link expires in 10 minutes, is written to the audit log and can be revoked.
Verify what you run.
AskCalls builds reproducibly. Every release publishes the SHA-256 of each client JavaScript asset, so you can check that askcalls.com serves exactly the code in the repository. App routes load no third-party scripts and run under a strict CSP.
$ curl -s https://askcalls.com/_next/static/chunks/[asset].js | shasum -a 256 $ grep [asset].js SHA256SUMS # the two hashes match
The leak test.
0 seeded words and phone numbers found in a full dump of the database and the storage bucket.
Every pull request runs it: seed calls with known words and numbers, dump the entire database and bucket, and search for them in UTF-8, UTF-16 and every normalized and token form. If anything turns up, the build fails.
Threat model and reporting.
AskCalls protects your calls from the server and anyone who gets at it. It can't protect what an unlocked device shows: a compromised device that is unlocked can read it. Report a vulnerability privately through the repository's security advisories, not in a public issue.