Contacts
The per-project contact book — automatic capture, manual create and CSV import, and what never happens on its own.
Every project has a contact book: Dashboard → Contacts. It lists everyone
who reached the project with an email address, one row per
(project, email) pair, newest first. Rows are captured server-side and
automatically; a project admin can also add rows by hand from the same
page — a Create contact form (email required; name and gender optional)
and a CSV import (a header row with an email column; name columns are
mapped when present, at most 500 rows per import, and the result reports
created vs. already-present vs. invalid rows).
The two capture paths
Capture never blocks the thing that triggered it
Both paths treat the contact write as a best-effort side effect. A failed contact insert never fails the form submission or the card — the message or card is stored either way, and the miss is only logged.
Form submissions
A submission to a form endpoint captures the submitter when it carries an email address:
- On a form with a builder schema, an email-typed field names the address authoritatively.
- On a legacy schema-less endpoint, the
email/from/senderkey heuristic that already routes the submission decides.
The submission's name field, when present, fills the contact's name.
Public kanban cards
A card created on the public board by a signed-in visitor captures that visitor's session address; their name comes from auth metadata, never from the email's local part.
A visitor who is not signed in must still provide a name and a valid email to
create a card — see
set_kanban_public_editing — but that
guest-typed address stays on the card row only; it is not captured into the
contact book (capture reads the signed-in session's identity, never the
typed field).
Capture semantics
- First capture wins. An address that is already in the book is left alone — a repeat submission (or a CSV row for an existing address) never overwrites a name the project owner may have edited.
- Names are never guessed. A missing name stays empty; it is never derived from the email local part, so a nameless capture shows as the bare address, not as "jdoe".
- Gender is never inferred. Auto-capture leaves the field empty until an owner sets it.
- Each row records its source —
formorkanbanfor auto-capture,manualfor a dashboard create or CSV import,mcpfor a contact created by an agent — and where it came from (the form endpoint, or the board and card).
API surface: MCP tools
Agents can read and edit the contact book over MCP:
list_contacts (contacts ≥ read), plus create_contact,
update_contact, and delete_contact (contacts ≥ write, with a
two-phase confirm on delete). Each tool is documented on the
MCP page.
There is still no dedicated REST endpoint and no export for contacts. This describes current behaviour, not a promise either way.