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

# User Lifecycle

> How M2M manages users across multiple links and transactions

## The referenceId

The `referenceId` is the foundation of user management in M2M. It's your unique identifier for a user, and M2M uses it to:

* **Identify returning users** - Skip verification for known users
* **Track CIP status** - Know if a user has completed identity verification
* **Manage data requests** - Request only missing data
* **Link transactions** - Connect multiple transactions to the same user

<Warning>
  Always use the same `referenceId` for the same user. Using different IDs for the same person creates duplicate users and increases friction.
</Warning>

## How M2M uses referenceId

When you create a link, M2M checks:

```mermaid theme={null}
flowchart TD
    A[Create Link API] --> B{User exists?}
    B -->|No| C[Create new user]
    B -->|Yes| D{CIP complete?}
    D -->|No| E[Check data completeness]
    D -->|Yes| F[Skip CIP in widget]
    E --> G{Data complete?}
    G -->|No| H[Request missing data]
    G -->|Yes| I[Proceed to CIP]
    C --> E
```

### New user (first link)

When a `referenceId` appears for the first time:

1. M2M creates a new user record
2. Stores any `userData` you provided
3. Evaluates what data is missing for CIP
4. If webhooks are configured and data is incomplete, sends `user.data_request`
5. User completes any remaining verification in the widget

### Returning user (subsequent links)

When you create a link for an existing `referenceId`:

1. M2M finds the existing user record
2. Merges any new `userData` (respecting precedence rules)
3. Checks if CIP was already completed
4. If CIP is complete, user skips verification entirely
5. If CIP is incomplete, only missing steps are shown

<Check>
  **Key benefit:** Once a user completes CIP, future links for that user are frictionless - they go straight to the transaction.
</Check>

## User states

M2M tracks these states for each user:

| State             | Description                        |
| ----------------- | ---------------------------------- |
| `pending`         | User created, no activity yet      |
| `data_incomplete` | Missing data for CIP               |
| `data_complete`   | All CIP data collected             |
| `cip_pending`     | CIP verification in progress       |
| `cip_verified`    | Identity verified                  |
| `cip_failed`      | Verification failed (needs review) |

### State transitions

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending: Link created
    pending --> data_incomplete: User opens link
    pending --> data_complete: All data provided
    data_incomplete --> data_complete: Data received
    data_complete --> cip_pending: Verification started
    cip_pending --> cip_verified: Passed
    cip_pending --> cip_failed: Failed
    cip_failed --> cip_pending: Retry
```

## Data management

### What M2M stores

For each user (identified by `referenceId`), M2M stores:

* User data (name, ID, DOB, etc.)
* Data source for each field (API, webhook, or widget)
* CIP verification status
* Transaction history reference

### Data precedence

When the same field comes from multiple sources:

| Priority    | Source            | When it's set                 |
| ----------- | ----------------- | ----------------------------- |
| 1 (highest) | `partner_api`     | Link creation with `userData` |
| 2           | `partner_webhook` | Data request response         |
| 3 (lowest)  | `widget`          | User input in widget          |

<Note>
  Higher priority data is never overwritten. If you send `firstName` via API, a webhook response with a different `firstName` is ignored.
</Note>

### Updating user data

To update user data for an existing user, provide new values when creating a new link:

```json theme={null}
{
  "referenceId": "user_12345",
  "userData": {
    "paymentMethod": {
      "type": "bank_account",
      "bankAccount": {
        "country": "MX",
        "accountNumber": "098765432109876543"
      }
    }
  }
}
```

Only fields you include are updated. Existing data is preserved.

## Link lifecycle

Each link goes through its own lifecycle, independent of user state:

| Status        | Description                |
| ------------- | -------------------------- |
| `created`     | Link generated, not opened |
| `opened`      | User opened the link       |
| `in_progress` | User is in the widget flow |
| `completed`   | Transaction completed      |
| `expired`     | Link expired               |
| `revoked`     | Link manually revoked      |

### One active link per user

M2M enforces a single active link per user. If you try to create a link for a user with an active link:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "ACTIVE_LINK_EXISTS",
    "message": "User already has an active link",
    "existingLinkId": "link_acme_abc123"
  }
}
```

To create a new link:

1. Wait for the existing link to expire or complete, OR
2. Revoke the existing link via API (coming soon)

## Best practices

<AccordionGroup>
  <Accordion title="Use stable identifiers">
    Use your primary user ID as the `referenceId`. Don't use:

    * Session IDs (change each visit)
    * Email addresses (users can change them)
    * Phone numbers (can be recycled)

    Good: `user_12345`, `usr_abc123def456`
    Bad: `session_xyz`, `john@example.com`
  </Accordion>

  <Accordion title="Handle user merges">
    If you merge user accounts in your system, you may have multiple `referenceId`s for the same person. Options:

    * Continue using the original ID
    * Create a mapping in your system
    * Contact support for account merging
  </Accordion>

  <Accordion title="Track CIP status">
    Cache the user's CIP status in your system to:

    * Show different UI for verified vs. unverified users
    * Skip unnecessary data collection
    * Predict user journey time
  </Accordion>

  <Accordion title="Provide consistent data">
    Always provide the same data for the same user. Inconsistent data can:

    * Trigger unnecessary data requests
    * Cause CIP verification issues
    * Confuse the user
  </Accordion>
</AccordionGroup>

## Example: Complete user journey

Here's how a typical user progresses through M2M:

### First transaction

```javascript theme={null}
// Partner creates first link with partial data
POST /partner/links
{
  "referenceId": "user_12345",
  "userData": {
    "name": { "firstName": "Juan", "lastName": "Garcia" }
  }
}

// M2M sends webhook for missing data
{
  "event": "user.data_request",
  "data": {
    "requiredFields": ["name.secondName", "idNumber", "dob"]
  }
}

// Partner responds with data
PUT /partner/data-requests/dr_123
{
  "userData": {
    "name": { "secondName": "Carlos" },
    "idNumber": "GALO900515",
    "dob": "1990-05-15"
  }
}

// User opens link, completes CIP, makes transaction
// Status: cip_verified
```

### Second transaction (frictionless)

```javascript theme={null}
// Partner creates second link - same referenceId
POST /partner/links
{
  "referenceId": "user_12345"  // No userData needed!
}

// M2M recognizes verified user
// No webhook sent - data is complete
// User opens link, skips CIP, makes transaction immediately
```

### Third transaction (new payment method)

```javascript theme={null}
// Partner creates link with updated payment method
POST /partner/links
{
  "referenceId": "user_12345",
  "userData": {
    "paymentMethod": {
      "type": "bank_account",
      "bankAccount": {
        "country": "MX",
        "accountNumber": "098765432109876543"
      }
    }
  }
}

// M2M updates payment method, user is still verified
// Smooth transaction with new payment destination
```

## Next steps

<CardGroup cols={2}>
  <Card title="User Data Guide" icon="user" href="/m2m/integration/user-data">
    Understand the friction vs. integration trade-off.
  </Card>

  <Card title="Data Requests" icon="database" href="/m2m/integration/data-requests">
    Handle webhooks for missing user data.
  </Card>
</CardGroup>
