# Data map

v1 region: **United States**. Processor: Say Hi Apply. Controller: the client account.

| What | Where | Who | TTL |
|---|---|---|---|
| Client email, session, CSRF hash | Neon (US) | Client | While the account is open |
| Embed key hash, `allowed_origins` | Neon | Client | While the key exists |
| Intro metadata (name, coarse place, consent_version, apply email on the link door only) | Neon | Client | 30 days after record, then tombstone |
| Media (R2 object key, not a public URL) | Cloudflare R2 (private, ENAM hint) | Client | 30 days, then hard-delete |
| Ingest copy of a take, pre-convert | Cloudflare R2 (`ingest/` prefix) | Client | Deleted as soon as the convert lands; lifecycle rule sweeps at 1 day |
| Audit (watch / download / delete / staff / webhook) | Neon | Client | ~12 months. No media. |
| Credit lots / events | Neon | Client | FIFO, 12-month lot expiry |
| Webhook signing secret | Neon (account) | Client | Until rotated |

Never stored: face embeddings, voiceprints, scores, card PAN, candidate login, a public file URL.

**What "region" is a promise about, and what it is not.** Metadata is the strong half: Neon runs in `us-east-1`, and the Worker's `placement.region` keeps fetch handlers in the Cloudflare PoP nearest it, so intro rows and audit never leave US East. Media is the softer half. The R2 bucket is created with an **Eastern North America** location hint, which is best effort — Cloudflare places the bucket in that region but does not contractually bind itself to it, and ENAM spans Canada as well as the US.

R2 does offer a hard guarantee, called a jurisdiction restriction, and we deliberately do not use it: a jurisdiction-restricted bucket cannot be listed or administered in the Cloudflare dashboard at all, so CORS, lifecycle, metrics, and object inspection would be CLI-only for the life of the product. That trade is worth revisiting for a client who needs residency in a contract, but it is a bucket that cannot be migrated after creation, so it would mean a new bucket and a copy — not a setting change. Say "primary location", not "stored in", when describing where media sits.

City fill (`GET /v1/place`) is a lookup, not a row. Coarse GPS of someone who allowed location is sent to BigDataCloud for a city name. The city that lands on the intro is whatever they Send (filled or typed).

The person on camera has no account. Rights (delete, export) go through the client.
