# Get user's referral summary

Read one trader's referral invite link, their referral totals and each invited trader's progress through your programs.

- Endpoint: `POST https://api.returning.ai/v1/referral/summary`
- Section: Gamification / Referrals
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `getUserData` (Shown in the dashboard as "Get User Data")
- Retries: Read-only; exact retries are safe
- Guide: hand-written
- Verified: code, 2026-09-27
- Last updated: 26 Sep 2026
- Web page: https://docs.returning.ai/api-reference/gamification-referral/get-users-referral-summary

## When to use this

- Show a trader their invite link and how the people they invited are doing, in your own app.
- Support needs to check whether a trader's invited friend has completed a stage.
- Reconcile the referral rewards a trader has been paid.

**Instead:** Use [Get referral programs](https://docs.returning.ai/api-reference/gamification-referral/get-referral-programs.md) instead to read how your programs and stages are set up, without any trader's progress.

## Authentication

- Header: `Authorization: Bearer <API_KEY>`
- Permission: `getUserData`

Send a Community API key with `getUserData`, shown in the dashboard as Get User Data. The key decides the community, so never send a community ID. The trader must be a member of that community. Keep the key on your server: the response includes emails and invite links.

## Behaviour

This read changes nothing. Identify the trader who sends invites; their invited traders come back in `referees`. The totals in `activity` and the progress in `referees` cover running programs only. When referrals are switched off for your community, a trader who exists still returns `200`, with zero totals and no `referees`.

Referral progress is worked out after the invited trader acts, so a stage can take a short while to show as complete. The list of invited traders can also be up to 30 seconds old. If you are waiting for a change, read again with backoff rather than treating the first result as final.

With `programId`, each stage's `status` is only `completed` or `not_started`, and stage `rewards` come back as `0`. Read reward amounts from Get referral programs.

## Request

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key with `getUserData`. (`Bearer <API_KEY>`) |
| `Content-Type` | `string` | Yes | Request body format. (`application/json`) |

### Body

Send `identifier` with a `type` and `value`. `id` is the numeric platform user ID from Get User Data, not the internal record ID. For a broker customer ID stored in a custom user field, use `custom-field` with that field's key as `fieldName`, for example `customerid`.

Page through invited traders with `pagination`: send `nextCursor` back as `pagination.cursor` until `hasNextPage` is `false`. Keep the same `identifier`, `programId` and `lang` for every page.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `identifier` | `object` | Yes | The trader whose referrals you want, the person who sends invites. Not one of the traders they invited. |
| `identifier.type` | `string` | Yes | How to find the trader: `id` (platform user ID), `email`, `data-email` (same as `email`), `custom-field` (a value in one of your custom user fields), or `data-customer-id`, which also reads a custom user field. (`id`, `email`, `data-email`, `data-customer-id` or `custom-field`) |
| `identifier.value` | `string` | Yes | The platform user ID, email or field value. Emails ignore case; spaces at either end are removed. (Non-empty text) |
| `identifier.fieldName` | `string` | No | The custom user field's key, matched exactly. Required with `custom-field`. With `data-customer-id` it defaults to `customerId`; send it when your field's key differs. (Custom field key) |
| `pagination` | `object` | No | Paging for the list of invited traders. |
| `pagination.limit` | `integer` | No | Invited traders per page. (Default 20; max 100) |
| `pagination.cursor` | `string` | No | `nextCursor` from the previous page. Omit it for the first page. A cursor that isn't recognised starts from the first page. (Opaque text) |
| `programId` | `string` | No | Show progress for one program only. A `programId` from Get referral programs. (Program ID in your community) |
| `lang` | `string` | No | Language code for program, stage and requirement text, such as `fr`. Omit it, or send `en`, for the text as entered. |

### Example request

```bash
curl --request POST \
  --url https://api.returning.ai/v1/referral/summary \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "identifier": {
      "type": "email",
      "value": "trader@example.com"
    },
    "pagination": {
      "limit": 20
    }
  }'
```

## Response

A `200` returns the trader in `data.user`, their links in `data.invite`, totals in `data.activity`, and one page of invited traders in `data.referees`. Share `invite.url`, not `platformUrl`: when your community has a custom invite link, `url` is that link. Branch on the HTTP status.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `meta` | `object` | always | Status of the request. |
| `meta.status` | `string` | always | Result of the request. (`success`) |
| `meta.statusCode` | `integer` | always | The HTTP status, repeated. |
| `message` | `string` | always | Human-readable summary. Do not branch on it. |
| `data` | `object` | always | The summary. |
| `data.user` | `object` | always | The trader you asked about. |
| `data.user.id` | `string` | always | The trader's internal user record ID. |
| `data.user.userId` | `string` | always | The trader's platform user ID, as a string. |
| `data.user.username` | `string` | always | The trader's username, or their display name when there is no username. |
| `data.user.email` | `string` | always | The trader's email, or `null` when none is stored. |
| `data.invite` | `object` | always | The trader's invite link. |
| `data.invite.url` | `string` | always | The link to share. Your custom invite link when one is set up for your community and the trader has every value it needs; otherwise the same as `platformUrl`. |
| `data.invite.platformUrl` | `string` | always | The invite link on your community's Returning.AI address, ending in `/v2/app/invite/<code>`. Starts with `/` when your community has no custom domain. |
| `data.invite.code` | `string` | always | The trader's invite code, as `<communityId>:<platform user ID>`. |
| `data.invite.origin` | `string` | always | Your community's custom domain, such as `https://community.example.com`. Empty when there is none. |
| `data.activity` | `object` | always | The trader's referral totals across your running programs. |
| `data.activity.totalReferrals` | `integer` | always | Traders this trader has invited who still have an account. |
| `data.activity.inProgress` | `integer` | always | Invited traders who have completed at least one stage, but not every stage. |
| `data.activity.completed` | `integer` | always | Invited traders who have completed every stage of every running program. |
| `data.activity.xpEarned` | `integer` | always | XP this trader has been paid for referrals. |
| `data.activity.coinsEarned` | `integer` | always | Coins this trader has been paid for referrals. |
| `data.referees` | `object[]` | always | The invited traders on this page, most recently joined first. Empty when there are none. |
| `data.referees.userId` | `string` | always | The invited trader's platform user ID, as a string. |
| `data.referees.username` | `string` | always | The invited trader's display name, falling back to their username. |
| `data.referees.avatar` | `string` | always | Profile picture URL. Empty string when none. |
| `data.referees.joinedAt` | `string` | always | When the invited trader joined, ISO 8601 UTC, or `null` when not recorded. |
| `data.referees.progressByProgram` | `object[]` | always | Progress in each running program, or in the one `programId` you sent. |
| `data.referees.progressByProgram.programId` | `string` | always | The program's ID. |
| `data.referees.progressByProgram.programName` | `string` | always | The program's name. |
| `data.referees.progressByProgram.completedStages` | `integer` | always | Stages completed. |
| `data.referees.progressByProgram.totalStages` | `integer` | always | Stages in the program. |
| `data.referees.progressByProgram.percentage` | `integer` | always | `completedStages` as a percentage of `totalStages`, rounded. |
| `data.referees.progressByProgram.stages` | `object[]` | always | Each stage, in order. |
| `data.referees.progressByProgram.stages.stageId` | `string` | always | The stage's ID. |
| `data.referees.progressByProgram.stages.stageNumber` | `integer` | always | Position in the program, starting at 1. |
| `data.referees.progressByProgram.stages.name` | `string` | always | Stage name. |
| `data.referees.progressByProgram.stages.description` | `string` | always | Stage description. Empty string when none. |
| `data.referees.progressByProgram.stages.displaySetting` | `string` | always | What traders are shown for the stage. (`condition`, `description` or `condition_and_description`) |
| `data.referees.progressByProgram.stages.status` | `string` | always | Where the invited trader is on this stage. `in_progress` means some requirements are met. (`completed`, `in_progress` or `not_started`) |
| `data.referees.progressByProgram.stages.isCompleted` | `boolean` | always | `true` when the invited trader has completed the stage. |
| `data.referees.progressByProgram.stages.revoked` | `boolean` | always | `true` when the stage was completed but the invited trader no longer meets its requirements. The reward isn't taken back. |
| `data.referees.progressByProgram.stages.rewards` | `object` | always | The stage's reward to the referrer, as `xp` and `coins`. |
| `data.referees.progressByProgram.stages.requirements` | `object` | always | How many requirements are met, as `met` out of `total`, with `operator` and each condition group in `mainConditions`. |
| `data.referees.progressByProgram.stages.requirements.operator` | `string` | - | `and` when every group must be met, `or` when one is enough. |
| `data.referees.progressByProgram.stages.requirements.met` | `integer` | - | Requirements met. |
| `data.referees.progressByProgram.stages.requirements.total` | `integer` | - | Requirements needed. A group or stage set to `or` counts as one. |
| `data.referees.progressByProgram.stages.requirements.mainConditions` | `object[]` | - | The condition groups. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.groupId` | `string` | - | The group's ID. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.operator` | `string` | - | `and` or `or`, for the conditions in the group. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.text` | `string` | always | The group's text for traders, or `null` when none is shown. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.isMet` | `boolean` | - | `true` when the group is met. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.revoked` | `boolean` | - | `true` when a condition in a completed stage is no longer met. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.nestedConditions` | `object[]` | - | The conditions in the group. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.nestedConditions.conditionId` | `string` | - | The condition's ID. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.nestedConditions.text` | `string` | - | The condition's text for traders. When none is set, the condition's key, or `Complete this condition`. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.nestedConditions.isMet` | `boolean` | - | `true` when the condition is met. Every condition shows `true` once the stage is complete. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.nestedConditions.revoked` | `boolean` | - | `true` when the condition was met for a completed stage and no longer is. |
| `data.referees.progressByProgram.stages.requirements.mainConditions.nestedConditions.isRaw` | `boolean` | - | `true` when `text` is the condition's key because no text for traders is set. |
| `data.totalCount` | `integer` | always | Invited traders across all pages. |
| `data.pagination` | `object` | always | Paging for the invited traders. |
| `data.pagination.hasNextPage` | `boolean` | always | `true` when another page follows. |
| `data.pagination.nextCursor` | `string` | always | Send as `pagination.cursor` to get the next page. `null` on the last page. (Nullable) |

### Example response (200)

```json
{
  "meta": {
    "status": "success",
    "statusCode": 200
  },
  "message": "Get referral summary success.",
  "data": {
    "user": {
      "id": "<userObjectId>",
      "userId": "3247779",
      "email": "trader@example.com",
      "username": "sample_trader"
    },
    "invite": {
      "code": "66f000000000000000000010:3247779",
      "origin": "https://community.example.com",
      "url": "https://community.example.com/v2/app/invite/66f000000000000000000010:3247779",
      "platformUrl": "https://community.example.com/v2/app/invite/66f000000000000000000010:3247779"
    },
    "activity": {
      "coinsEarned": 25,
      "xpEarned": 250,
      "totalReferrals": 1,
      "inProgress": 1,
      "completed": 0
    },
    "referees": [
      {
        "userId": "3247780",
        "username": "Invited Trader",
        "avatar": "",
        "joinedAt": "2026-09-20T08:30:00.000Z",
        "progressByProgram": [
          {
            "programId": "66f000000000000000000710",
            "programName": "Invite a trader",
            "completedStages": 1,
            "totalStages": 2,
            "percentage": 50,
            "stages": [
              {
                "stageId": "66f000000000000000000711",
                "stageNumber": 1,
                "name": "Get active",
                "description": "Your friend earns more than 500 XP.",
                "displaySetting": "condition",
                "status": "completed",
                "isCompleted": true,
                "rewards": {
                  "xp": 250,
                  "coins": 25
                },
                "requirements": {
                  "operator": "and",
                  "met": 1,
                  "total": 1,
                  "mainConditions": [
                    {
                      "groupId": "66f000000000000000000712",
                      "operator": "and",
                      "text": null,
                      "isMet": true,
                      "revoked": false,
                      "nestedConditions": [
                        {
                          "conditionId": "66f000000000000000000713",
                          "text": "Earn more than 500 XP",
                          "isMet": true,
                          "revoked": false,
                          "isRaw": false
                        }
                      ]
                    }
                  ]
                },
                "revoked": false
              },
              {
                "stageId": "66f000000000000000000714",
                "stageNumber": 2,
                "name": "Fund an account",
                "description": "",
                "displaySetting": "condition",
                "status": "not_started",
                "isCompleted": false,
                "rewards": {
                  "xp": 0,
                  "coins": 100
                },
                "requirements": {
                  "operator": "and",
                  "met": 0,
                  "total": 1,
                  "mainConditions": [
                    {
                      "groupId": "66f000000000000000000715",
                      "operator": "and",
                      "text": null,
                      "isMet": false,
                      "revoked": false,
                      "nestedConditions": [
                        {
                          "conditionId": "66f000000000000000000716",
                          "text": "Make a first deposit",
                          "isMet": false,
                          "revoked": false,
                          "isRaw": false
                        }
                      ]
                    }
                  ]
                },
                "revoked": false
              }
            ]
          }
        ]
      }
    ],
    "totalCount": 1,
    "pagination": {
      "nextCursor": null,
      "hasNextPage": false
    }
  }
}
```

## Errors

Validation `400` errors and the key errors (`401`, `403`) carry a code in `meta.code`. Other errors have no code: read `detail`, which says what went wrong, and branch on the HTTP status. `message` on a validation error can vary; don't branch on it.

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 400 | - | The lookup couldn't run. `detail` says why: `Custom field identifier does not exist.` (no custom user field has that key; `data-customer-id` without `fieldName` looks for `customerId`), or `Custom field identifier value is invalid.` (the value doesn't fit the field's type). |
| 400 | `VALIDATION_FAILED` | A field is missing or invalid: no `identifier`, an unknown `identifier.type`, an empty `value`, `custom-field` without `fieldName`, or `pagination.limit` outside 1-100. `detail` names each field, such as `identifier.fieldName`. |
| 401 | `AUTH_API_KEY_REQUIRED` | No key was sent. Send `Authorization: Bearer <API_KEY>`. |
| 401 | `AUTH_API_KEY_INVALID` | The key is unknown, expired or malformed. Use a current Community API key. |
| 403 | `AUTH_PERMISSION_REQUIRED` | The key lacks `getUserData`. Add Get User Data in Settings > Integration > API Keys. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 404 | - | `detail` says which: `User not found` (nothing matches the identifier; for `id`, send the platform user ID, not the internal record ID), `User is not exists in this community` (the trader isn't a member of your community; a trader created through Create User can also get this while Get User Data finds them, in which case contact Returning.AI support), or `Referral program not found` (check `programId` with Get referral programs). |
| 409 | - | More than one trader has this custom field value (`Duplicate users found for this custom field identifier.`). Use `id` or `email` instead, and fix the duplicate values. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 503 | - | The custom field lookup isn't ready yet. Retry with backoff, or look the trader up by `id` or `email`. |
| 502 | `STORE_DEPENDENCY_UNAVAILABLE` | Referral data is briefly unavailable. Retry the same request with exponential backoff. |
| 500 | `INTERNAL_ERROR` | The read failed unexpectedly. A `500` with `message` `Get referral summary error.` and no code means the same. Retry with exponential backoff. |
| 401 | `AUTH_API_KEY_VALIDATION_FAILED` | The key could not be checked just now. Retry with backoff; the key itself may be fine. |

**Retries:** This endpoint is read-only, so retrying the exact same request is safe after a network error, a `500`, `502` or `503`. Use bounded exponential backoff. Don't retry a `400`, `404` or `409` without changing the request. Over the [rate limit](https://docs.returning.ai/how-it-works.md#rate-limits), requests return `429`; wait for the window to reset, then retry.

## Next step

- [Get referral programs](https://docs.returning.ai/api-reference/gamification-referral/get-referral-programs.md): `GET /v1/referral/programs`. Read every stage's requirements and rewards, and the program completion bonus.
- [Check the trader is in your community with Get User Data](https://docs.returning.ai/api-reference/users/get-user-data.md): `POST /v1/users/info`.
