Skip to Content
DocsAPIProfiles

Profiles API

User identity and trait management. Writes are async; reads come from ClickHouse.

Services: ProfilesSDKService (SDK auth), ProfilesService (shared auth)

ProfilesSDKService — Identify

Link an anonymous profile to an external ID and merge traits. Called by SDKs via identify().

Auth: SDK API key

Request

{ "externalId": "user-123", "traits": { "email": "user@example.com", "plan": "pro", "company": "Acme Inc" } }

Response

{ "profileId": "prof_abc123" }

TypeScript client

import { ProfilesSDKService } from './gen/profiles/v1/profiles_sdk_connect' const client = createClient(ProfilesSDKService, transport) await client.identify({ externalId: 'user-123', traits: { email: 'user@example.com', plan: 'pro' } })

Behavior

  • Creates a new profile if externalId is unknown
  • Merges traits if profile already exists (does not remove existing traits)
  • Merges all prior anonymous events into the identified profile
  • Write is async — profile may not appear in search for a few seconds

ProfilesSDKService — Alias

Merge two profile IDs into one. Use when a user had separate anonymous profiles before and after signup, or when migrating from another system.

Auth: SDK API key

{ "previousId": "anonymous-abc", "externalId": "user-123" }

All events from previousId are attributed to the profile for externalId.

ProfilesService — Read and manage

Server-side profile operations. Requires shared API key or JWT.

Auth: Shared API key or JWT

Get profile

// Request { "externalId": "user-123" } // Response { "profile": { "id": "prof_abc123", "externalId": "user-123", "traits": { "email": "user@example.com", "plan": "pro" }, "firstSeen": "2026-01-15T10:00:00Z", "lastSeen": "2026-06-01T14:30:00Z", "eventCount": 342 } }

List profiles

// Request { "query": "user@example.com", "limit": 50, "offset": 0 } // Response { "profiles": [ ... ], "total": 1 }

Search by external ID, email trait, or custom trait filters.

Delete profile

Soft-delete a profile. Events remain in ClickHouse but the profile is hidden from search and marked deleted in Postgres.

// Request { "externalId": "user-123" }

Storage model

Identify / Alias (SDK) → NATS profile worker → Postgres (profile record, traits) → ClickHouse (event attribution update) Read (ProfilesService) → ClickHouse (event history, derived traits) → Postgres (soft-delete state, trait overrides)

Read lag: Profile writes are async. After identify(), wait 2–5 seconds before querying via ProfilesService.Get.

SDK integration

Client-side identity is handled by the SDK — you rarely call ProfilesSDKService directly:

import { identify, reset } from '@poluruprvn/pug-web' identify('user-123', { email: 'user@example.com' }) reset() // on sign-out

See Identity & sessions.

Dashboard

Profile search and detail UI: Profiles

Errors

ErrorCauseFix
not_foundExternal ID doesn’t existConfirm identify was called
invalid_argumentEmpty external IDProvide non-empty string
Profile not in search after identifyAsync write lagWait a few seconds and retry

Further reading

Last updated on