PPactDocs
Team & Org

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 specific email, phone, or customer_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 time T.
  • 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 via POST /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 a drop profile into a keep profile; 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.