Tamper-evident audit log
previous_hash → entry_hashEvery audit row carries the hash of the previous row plus its own. Twenty-plus event types — reads, writes, deletes, logins, customer-data access, config changes, flow execution. Append-only by convention: only INSERT and SELECT, never UPDATE. Default retention 7 years (the SOC 2 minimum); auditors don’t wait on us — every tenant queries its own audit history through GET /admin/compliance/audit.
Application-layer key encryption
AES-256-GCM · key outside DBCustomer API credentials are encrypted at the application layer with AES-256-GCM. The encryption key is a deployment-held secret, kept outside the database. Plaintext is zeroized on drop. Other data relies on storage-layer encryption — see §04 for the honest two-tier picture.
Logical multi-tenant isolation
WHERE client_id = $1Every data query is scoped by client_id at the query layer. RBAC middleware extracts the client id from the request path and validates it against the JWT claims before the handler runs — a token issued for tenant A cannot read tenant B’s data even if the path is forged. Logical isolation in a shared database; physical separation available on request.
Configurable retention with scheduled purges
data_retention_policiesPer-tenant retention windows in data_retention_policies. A RetentionEnforcer actually runs scheduled purges (interval and batch size both configurable), so retention isn’t just a written policy — it’s enforced. Defaults seeded conservatively; every row is overridable. Full table in §03.
GDPR Article 15 + 17, real handlers
/dsar · /erasureSubject access request (Article 15) exports cases, interactions, emails, voice calls, auto-response log, and audit entries. Erasure (Article 17) anonymizes by SHA-256 hash replacement of email, phone, and name in a single transaction. Both endpoints are SuperAdmin-only and write an audit row of their own.
JWT auth, configurable
HS256 / RS256 · default 24hJWT auth with HS256 (RS256 also supported), default 24-hour expiry, configurable per environment. Audience is configured per environment, not per tenant. Rate limits are configurable per API client and per dashboard user.