Data tables
Structured storage your agents can read from and write to — fields, personal data handling, and per-agent permissions.
A data table is structured storage your agent can read from and write to during a conversation — like a spreadsheet the agent can use. Each table has a name, a permanent slug, typed fields (columns) and records (rows).
Common uses: leads, support tickets, orders, bookings, and anything else an agent needs to capture or look up.
Creating a table
Select Data in the sidebar, then create a table and add fields. Then open the table's Permissions tab and grant your agent access — an agent cannot see a table until you do.
Field types
| Group | Types |
|---|---|
| Basic | text, long_text, number, currency, date, datetime, boolean, json, url |
| Selection | single_select, multi_select, status |
| Identity / personal data | email, phone, name, nric, address, whatsapp_id, telegram_id, telegram_handle |
| System | auto_increment, uuid, contact_id |
| Structural | relation (links to another table), asset_id (references an uploaded file) |
A field's slug and type are permanent once created — they are the key its values are stored under. The table's display name, description, icon and colour can change at any time; the table's slug cannot.
Personal data and encryption
The identity field types above are treated as personal data and are automatically redacted from audit logs, whether or not the field is encrypted.
A subset additionally supports field-level encryption: text, long_text, email,
phone, url, name, nric, address, whatsapp_id. Note that telegram_id and
telegram_handle are personal data but cannot be encrypted.
When a field is created with encryption on:
- Values are encrypted at rest and never returned in plaintext to an agent or a dashboard user.
They are masked on read (
j***@x.com) and revealed only through an explicit, audited action. - Search on that field supports exact match only. Partial matches, "starts with" and range queries are not possible.
- The choice is permanent. Encryption cannot be switched on or off after the field exists.
Because of the search tradeoff, encrypt deliberately. Sensitive identifiers like nric,
address and whatsapp_id are worth encrypting; a name you need to search by prefix may not
be.
Permissions
Access is granted per agent, per table, as four gates: read, create, update, delete. Finer control — which fields an agent can see, whether it can search all rows or only its own, export limits — is set by an admin on the table's Permissions tab. It cannot be changed from a conversation.
Access modes
Read, write and delete each have an access mode:
| Mode | Who it is for |
|---|---|
| Self (own records only) | Customer-facing agents — each end user only sees rows belonging to them. Requires at least one row scope rule |
| Scoped | Internal agents limited to an admin-defined slice, e.g. one branch |
| Table | Broad row access for trusted internal agents, still subject to field limits and per-query caps |
| Analytics (read only) | Aggregate and count queries only; individual rows are never returned |
Row scope rules
A scope rule ties rows to the person currently chatting by comparing a field against the live identity of the conversation — contact ID, WhatsApp ID, Telegram ID or handle, user ID.
contact_idequals the current contact ID — matches rows created for this contact.whatsapp_numberequals the current WhatsApp ID — matches imported rows keyed by the customer's number, including rows that existed before they ever chatted.
Scope rules are enforced on the server and fail closed: if the required identity is not available in that conversation, access is denied rather than opened up.
In dashboard test chat the identity is the logged-in operator's own contact, so a WhatsApp-keyed rule only resolves there if that contact has a linked WhatsApp number.
Scoping through a related table
When a child table links to a parent via a relation field — for example
policies.client_id → clients — the child can be scoped through the parent, so you do not
have to copy the customer's identity onto every child row.
Read the rule as: allow a child row when its relation points at a parent row owned by the person currently chatting. A customer asking "show my policies" has their client record found by WhatsApp number, and only policies linked to that client are returned.
Constraints:
- The relation must be a single-value relation. Multi-value relations cannot be used for scoping, and the dashboard rejects invalid configurations when you save.
- When the person matches exactly one parent record, the relation is stamped automatically on create — the agent cannot point a row at someone else's parent record.
- Matching no parent record returns zero rows, not an error.
- Matching several parent records means the agent must specify which one when creating.
Adding records
Records can be created by an agent during a conversation, added manually in the dashboard, or brought in from a file — an agent can preview and import records from an attached spreadsheet.
Protecting personal data from the agent
Blocking a field prevents both reading and writing it. If you want an agent to capture personal data but never read it back — the right default for a public-facing agent — restrict it on read only. Fields like contact name, email, phone and messaging IDs should be read-blocked on any agent the public can talk to.