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

# User Identification

> How RevBridge AI identifies and resolves customer identities from your event data.

## Identifiers

Every user in your request must include at least one **primary identifier** so RevBridge can link events to the correct customer profile.

### Primary identifiers

At least one of these is required per user:

| Identifier     | Type   | Description                                                 |
| -------------- | ------ | ----------------------------------------------------------- |
| `user_id`      | string | Your internal user or customer ID                           |
| `email`        | string | The user's email address                                    |
| `phone_number` | string | Phone number in E.164 format (e.g., `+5511999887766`)       |
| `anon_id`      | string | An anonymous identifier for users who haven't signed up yet |

<Warning>
  If none of these four identifiers are provided, the user will be rejected with a validation error.
</Warning>

### Additional identifiers

You can also include device and advertising identifiers:

| Identifier | Type   | Description                      |
| ---------- | ------ | -------------------------------- |
| `idfa`     | string | Apple Identifier for Advertisers |
| `adid`     | string | Google Advertising ID            |

## Identity resolution

RevBridge uses **deterministic identity resolution** to automatically unify customer profiles across all your data sources — API events, CSV imports, and integrations like Shopify.

When you send an event with identifiers, RevBridge:

1. Looks for an existing profile that matches any of the provided identifiers.
2. If a match is found, the event is linked to that profile, and any new identifiers are added.
3. If multiple profiles match (e.g., one matched by email, another by phone), they are **automatically merged** into a single unified profile.
4. If no match exists, a new profile is created.

### Merge keys

Not all identifiers trigger a merge. RevBridge distinguishes between **merge keys** (which can unify two separate profiles) and **identification keys** (which can only recognize an existing profile):

