Manage Contacts
CRUD and reverse-lookup endpoints for the organization's contact directory. All contacts are org-scoped; the caller's organization is derived from the auth context.
Create contact POST
POST /contactsCreate a new contact. At least one of given_name, family_name, company_name, or preferred_name must be non-empty (the vCard FN rule). If preferred_name is omitted it is synthesized from the other name fields.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
preferred_name | string | null | No | Display name (max 255 chars). Synthesized if omitted. |
name_prefix | string | null | No | e.g. "Dr." (max 32) |
given_name | string | null | No | First name (max 128) |
middle_name | string | null | No | Middle name (max 128) |
family_name | string | null | No | Last name (max 128) |
name_suffix | string | null | No | e.g. "Jr." (max 32) |
company_name | string | null | No | Company (max 255) |
job_title | string | null | No | Title (max 255) |
birthday | string | null | No | ISO YYYY-MM-DD |
notes | string | null | No | Free-text note on the contact |
emails | object[] | No | Up to 50 items. See Contact object. |
phones | object[] | No | Up to 50 items (E.164 values) |
websites | object[] | No | Up to 25 items |
dates | object[] | No | Up to 25 non-birthday dates |
addresses | object[] | No | Up to 10 postal addresses |
custom_fields | object[] | No | Up to 50 free-form label/value pairs |
Request example
Response (201)
Returns the created contact with memory_count: 0 and latest_memory: null (see Contact object).
Error responses
| Status | Description |
|---|---|
| 400 | vCard FN rule violated — no name fields provided |
| 422 | Invalid email, non-E.164 phone, duplicate email/phone, or more than one is_primary |
Code examples
List contacts GET
GET /contactsList contacts. Contacts are organization-wide, so every agent in the org sees every contact.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | — | Case-insensitive substring match over name, company, job title, and the notes field. Max 100 chars. |
order | string | name | "name" (preferred_name ascending) or "recent" (created_at descending) |
review_status | string[] | All | Repeat the parameter to include "unreviewed", "confirmed", or both. For example, ?review_status=unreviewed&review_status=confirmed. |
limit | integer | 50 | Results per page (1–200) |
offset | integer | 0 | Offset for pagination |
Response (200)
Returns an array of contact objects, each with memory_count and latest_memory for its active generated memories.
Code examples
Lookup contact GET
GET /contacts/lookupReverse-lookup contacts by a single field. Useful for routing inbound email or phone calls to a known contact.
Query parameters
Exactly one of the following must be provided:
| Parameter | Type | Description |
|---|---|---|
email | string | Exact match against a stored email (canonicalized to lowercase) |
email_contains | string | Substring match on stored email values (case-insensitive) |
email_domain | string | Suffix match on @<domain>. Leading @ tolerated (max 253 chars) |
phone | string | Exact match against a stored E.164 phone number |
phone_contains | string | Substring match on stored E.164 phone values |
Response (200)
Returns an array of contact objects (up to 200). An empty array means no match.
Error responses
| Status | Description |
|---|---|
| 400 | Zero or more than one filter provided; or phone is not a valid E.164 number |
Code examples
Get contact GET
GET /contacts/{contact_id}Fetch a single contact by its UUID.
Path parameters
| Parameter | Type | Description |
|---|---|---|
contact_id | UUID | Unique contact identifier |
Code examples
Update contact PATCH
PATCH /contacts/{contact_id}Update a contact using JSON-merge-patch semantics:
- Omit a field to leave it unchanged
- Send a value to set it
- Send
nullon a nullable scalar to clear it - Send a list to replace the stored list wholesale (no per-item merge)
After applying the patch, the server re-runs the FN rule against the merged state — if the update would leave every name field empty, the request 400s.
The response includes the contact's current active memory_count and latest_memory.
Path parameters
| Parameter | Type | Description |
|---|---|---|
contact_id | UUID | Unique contact identifier |
Request body
Any subset of the Create contact body fields. Organization administrators can also send review_status: "confirmed" to confirm an unreviewed contact. Agent-scoped callers cannot set review_status, and confirmed contacts cannot transition back to unreviewed.
Editing profile fields as an organization administrator also confirms an unreviewed contact. Confirmation records reviewed_at and reviewed_by in the response.
Code examples
Delete contact DELETE
DELETE /contacts/{contact_id}Delete a contact. Returns 204 No Content on success.
Path parameters
| Parameter | Type | Description |
|---|---|---|
contact_id | UUID | Unique contact identifier |
Code examples
Contact object
Top-level fields
| Field | Type | Description |
|---|---|---|
id | UUID | Unique contact identifier |
organization_id | string | Owning organization |
preferred_name | string | null | Display name — always non-empty in responses |
name_prefix | string | null | |
given_name | string | null | |
middle_name | string | null | |
family_name | string | null | |
name_suffix | string | null | |
company_name | string | null | |
job_title | string | null | |
birthday | string | null | ISO YYYY-MM-DD |
notes | string | null | Free-text note |
emails | object[] | See Email item |
phones | object[] | See Phone item |
websites | object[] | See Website item |
dates | object[] | See Date item |
addresses | object[] | See Address item |
custom_fields | object[] | See Custom field item |
creation_source | string | How the contact was created: manual, vcard, communication, or backfill |
review_status | string | unreviewed or confirmed |
reviewed_at | string | null | ISO 8601 time of confirmation |
reviewed_by | string | null | Principal that confirmed the contact |
preferred_name_source | string | Source of the display name: manual, vcard, provider, mail_header, or identifier_fallback |
preferred_name_locked_at | string | null | ISO 8601 time after which generated updates cannot replace the preferred name |
created_by_identity_id | UUID | null | Agent identity that created the contact, when applicable |
merged_into_contact_id | UUID | null | Surviving contact ID when this contact has been merged |
is_auto_created | boolean | Whether the contact was created from communication history |
is_confirmed | boolean | Convenience boolean equivalent to review_status === "confirmed" |
status | string | active |
created_at | string | ISO 8601 |
updated_at | string | ISO 8601 |
memory_count | integer | Active generated memories. Returned by list, create, update, and merge responses; omitted by get and lookup responses. |
latest_memory | object | null | Most recently updated active memory as {id, content, updated_at}. Returned with memory_count; omitted by get and lookup responses. |
Email item
| Field | Type | Description |
|---|---|---|
value | string | Email address (stored lowercased) |
label | string | null | e.g. "work", "home" (max 64) |
is_primary | boolean | At most one email per contact is primary |
Phone item
| Field | Type | Description |
|---|---|---|
value_e164 | string | E.164 phone number (e.g. +15551234567) |
label | string | null | e.g. "mobile", "work" (max 64) |
is_primary | boolean | At most one phone per contact is primary |
Website item
| Field | Type | Description |
|---|---|---|
url | string | HTTP/HTTPS URL (max 2048) |
label | string | null | e.g. "personal", "linkedin" (max 64) |
Date item
| Field | Type | Description |
|---|---|---|
date | string | ISO YYYY-MM-DD |
label | string | Required free-string label (e.g. "anniversary") |
Address item
All fields are optional free text.
| Field | Type | Description |
|---|---|---|
street | string | null | Street line(s) |
city | string | null | |
region | string | null | State, province, or region |
postal | string | null | Postal or ZIP code |
country | string | null | Country name or ISO code |
label | string | null | e.g. "home", "work" |
Custom field item
| Field | Type | Description |
|---|---|---|
label | string | Field key (max 128) |
value | string | Field value (max 1024) |