# Data & Privacy

## Overview

Because WaDesk is self-hosted, your data lives entirely on your own server — nothing leaves your systems except the calls you make to WhatsApp, your payment gateway, and any AI provider you set up. Inside the app, three things keep that data safe: every secret is encrypted, each workspace is kept separate so customers can't see each other's data, and sensitive actions are recorded in an audit trail. A GDPR/CCPA cookie-consent banner covers the public-facing side.

## Encryption at Rest

Provider credentials and other secrets are encrypted with strong, industry-standard encryption (AES-256) before they're ever saved to the database. The following sensitive data is encrypted:

Data | Protection
--- | ---
WhatsApp provider credentials (Cloud API app secret, access token, and webhook verify token; Twilio SID and token; Unofficial API device info) | Encrypted
Per-workspace AI provider API keys (OpenAI, Anthropic, Gemini) | Encrypted
Admin / platform AI keys | Encrypted
2FA authenticator secret & recovery codes | Encrypted
Payment-gateway credentials | Encrypted
Integration tokens (Shopify, WooCommerce, HubSpot, Google) | Encrypted
Account passwords | bcrypt one-way hash (can never be read back)

Decryption fails gracefully: if a secret can't be unlocked (for example after the encryption key is changed), the app simply asks you to re-enter that key instead of breaking. Nothing crashes.

> Important: Everything above is protected by your app's encryption key (`APP_KEY`). Set a strong, unique key when you install, keep it out of source control, and back it up safely — if you lose it, every encrypted credential becomes unrecoverable, and changing it invalidates all stored secrets and 2FA setups. Never reuse the key across different environments.

## Per-Workspace Data Isolation

The workspace is the top-level boundary between customers. A user can belong to several workspaces and switch between them, and each one holds completely separate data — contacts, conversations, campaigns, flows, templates, devices, AI keys, and provider settings.

Every piece of data is tagged with the workspace it belongs to, and the app only ever reads data for the workspace you're currently in — so one customer's data can never leak into another's. Within a workspace, roles (owner, admin, agent, viewer) further limit what each member can see and do.

> Note: Platform staff work in a separate admin area and aren't put through the workspace gate, because workspaces are a customer concept. Customer data is reached through the workspace, not by staff browsing the database directly.

## Audit Log

Sensitive actions are recorded in a tamper-resistant, append-only audit trail. Each entry captures:

Recorded | What it captures
--- | ---
Level | Whether it was a platform-wide (admin) action or a workspace action
Who | The signed-in user who performed the action
Workspace | The workspace the action belongs to
Action | The event name (e.g. `billing.payment.captured`, `admin.security.policy_updated`)
What was affected | The specific record the action changed
Details | A label plus any extra context — with secrets stripped out, see below
Origin | The IP address and browser the request came from
Result | `success`, `warning`, or `failure`

A separate security log captures security-tab events — turning 2FA on or off, failed 2FA attempts, signing devices out, and password changes — with the same who / workspace / IP / browser context. Neither log can ever block the action it's recording: if writing the log fails, the action still goes through. The Admin Security dashboard presents these entries as a risk queue with key figures (open risks, blocked login attempts, 2FA coverage).

### Secret Scrubbing

As an extra safeguard, before any audit entry is saved, its details are scanned and any value that looks like a secret — anything labelled `password`, `secret`, `token`, `api_key`, `credential`, `private`, `access_key`, `client_secret`, `webhook`, `signing`, or `bearer` — is replaced with `[REDACTED]`. The label is kept so you can still see what changed, just never the secret value itself. This is a backstop to make sure a secret can never accidentally end up in an audit entry. A few fields that only hold field names (not the secrets) are left as-is.

## Audit Retention & Pruning

Old audit entries are removed automatically once they pass the retention window. The default window is 365 days, and you can change it under Admin → Security. If you'd rather run the cleanup by hand, these commands are available:

    php artisan audit:prune              # use the configured retention period
    php artisan audit:prune --days=180   # one-off: use 180 days instead
    php artisan audit:prune --dry-run    # report what would be deleted, delete nothing

Setting the retention to 0 turns pruning off entirely — useful where compliance requires keeping records permanently. The cleanup deletes in small batches so it can't overwhelm a large database.

> Important: Choose a retention period that matches your compliance and legal-hold requirements before going live. Pruning is permanent — run the `--dry-run` option first to see how much would be deleted, and make sure your database backups capture any audit records you might need beyond the retention window.

## GDPR Cookie Consent

The public site shows a GDPR/CCPA cookie-consent banner. It has two parts: a compact banner (a bottom bar, top bar, or centered box, depending on the admin setting) with Decline / Customize / Accept All, and a detailed preferences box with three categories — Strictly Necessary (always on), Analytics, and Marketing.

The visitor's choice is saved in a cookie named `wadesk_cookie_consent`. On every page, only the trackers for the categories the visitor allowed are loaded — so analytics and marketing scripts never run until consent is given. Admins set the banner text, style, policy links, and behaviour from the privacy settings page, and the banner respects the browser's Do Not Track signal when that option is turned on.

> Note: Set your Privacy Policy and Cookies Policy URLs in the admin privacy settings so the consent banner can link to them — required for a compliant disclosure.