The Profiles API exposes the profiles UMAP360 resolves from your events — list
and search them, read a profile's events and identity signals, and merge two
profiles. All routes are served under /v1-profiles and require a
read key (uk_read_); the merge
route requires an admin key.
GET https://YOUR_PROJECT_REF.supabase.co/functions/v1/v1-profiles
Authorization: Bearer uk_read_YOUR_READ_KEYRead & admin keys are server-side only
Unlike the write key, read and admin keys can access your data — keep them on your server, never in a browser. See Authentication.
Reads are rate-limited to 60 requests per minute and the merge to 10 per
minute, per (organization, client IP) — see Errors & rate
limits. An
invalid limit / offset returns 400; an unmatched route returns 404 Not found.
List profiles — GET /v1-profiles
| Parameter | Default | Notes |
|---|---|---|
limit | 50 | Max 100 (larger values are clamped to 100). |
offset | 0 | For pagination. |
identified | — | true / false to filter by whether the profile has a known userId. |
sort_by | last_seen_at | One of last_seen_at, first_seen_at, event_count, session_count, engagement_score, created_at. |
sort_order | desc | asc or desc. |
{ "success": true, "profiles": [ /* … */ ], "total": 1234, "limit": 50, "offset": 0 }Get a profile — GET /v1-profiles/:id
:id is either the profile's UUID or its canonical_id. Returns the profile
plus its active identity signals and 30-day stats — 404 Profile not found if it
doesn't exist in your org.
{
"success": true,
"profile": {
"id": "…",
"canonical_id": "…",
"identities": [ /* signal_type, signal_value, confidence, first_seen_at, last_seen_at, occurrence_count */ ],
"stats": { "total_sessions": 12, "events_last_30_days": 340 }
}
}Get a profile's events — GET /v1-profiles/:id/events
| Parameter | Default | Notes |
|---|---|---|
limit | 50 | Max 200. |
offset | 0 | Legacy offset pagination (returns an exact total). |
cursor | — | Opaque keyset cursor — recommended for deep pages. Takes precedence over offset. A malformed value → 400 Invalid cursor. |
event_name | — | Filter to a single event name. |
precise_total | — | 1 to get an exact total on the cursor path (otherwise it's estimated). |
The offset path returns an exact total; the cursor path returns next_cursor
has_moreand (by default) an estimatedtotal:
{
"success": true,
"events": [
{ "id": "…", "event_name": "Purchase Completed", "event_timestamp": "…", "properties": {}, "context": {}, "resolution_method": "…" }
],
"total": 340,
"limit": 50,
"next_cursor": "eyJ0cyI6…",
"has_more": true
}Page forward by passing the returned next_cursor as ?cursor= on the next
request; stop when has_more is false.
Read a profile's identities — GET /v1-profiles/:id/identities
Returns the identity signals, browser fingerprints, and click IDs tied to the profile — useful for confirming identity stitching worked.
{
"success": true,
"identities": {
"signals": [ /* … */ ],
"fingerprints": [ /* fingerprint_hash, stability_score, first_seen_at, last_seen_at, occurrence_count */ ],
"click_ids": [ /* click_type, click_id, campaign_source, campaign_medium, first_seen_at, is_exhausted */ ]
}
}Search profiles — GET /v1-profiles/search
Provide one of:
| Parameter | Notes |
|---|---|
email | Match by email signal (lower-cased). |
user_id | Match by known user ID. |
anonymous_id | Match by anonymous ID. |
q (or query) | Free-text search (max 200 characters). |
Add ?partial=true for substring matching (the default is exact match). limit
defaults to 20 (max 50). With no search parameter you get
400 Search parameter required: q, email, user_id, or anonymous_id.
{ "success": true, "profiles": [ /* … */ ], "count": 3 }Merge two profiles — POST /v1-profiles/merge
Requires an admin key
Merging is destructive and needs an admin key (uk_admin_).
curl -X POST https://YOUR_PROJECT_REF.supabase.co/functions/v1/v1-profiles/merge \
-H "Authorization: Bearer uk_admin_YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"source_profile_id": "…",
"target_profile_id": "…",
"reason": "manual review"
}'source_profile_id and target_profile_id are required and must both exist in
your org. Merging a profile into itself returns 400; a missing or cross-org
profile returns 404 One or both profiles not found. reason is optional.
Next
- Identity stitching — how profiles are resolved, and how to confirm a merge worked.
- Attribution — the click IDs surfaced under
/identities. - Authentication — read vs admin keys.
Last updated 2026-06-10