The contact record model
Every message you send, every campaign and flow you build, every inbox conversation, and every segment you define resolves to a contact: one row per person in your tenant, holding who they are, where they can be reached, and what they consented to. This page defines that row. The surrounding pages each cover one mechanism around it; this one covers the record itself.What a contact is
A contact is the canonical identity anchor for a person inside one tenant. Other pillars reference it by id and never copy it:- A campaign or batch send selects contacts (directly or through a segment) and resolves a reachable address per channel at send time.
- A flow branches on contact fields and writes back to them.
- An inbox conversation is attached to a contact so an agent sees the person’s history, not just the thread.
- The CDP layers traits, scores, and segments on top of the record.
- Billing meters outcome usage against the tenant, attributed per contact touch.
first_name,
last_name, display_name, company, country_code, timezone,
language), the channel addresses in the next section, your
external_id for the person, tags, and any custom fields you defined. It
deliberately does not hold message history, call records, or event
streams; those live on their own surfaces and join back to the contact id
(see Timeline and journey).
How this page relates to the fragments around the record:
- Contact merge policy defines how two contact rows collapse into one survivor.
- CDP identity resolution defines how likely-matches are proposed at scale before any merge runs.
- Customer 360 snapshot model defines the read-composed dashboard view over the record.
- Audience overview maps the operator console where you browse and edit contacts.
Identifiers
Every contact carries one immutable system id, assigned at creation:cnt_ followed by 32 lowercase hex characters, for example
cnt_4f9a2c81d03b4e6a7f1c2b3d4e5f6071. The id is never reassigned and
never reused: when two records merge, the survivor keeps its id; when a
record is erased, the id is tombstoned.
External identifiers index the record from the outside:
Uniqueness is per tenant and per identifier type. Within a tenant, a
normalized email or phone identifies at most one live contact for
matching purposes: an import that arrives carrying an email or phone an
existing contact already holds resolves to that contact instead of
creating a second row, and the server-side duplicate scanner groups on
normalized email and phone (see
Duplicates).
external_id is the
key we recommend your integrations store: unlike an address, it never
changes when the person changes handset or inbox, so it keeps resolving
after merges and re-imports.
Channel addresses
One contact holds several reachable addresses at once: an SMS-capable phone number, a WhatsApp identity, an email address, a Viber identity, and push tokens. A send on a given channel resolves to that channel’s address on the record; when more than one candidate exists, the contact’s channel preferences and your messaging services decide which address is used (see sender resolution and message send request lifecycle). Consent attaches per channel address, not to the record as a whole. A contact who opted in on SMS and never on email is sendable on one channel and suppressed on the other. The consent state, the quiet-hours and frequency-cap checks, and the suppression lists that enforce it are defined in consent and suppression model; this page only fixes where consent lives: on the channel address of a specific contact, never implied by the contact existing.Lifecycle
A contact moves through four states:- Created. Creation paths that write a contact row:
POST /api/v1/contactsfrom your backend or an SDK.- CSV and CRM imports, which upsert: a row whose identifier matches an existing contact is updated, not duplicated (see imports migration model).
- Inbound auto-create: a message or call from an address your tenant has never seen resolves to a new contact so the conversation has a person attached (see inbound message resolution).
- Form and widget captures, and CRM sync connectors.
- Active. The normal state. Profile fields, tags, custom fields, and addresses are editable from the dashboard, the API, imports, and enrichment.
- Merged-into. A duplicate is folded into a survivor. The secondary id stops being a live record and is kept as an alias that resolves to the survivor, so stored references keep working (see Duplicates).
- Erased. A GDPR erasure tombstones the row (see Erasure and retention).
Duplicates: two collapse mechanisms
Two different mechanisms remove duplicate records, and they fire at different points:- Deterministic merge collapses one pair (up to 20 secondaries per request) into one survivor, per-field, with an audit row and a 30-minute undo window. You drive it: the duplicate scanner proposes groups on normalized email and phone, and you or your code post the merge. This is contact merge policy.
- Probabilistic identity resolution proposes likely-matches at scale from the CDP side, feeding a review queue instead of merging directly. This is CDP identity resolution, backed by the identity and device graphs.
Enrichment
Enrichment is an overlay on the record, not a second record. The enrichment surface derives traits a sender did not provide (for example a timezone resolved from a phone number) and next-best-action fields used by decisioning, and writes them onto the contact so every reader sees them. Two rules keep the overlay honest:- Enrichment does not silently overwrite values an operator or your API set by hand; manual edits keep precedence and carry their own history.
- Enriched traits are readable and filterable exactly like imported fields, so segments and personalization slots do not need to know where a value came from.
Timeline and journey
The per-contact timeline assembles the events that reference the contact id into one ordered feed: sent and received messages, calls, campaign touches, consent changes, and operator notes. The journey view is the campaign- and flow-shaped cut of the same events: which sends reached this person, with what outcome. Neither is stored as a copy on the contact row; both are read-time assemblies over the event surfaces, which is why a message deleted or retired by a retention window disappears from the timeline too. The Customer 360 snapshot is the third read over the same record: it composes the timeline, consent state, scores, and open conversations into the drawer an agent sees. Timeline and 360 differ only in composition, not in source data.Bulk operations and limits
For more than a handful of records, use the bulk surface instead of looping single-record calls:- Bulk tag, field update, and lifecycle-stage changes accept up to 50,000 contact ids per request.
- Tag writes carry at most 25 tags per contact, each tag up to 50 characters; name fields cap at 200 characters and timezone at 64.
- Bulk operations are idempotent by construction where it matters: re-applying the same stage change to a contact already in that stage is a no-op, so a retry after a timeout does not double-apply.
- CSV-scale ingestion runs as import jobs with their own history and matching rules rather than as one giant request (see imports migration model).
Erasure and retention
Erasure is the terminal state and is treated as a legal operation, not a delete button: the request requires re-authentication, the profile fields and channel addresses are scrubbed, references in histories are anonymized, and the id is tombstoned rather than recycled. Two things deliberately survive:- Suppression entries. The person’s addresses are projected onto the suppression surface so an erased contact can never be re-imported into a send. Suppression outliving the record is what makes the erasure durable (see consent and suppression model).
- Held records. Rows the law keeps independent of the contact, such as billing and consent audit trails, persist in their own surfaces under their own retention rules (see retention windows and deletion).
Worked example: one contact, four surfaces
- Inbound SMS creates the record. A text arrives from
+14155550100, an address your tenant has never seen. Inbound resolution creates a contact with that phone address, assigns acnt_id, and attaches the conversation. Surface: messaging inbound plus the contacts record. - Enrichment layers on. The enrichment surface derives a timezone from the number and writes next-best-action fields onto the same row. Surface: enrichment overlay; the record gains traits without a second row appearing anywhere.
- A CRM import lands a duplicate, and merge collapses it. Your
nightly import carries the same person as
external_id: c-8841with an email address. Import matching keeps both rows because no identifier overlapped; the duplicate scanner or identity resolution later proposes the pair; you postPOST /api/v1/contacts/mergewith the imported record as secondary. The survivor keeps itscnt_id, unions the email andexternal_idonto the record, folds the import’s conversation history in, and writes an audit row. Surface: imports, then the deterministic merge. - The person asks to be erased. You issue the erasure with re-authentication. The profile and addresses are scrubbed, the id is tombstoned, the phone and email are projected onto suppression, and the merge audit remains available for disclosure. Surface: erasure plus suppression; the timeline now shows an anonymized history.
Related route surface
See also
- Contacts API reference - every endpoint and payload shape around the record
- Contact merge policy - the deterministic collapse mechanism
- CDP identity resolution - the probabilistic matching mechanism
- Identity and device graphs - the graph internals behind resolution
- Customer 360 snapshot model - the composed read over the record
- Consent and suppression model - where per-channel consent and suppression live
- DSAR concept - export and erasure workflow
- Custom fields model - extending the record with tenant-defined fields
- Audience overview - the operator console for contacts