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

# identify()

> Attach traits to a user — plan, MRR, email, and custom metadata.

## Overview

`identify()` links a user ID to a set of traits. Call it when a user signs up, logs in, or changes their plan. The scoring model uses traits like `plan` and `mrr` to contextualize risk.

You don't need to call `identify()` before every event — identify once, then just track events.

## Signature

```typescript theme={null}
churn.identify(
  userId: string,
  traits: UserTraits,
  options?: CallOptions
): Promise<{ ok: boolean }>
```

## Parameters

<ParamField path="userId" type="string" required>
  A stable, unique identifier for the user. Must be a non-empty string.
</ParamField>

<ParamField path="traits" type="UserTraits" required>
  An object of user properties. All fields are optional but at least one is recommended.
</ParamField>

<ParamField path="options.signal" type="AbortSignal">
  An `AbortSignal` to cancel the request. The SDK throws `ChurnKitError` with code `ABORTED` if the signal fires.
</ParamField>

## UserTraits

| Field        | Type                                  | Description                                                 |
| ------------ | ------------------------------------- | ----------------------------------------------------------- |
| `plan`       | `string`                              | Pricing plan (`'free'`, `'pro'`, `'enterprise'`)            |
| `mrr`        | `number`                              | Monthly recurring revenue **in cents** (e.g. `4900` = \$49) |
| `email`      | `string`                              | User's email address                                        |
| `name`       | `string`                              | User's display name                                         |
| `company`    | `string`                              | Company name                                                |
| `signedUpAt` | `string`                              | ISO 8601 timestamp of signup                                |
| `[key]`      | `string \| number \| boolean \| null` | Any custom trait                                            |

## Examples

### Basic identification

```typescript theme={null}
await churn.identify('user_123', {
  email: 'alice@acme.com',
  name: 'Alice Smith',
  plan: 'pro',
  mrr: 4900,
})
```

### On login

```typescript theme={null}
// After successful authentication
await churn.identify(session.userId, {
  plan: user.plan,
  mrr: user.mrrCents,
  email: user.email,
})
```

### Plan upgrade

```typescript theme={null}
// After Stripe webhook confirms upgrade
await churn.identify(userId, {
  plan: 'enterprise',
  mrr: 29900,
})
```

### Custom traits

```typescript theme={null}
await churn.identify('user_123', {
  plan: 'pro',
  industry: 'fintech',
  team_size: 12,
  referral_source: 'producthunt',
})
```

### With abort signal

```typescript theme={null}
const controller = new AbortController()
setTimeout(() => controller.abort(), 3000)

await churn.identify('user_123', { plan: 'pro' }, { signal: controller.signal })
```

## Return value

```typescript theme={null}
{ ok: true }
```

## Errors

| Code               | When                              |
| ------------------ | --------------------------------- |
| `VALIDATION_ERROR` | `userId` is empty or not a string |
| `UNAUTHORIZED`     | API key is invalid or revoked     |
| `TIMEOUT`          | Request exceeded `timeout` ms     |
| `ABORTED`          | `AbortSignal` fired               |

<Warning>
  Calling `identify()` with an empty `traits` object (`{}`) is allowed but a warning is logged — no data is stored.
</Warning>
