> ## 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.

# Data requests

> Handle user.data_request webhooks and fulfill missing CIP fields with PUT /partner/data-requests/:id

## Overview

When a user opens a magic link and CIP data is incomplete, RaaS Widgets can send a `user.data_request` webhook. You then fulfill the gaps with `PUT /partner/data-requests/:id` before the widget asks the user.

<Info>
  Data requests are **non-blocking**. Acknowledge the webhook quickly, fetch data, then call the reply endpoint. If you respond before the user reaches CIP, they often skip data entry.
</Info>

## Webhook payload

```json theme={null}
{
  "event": "user.data_request",
  "timestamp": "2026-07-23T14:30:00.000Z",
  "data": {
    "dataRequestId": "dr_acme_a1b2c3d4e5f6",
    "referenceId": "user_12345",
    "linkId": "link_acme_x9y8z7w6v5u4",
    "requiredFields": [
      "name.secondName",
      "name.secondLastName",
      "idNumber",
      "idType",
      "dob"
    ],
    "replyEndpoint": "https://raas-widgets-backend-sandbox.up.railway.app/partner/data-requests/dr_acme_a1b2c3d4e5f6",
    "expiresAt": "2026-07-23T15:30:00.000Z"
  }
}
```

<Note>
  Webhook authenticity headers: `X-RAAS-Signature`, `X-RAAS-Timestamp`, and `X-RAAS-Event`. Verify them as documented in [Webhook security](/raas-widgets/webhooks/security).
</Note>

| Field            | Description                                |
| ---------------- | ------------------------------------------ |
| `dataRequestId`  | Id for this request (also in the URL path) |
| `referenceId`    | Your end-user id — must match on the PUT   |
| `requiredFields` | Fields still needed for CIP                |
| `replyEndpoint`  | Full URL for your PUT                      |
| `expiresAt`      | Deadline (aligned with link TTL)           |

Configure your HTTPS endpoint and copy the webhook secret in the [Partner Portal](https://raas-widgets-partner-portal-sandbox.up.railway.app/) (**Settings → Webhooks**).

## Fulfill the request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PUT https://raas-widgets-backend-sandbox.up.railway.app/partner/data-requests/dr_acme_a1b2c3d4e5f6 \
    -H "X-API-Key: raas_sandbox_your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "referenceId": "user_12345",
      "userData": {
        "name": {
          "secondName": "Carlos",
          "secondLastName": "Lopez"
        },
        "idNumber": "GALO900515HDFRPN09",
        "idType": "CURP",
        "dob": "1990-05-15"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    'https://raas-widgets-backend-sandbox.up.railway.app/partner/data-requests/dr_acme_a1b2c3d4e5f6',
    {
      method: 'PUT',
      headers: {
        'X-API-Key': process.env.RAAS_WIDGETS_API_KEY,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        referenceId: 'user_12345',
        userData: {
          name: { secondName: 'Carlos', secondLastName: 'Lopez' },
          idNumber: 'GALO900515HDFRPN09',
          idType: 'CURP',
          dob: '1990-05-15',
        },
      }),
    },
  );
  ```
</CodeGroup>

<ParamField body="referenceId" type="string" required>
  Must match the webhook `data.referenceId`.
</ParamField>

<ParamField body="userData" type="object" required>
  Only the fields you can provide — partial responses still reduce friction.
</ParamField>

### Success

```json theme={null}
{
  "success": true,
  "data": {
    "dataRequestId": "dr_acme_a1b2c3d4e5f6",
    "status": "fulfilled",
    "linkId": "link_acme_x9y8z7w6v5u4",
    "fulfilledAt": "2026-07-23T14:35:00.000Z",
    "message": "User data received and merged successfully"
  }
}
```

### Common errors

| HTTP | Code                     | Meaning                      |
| ---- | ------------------------ | ---------------------------- |
| 400  | `REFERENCE_ID_MISMATCH`  | Body `referenceId` ≠ request |
| 404  | `DATA_REQUEST_NOT_FOUND` | Unknown id                   |
| 409  | `DATA_REQUEST_EXPIRED`   | Link / request expired       |

## Handler sketch

Store your webhook secret as an env var (example name):

```bash theme={null}
RAAS_WEBHOOK_SECRET=whsec_your_secret_from_partner_portal
```

```javascript theme={null}
app.use('/webhooks/raas-widgets', express.raw({ type: 'application/json' }));

app.post('/webhooks/raas-widgets', async (req, res) => {
  // Verify X-RAAS-Signature / X-RAAS-Timestamp first (see Webhook security)
  const event = JSON.parse(req.body.toString());
  res.status(200).send('OK');

  if (event.event !== 'user.data_request') return;

  const { referenceId, requiredFields, replyEndpoint } = event.data;
  const userData = await loadFieldsForUser(referenceId, requiredFields);
  if (!userData) return;

  await fetch(replyEndpoint, {
    method: 'PUT',
    headers: {
      'X-API-Key': process.env.RAAS_WIDGETS_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ referenceId, userData }),
  });
});
```

<Tip>
  Aim to fulfill within a few seconds. If you cannot respond, the widget still works — the user enters missing fields manually.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="User data" icon="user" href="/raas-widgets/guides/user-data">
    Friction vs prefills.
  </Card>

  <Card title="Webhook security" icon="shield" href="/raas-widgets/webhooks/security">
    HMAC verification with legacy header names.
  </Card>
</CardGroup>
