Security
Security built into the foundations
Your conversations and your visitors’ data are protected by the database, the API and the widget, not by a single layer.
Tenant isolation
- Every organization’s data is separated by PostgreSQL row-level security. The application’s database role cannot bypass it, so a query without the right organization context returns nothing
- Organization ids come from the URL and are checked against your membership; non-members get “not found”, so organizations can’t be probed
- Automated tests attack every organization-scoped API operation from another organization on each change
Accounts and access
- Passwords hashed with Argon2id; refresh, email, invitation and visitor tokens stored only as SHA-256 hashes
- Access tokens last 15 minutes; refresh tokens rotate on every use, and reusing an old one ends the session
- Logging out, revoking a session, changing a password or a role takes effect immediately, including on open realtime connections
- Five roles with clear permissions and an append-only audit log of changes
- Rate limits on sign-in, sign-up, password and invitation flows; no account enumeration
The widget
- Runs in a sandboxed iframe on ZebChat’s own origin, isolated from your page
- Loads only on the domains you allow
- Messages between your page and the widget are checked for origin and shape
- Logged-in visitors can be verified with an HMAC signed by your server
- Per-IP, per-visitor and per-organization rate limits, plus optional Cloudflare Turnstile
Files
- An allow-list of file types; SVG and HTML are refused because they can carry scripts
- Uploads are signed for exactly the declared type and size, and checked before they are attached
- Storage stays private: downloads go through short-lived signed links
- Anything that isn’t an image downloads as an attachment instead of opening in the browser
API and webhooks
- API keys are stored hashed, carry scopes limited by their creator’s role and have their own rate limits
- Webhooks are signed with HMAC-SHA256 and a timestamp, so you can reject forgeries and replays
- Outgoing calls (webhooks, AI actions, knowledge crawling) are HTTPS-only and checked against private and internal addresses at every request
AI assistant
- AI provider keys are encrypted with AES-256-GCM and write-only: nobody can read them back
- Answers run on your own Claude or OpenAI account, under your agreement with that provider
- Responses from your action endpoints are passed to the model as untrusted data
- A monthly spend cap and per-chat limits keep usage under control
Privacy and retention
- Cookie-consent mode: the widget stores nothing and tracks nothing until the visitor agrees
- Export or erase a visitor’s data for access and deletion requests
- Retention settings for ended conversations and their files; old tracking data is removed automatically
- Internal notes never reach visitors, and visitors only ever see their own conversations
ZebChat staff access
- Staff actions are recorded in an append-only platform audit trail
- Sensitive staff actions require re-authentication
- Support sign-ins to a customer account are time-limited, shown with a banner and logged in that organization’s own audit log
Roadmap
What we are adding next Coming soon
These are planned and not available yet. We list them so you can plan around what exists today.
- Two-factor authentication (TOTP)
- Single sign-on (SSO)
- IP allow-lists for API keys
- An independent third-party penetration test
Disclosure
Report a vulnerability
If you believe you have found a security issue, email [email protected] with the subject “Security report” and the steps to reproduce it. Please give us a chance to fix it before telling anyone else, and don’t access data that isn’t yours.
Developers: the security and data handling page explains what the widget collects and how data flows. Our Data Processing Agreement describes our commitments as your processor.
Keys and secrets are never shown again after you create them.