> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trycurious.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Identify users

> Link an anonymous user to a known one with POST /identify, update a known user's state, and handle login and logout correctly.

`POST /identify` declares that two ids are the same person, or updates a known user's identifiers and attributes without a decision. Secret keys only: the browser never holds the authority to merge identities. Full field reference: [the endpoint](https://docs.trycurious.ai/api-reference/identify.md).

```bash theme={null}
curl https://api.trycurious.ai/v1/identify \
  -H "Authorization: Bearer $CURIOUS_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "anonymous_id": "anon_456",
    "user_id": "user_123",
    "identifiers": { "email": "alice@example.com" },
    "attributes": { "plan": "pro" }
  }'
```

```json theme={null}
{ "status": "success" }
```

## Linking

`anonymous_id` is the id you decided and reported events with before the person was known; `user_id` is who they turned out to be. After the call, decisions and events on either id attribute to the merged user, so a reward reported under `user_id` credits a decision made under `anonymous_id`. The link is durable: many anonymous ids may link into one known user, and a retry of the same link is a harmless no-op.

A declaration that conflicts with existing links, for example an anonymous id already linked to a different known user, is a `409` with `{ "error": "conflict", "message": "Identity conflicts with an existing user" }`. Resolve it on your side; Curious does not guess.

## Profile-only updates

Omit `anonymous_id` to update a known user's state:

```json theme={null}
{ "user_id": "user_123", "attributes": { "plan": "enterprise" } }
```

The two buckets are the same as on `decide`: `identifiers` are durable identity (email, name, phone) and `attributes` are durable state (plan, company size). You pick the bucket; nothing is routed by key name. A different bucket name such as `traits` is a `400` with `Received unknown parameter: traits`. Explicit values win per key; unchanged state writes nothing.

## Sequencing

Identify is an ordered mutation, not telemetry. Send it the moment the person becomes known, and await it before a decision that should see the merged profile. The [server SDK](https://docs.trycurious.ai/sdks/node/events.md) sends it at once, retries only transient failures, and never queues it behind events.

## Login

1. Before login, the client decides and reports events under an anonymous id (the browser SDK mints `anon_<uuid>` in a first-party cookie; a mobile app can use an installation id).
2. On login or signup, your **server** calls `identify` with `anonymous_id` and the account's `user_id`, and awaits the response.
3. The client switches to the account's `user_id` for every later `decide` and `events` call.

From then on, history under both ids belongs to one person, and a purchase after login credits the decision that was served before it.

## Logout

Stop sending the account's `user_id`, and start a **fresh** anonymous id. Never reuse the anonymous id from before login: it is now linked to the account, so the next person on the same device would be attributed to it. The browser SDK's `reset()` does exactly this.

## Several accounts, one device

Each login links that session's anonymous id to that account. Linking the same anonymous id to a second account is the `409` above, which is why logout must mint a new anonymous id before anyone else signs in.

## With the server SDK

```ts theme={null}
await curious.identify({ userId: "user_123", anonymousId: "anon_456", attributes: { plan: "pro" } });
```

`identify()` throws a `CuriousError` with code `identity_conflict` on a `409`. See [Events and identify with the server SDK](https://docs.trycurious.ai/sdks/node/events.md).

If decisions and events under the two ids are still separate after a link, see [A user's history did not merge after identify](https://docs.trycurious.ai/troubleshooting/identify-not-merged.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.