| Identifier      | Merge key | Behavior                                                                                                                                                                                                             |
| --------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email`         | Yes       | Two events with the same email = same person                                                                                                                                                                         |
| `phone_number`  | Yes       | Two events with the same phone = same person                                                                                                                                                                         |
| `user_id`       | Yes       | Two events with the same user\_id = same person                                                                                                                                                                      |
| `anon_id`       | No        | Identifies a device/session; won't merge two distinct people, but an unclaimed anonymous device can be **adopted** into a person profile on `identify()` (see [Anonymous adoption](#anonymous-adoption-on-identify)) |
| `idfa` / `adid` | No        | Identifies a device, but won't merge two people                                                                                                                                                                      |

<Info>
  Providing multiple identifiers (e.g., both `email` and `user_id`) improves matching accuracy and enables cross-source merging. When a user logs in and you know both their anonymous ID and their real identity, send all identifiers together. The [Web SDK](/developer/web-sdk) handles this for browser traffic automatically — see below.
</Info>

### Anonymous adoption on identify

When an event carries an `anon_id` whose device profile has **no person identifiers yet**, and that same request (or a later `identify` call) provides a person identifier such as `email` or `user_id`, RevBridge **adopts** the anonymous device profile: it re-points the anonymous history onto the person's profile instead of creating a separate one. All prior anonymous activity then belongs to the known person, so you keep the full journey from first touch through login.

<Warning>
  Adoption only claims an **unclaimed** device — one with no person identifiers yet. If the device profile already belongs to a known person (for example, a previous login on a shared computer), it is **not** re-pointed. This protects shared devices: two different people on the same browser never merge.
</Warning>

Adoption fires for browser traffic ingested through the [Web SDK](/developer/web-sdk), which RevBridge tags as `source: web`. For server-side ingestion with a secret API key, keep sending the `anon_id` together with the person identifier in the same event (as in Example 1 below) to link anonymous history.

### Example 1: Progressive profile enrichment

A user first browses anonymously, then logs in, then makes a purchase from a different channel. RevBridge automatically builds the complete profile:

```json title="Event 1: Anonymous browsing" theme={null}
{
  "identifiers": { "anon_id": "device_abc123" },
  "events": [{ "event_name": "page_view", "page": "/summer-sale" }]
}
```

```json title="Event 2: User logs in — links anonymous activity to known identity" theme={null}
{
  "identifiers": {
    "anon_id": "device_abc123",
    "email": "jane@example.com",
    "user_id": "usr_001"
  },
  "events": [{ "event_name": "login" }]
}
```

```json title="Event 3: Purchase via SMS link — adds phone to same profile" theme={null}
{
  "identifiers": {
    "phone_number": "+5511999887766",
    "email": "jane@example.com"
  },
  "events": [{ "event_name": "purchase", "revenue": 129.90 }]
}
```

After these three events, RevBridge has **one unified profile** with all identifiers linked: `anon_id`, `email`, `user_id`, and `phone_number`. All events appear in the same timeline.

### Example 2: Cross-source merge

A customer is imported via CSV with only a phone number. Later, an API event arrives with the same phone and an email. RevBridge automatically merges them:

```
CSV import: phone: +5511999887766               → Profile created
API event:  email: jane@example.com, phone: +5511999887766  → Same profile, email added
```

This works across all data sources — API, CSV imports, and integrations.

### Example 3: Shared identifiers

When multiple people share a phone number (e.g., a family phone), events with that phone are merged into the same profile. This is the default behavior since phone is a merge key.

```
email: maria@gmail.com, phone: +5511982299869 → Profile created
email: joao@gmail.com,  phone: +5511982299869 → Merged into same profile
```

<Tip>
  If shared phone numbers are common in your use case, contact support to configure phone as a non-merge key for your account.
</Tip>

### Data isolation

Identity resolution is **fully isolated per account**. The same email address used by two different RevBridge customers will never be merged across accounts. Each account has its own independent identity resolution.

## User profile fields

Beyond identifiers, you can send profile attributes in the `identifiers` object. These fields are attached to the customer profile and can be used for segmentation and personalization.

### Personal information

| Field        | Type   | Description                                 |
| ------------ | ------ | ------------------------------------------- |
| `first_name` | string | First name                                  |
| `last_name`  | string | Last name                                   |
| `gender`     | string | Gender                                      |
| `birthday`   | string | Date of birth (e.g., `1990-05-20`)          |
| `age`        | number | Age (must be a numeric value)               |
| `language`   | string | Preferred language (e.g., `pt-BR`, `en-US`) |
| `timezone`   | string | IANA timezone (e.g., `America/Sao_Paulo`)   |

### Location

| Field     | Type   | Description                                |
| --------- | ------ | ------------------------------------------ |
| `city`    | string | City name                                  |
| `country` | string | ISO 3166-1 country code (e.g., `BR`, `US`) |

### Technical context

| Field          | Type   | Description                              |
| -------------- | ------ | ---------------------------------------- |
| `platform`     | string | Platform (e.g., `web`, `ios`, `android`) |
| `ip_address`   | string | User's IP address                        |
| `user_agent`   | string | Browser or app user agent string         |
| `carrier`      | string | Mobile carrier                           |
| `device_model` | string | Device model (e.g., `iPhone 15 Pro`)     |
| `os_version`   | string | Operating system version                 |
| `app_version`  | string | Your application version                 |

## Consent flags

RevBridge tracks channel-level consent using a **3-state model** to ensure campaigns only reach users who have explicitly opted in. Send these as part of the `identifiers` object.

| Value             | Meaning                                           | Campaign behavior                              |
| ----------------- | ------------------------------------------------- | ---------------------------------------------- |
| `true` or `1`     | **Opt-in** — user has given consent               | Eligible for campaigns on this channel         |
| `false` or `0`    | **Opt-out** — user has explicitly revoked consent | Excluded from campaigns on this channel        |
| Not sent / `null` | **Not informed** — no consent data yet            | Excluded from campaigns (conservative default) |

| Field                | Type    | Description                                    |
| -------------------- | ------- | ---------------------------------------------- |
| `email_opt_in`       | boolean | User has opted in to receive emails            |
| `sms_opt_in`         | boolean | User has opted in to receive SMS               |
| `whatsapp_opt_in`    | boolean | User has opted in to receive WhatsApp messages |
| `rcs_opt_in`         | boolean | User has opted in to receive RCS messages      |
| `web_push_opt_in`    | boolean | User has opted in to web push notifications    |
| `mobile_push_opt_in` | boolean | User has opted in to mobile push notifications |
| `gdpr_consent`       | boolean | User has given GDPR consent                    |

<Tip>
  Send consent flags whenever they change — for example, when a user subscribes to your newsletter or updates their communication preferences. The distinction between "not informed" and "opt-out" matters: a user who has never been asked is different from one who explicitly declined.
</Tip>

## Custom attributes

Any fields in the `identifiers` object that are not listed above are stored as **custom attributes** on the user profile. This lets you attach any business-specific data.

```json theme={null}
{
  "identifiers": {
    "email": "jane@example.com",
    "user_id": "usr_001",
    "first_name": "Jane",
    "loyalty_tier": "gold",
    "company": "Acme Inc",
    "signup_source": "google_ads"
  },
  "events": [
    { "event_name": "purchase", "revenue": 59.90 }
  ]
}
```

## Complete example

A request with full profile data, consent flags, and custom attributes:

```json theme={null}
{
  "users": [
    {
      "identifiers": {
        "user_id": "usr_001",
        "email": "jane@example.com",
        "phone_number": "+5511999887766",
        "first_name": "Jane",
        "last_name": "Smith",
        "gender": "female",
        "birthday": "1992-03-15",
        "age": 33,
        "language": "pt-BR",
        "timezone": "America/Sao_Paulo",
        "city": "São Paulo",
        "country": "BR",
        "platform": "web",
        "email_opt_in": true,
        "sms_opt_in": true,
        "whatsapp_opt_in": false,
        "loyalty_tier": "gold"
      },
      "events": [
        {
          "event_name": "purchase",
          "timestamp": "2026-01-15T14:30:00Z",
          "revenue": 259.90,
          "currency": "BRL",
          "order_id": "ORD-12345"
        }
      ]
    }
  ]
}
```
