Profiles
Pact's canonical identity spine — one deduplicated profile per person, its source identities, time-travel history, and manual or automatic merges.
Profiles
A profile in Pact is the canonical, deduplicated record of a person — the single spine that ties together every email, phone, and external customer ID that resolves to the same human. When two contacts turn out to be the same person, or a webhook delivers an identity you've seen before under a different address, the profile graph is what keeps them merged and keeps their history intact.
Live
The identity spine is served by the /v1/profiles API
(api/routes/profiles_graph.py) over the canonical_profiles,
source_identities, and profile_attribute_provenance tables. Graph logic
lives in core/profiles_graph/ (identity graph, merge, time-travel, attribute
provenance). Reads require contacts:read + the data module; writes require
owner/admin.
Canonical profiles and their identities
Each canonical profile has a stable id, a primary_email, a primary_phone,
and a display_name. Hanging off it are N source identities — (kind, value) tuples such as ("email", "[email protected]") or ("phone", "+1…").
Any identity resolves back to exactly one canonical profile.
GET /v1/profiles— list profiles for the tenant.GET /v1/profiles/search— search by free text (q), or resolve a specificemail,phone, orcustomer_id.GET /v1/profiles/{id}— fetch one profile.POST /v1/profiles— create a fresh canonical profile (admin only).
Identities are managed with GET /v1/profiles/{id}/identities,
POST /v1/profiles/{id}/identities (link), and
POST /v1/profiles/{id}/identities/unlink. Email and phone values are
normalized on the way in so [email protected] and [email protected] resolve to
the same identity.
Time travel
Because every attribute write is recorded in profile_attribute_provenance,
the graph can reconstruct a profile as it existed at any past moment:
GET /v1/profiles/{id}/as-of?at=<timestamp>— the version of every attribute that was current at timeT.GET /v1/profiles/{id}/diff— what changed between two points.GET /v1/profiles/{id}/attributes/{attribute_name}/provenance— the full provenance trail for one attribute: every value, when it was written, and by which source. New values are recorded viaPOST /v1/profiles/{id}/attributes.
Merging
Two profiles that turn out to be one person are reconciled by merge:
POST /v1/profiles/merge— an admin merges adropprofile into akeepprofile; identities and provenance are re-pointed to the survivor.POST /v1/profiles/auto-merge— runs the automatic merge pass that collapses profiles sharing a strong identity signal.
Ingestion is idempotent
POST /v1/profiles/webhooks/ingest accepts an identity payload from an external
source and resolves-or-creates the canonical profile, writing an attribute
trail as it goes — so replaying the same event doesn't fork a duplicate person.
Not your account settings
This page documents the customer identity graph — the people your workspace
tracks. It is distinct from a signed-in user's own account profile and from the
tenant_users roster covered under Team.