Security model
The rules Postgres enforces, whatever the app or an API client sends.
The app hides what you can't use, but hiding is not the protection. Every rule below is enforced by the database with row level security, column permissions, triggers or checked functions, and each one has a test that tries to break it (pnpm test:rls, see Tests).
Product tables start with cw_ and every row belongs to one workspace. Access is decided by one function, cw_can_access(workspace, module, level), used by every policy.
The rules
| # | Rule | How Postgres enforces it |
|---|---|---|
| 1 | Only billing can change a workspace's plan, status, period or pool allowance. | Signed-in users can read cw_subscriptions but have no write permission on it. |
| 2 | Nobody unlocks a module without paying. New modules start locked; switching on a locked module fails. | access_state can't be written by users; owners may only update enabled, and a trigger refuses it for locked modules. |
| 3 | Publishing a script needs publish on Receptionist. | Versions are only created by cw_publish_agent(), which checks the level. Direct writes to versions or the published pointer are refused. |
| 4 | Call records can't be rewritten from the app. Only the review mark and flag change, and the reviewer is always you. | Column permission on review fields only; a trigger sets the reviewer and time from the signed-in user. |
| 5 | Users can't create calls or transcript lines. | No insert permission. Only the voice service, through the signed webhook, writes them. |
| 6 | A module set to none, or switched off, returns no rows. | cw_can_access in every read policy. |
| 7 | One workspace never sees another's data. | cw_can_access checks membership first. |
| 8 | People can't raise their own access level. | Only owners and admins edit access; admins can't touch the owner's rows. |
| 9 | Audit entries are written only as yourself. | Insert policy requires actor_type = 'user' and your own user id; no updates or deletes. |
| 10 | A read-only workspace can't be changed. | cw_can_access caps every level at view for read-only modules. |
| 11 | Erasing a caller, and routing a phone number, need publish. | cw_erase_caller() checks the level; the update policy on phone numbers requires publish. |
| 12 | Notes belong to their author. | Inserts must use your own user id; you can only edit or delete your own notes. |
Other protections
- Service role. The key that bypasses these rules is used in one server file only (
apps/web/lib/callwise/service-role.ts), for billing and the voice webhook. - Voice webhook. HMAC-signed, timestamp checked within 5 minutes, idempotent on event id.
- Recordings. Private bucket; a file is readable only with
viewon Receptionist in its workspace; no uploads from the app. - Outgoing webhooks. Destination URLs pass a server-side guard before anything is sent.
- Demo mode can't run in production without an explicit
ALLOW_DEMO_IN_PROD=true, and never makes an outside request.
What is not covered yet
No external security audit or penetration test has been done, and Callwise holds no compliance certification. See Data and privacy.
Voice service contract
The signed webhook the voice service calls to create calls, add transcript turns and close calls.
Licensing
Callwise is proprietary software for private use only. Copyright (c) 2026 Techsy Yapay Zeka Ve Yazılım Teknolojileri Anonim Şirketi (Istanbul, Türkiye). All rights reserved.