Participants and enrollment

Participants

A participant exists once in your organization, identified by your own externalId (1–128 characters, unique in your organization). In paths, URL-encode it. Create a participant here, then enroll them in each program.

These endpoints cover your whole organization, so they need an admin key. A read-only key gets 404 on reads and 403 on changes; it reads participants through its program.

The Participant object

Field Type Description
externalId string Your ID for the participant
kybStatus string pending, approved, declined or expired
kybApprovedOn date or null Set only when kybStatus is approved
tags string[] Your labels for the participant
removedAt timestamp or null Set when the participant is removed
createdAt, updatedAt timestamp
programs array Every program the participant is enrolled in, including removed enrollments: { programId, enrolledOn, eligible, removedAt }
addresses array Single-participant endpoints only. Every wallet address linked to the participant, in any program: { address, attachedOn }

List participants

GET /participants → 200 { participants: Participant[], total }

Query Description
limit, offset Paging. Default 50, max 500.
kybStatus pending, approved, declined or expired
tag Participants with this tag
includeRemoved true to include removed participants. Default false.

Sorted by externalId. List items don't include addresses.

Get a participant

GET /participants/{externalId} → 200 Participant

{
  "externalId": "P-1001",
  "kybStatus": "approved",
  "kybApprovedOn": "2026-08-01",
  "tags": ["gold"],
  "removedAt": null,
  "createdAt": "2026-10-13T16:06:03.196Z",
  "updatedAt": "2026-10-13T16:06:03.196Z",
  "programs": [
    {
      "programId": "5a30079b-940a-4570-95dc-ba59e019852a",
      "enrolledOn": "2026-08-01",
      "eligible": true,
      "removedAt": null
    },
    {
      "programId": "c5bfa72c-c742-4a43-a031-2350ea524e55",
      "enrolledOn": "2026-09-15",
      "eligible": true,
      "removedAt": null
    }
  ],
  "addresses": [{ "address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU", "attachedOn": "2026-08-01" }]
}

Removed participants are still returned, with removedAt set. Returns 404 participant_not_found if there's no participant with that ID.

Create a participant

POST /participants → 201 Participant

Body field Type Required Description
externalId string Yes 1–128 characters, unique in your organization
kybStatus string No Default pending
kybApprovedOn date No Only with kybStatus: approved. Not in the future. Defaults to today.
tags string[] No Up to 20, each 1–64 characters
POST /api/v1/rewards/participants
Idempotency-Key: create-P-1001

{ "externalId": "P-1001", "kybStatus": "approved", "kybApprovedOn": "2026-08-01", "tags": ["gold"] }

Errors: 409 participant_already_exists if the externalId is taken, including by a removed participant; 400 if kybApprovedOn is in the future or set without kybStatus: approved.

To create many participants at once, upload a CSV.

Update a participant

PATCH /participants/{externalId} → 200 Participant

Body field Type Description
kybStatus string Changing it to anything other than approved clears kybApprovedOn
kybApprovedOn date Required when changing to approved
tags string[] Replaces the whole list

Send at least one field. externalId can't change. A change applies in every program the participant is enrolled in. A KYB change recalculates the open periods of each of those programs; a tag change recalculates nothing. Returns 404 participant_not_found for an unknown or removed participant.

PATCH /api/v1/rewards/participants/P-1002
Idempotency-Key: kyb-approved-P-1002

{ "kybStatus": "approved", "kybApprovedOn": "2026-10-12" }

Remove a participant

DELETE /participants/{externalId} → 204

Removes the participant from every program they're still enrolled in, then removes the participant. They stop earning everywhere, and their open periods are recalculated. Their history is kept: activity, rewards and wallet addresses stay, and the participant can still be read. A removed participant can't be updated or enrolled, and their externalId can't be used again.

To take a participant out of one program only, remove the enrollment.

Program enrollment

An enrollment puts a participant in one program. It holds what differs per program: the enrollment date, eligibility and removal. Program reads return a combined view, with KYB status and tags from the participant.

The Enrollment object

Field Type Description
externalId string The participant's ID
enrolledOn date Enrollment date. The participant earns in this program from this day.
kybStatus string From the participant
kybApprovedOn date or null From the participant
eligible boolean Your own eligibility flag, for filtering. It doesn't affect reward calculation.
tags string[] From the participant
removedAt timestamp or null Set when the participant is removed from this program
createdAt, updatedAt timestamp

Enrollment detail. The single-enrollment endpoints (get, enroll, update) also return:

Field Type Description
addresses array The wallet addresses this program's activity names: { address, attachedOn }, attached from the first day this program reports them
currentPeriod object The participant's reward in the program's current period, below
earningBalance amount or null The participant's latest reported balance across their wallets
balancesThrough date or null Date of the latest balance included in earningBalance
currentPeriod field Description
periodId The current period
status The period's status: OPEN, FINALIZING or FINALIZED
estimatedReward The participant's reward so far: all reward lines plus adjustments
calculatedAt When it was last calculated
pendingRecalculation true while a newer calculation is queued
lines Reward lines
adjustments Adjustments in the period

List enrolled participants

GET /programs/{id}/participants → 200 { participants: Enrollment[], total }

Query Description
limit, offset Paging. Default 50, max 500.
kybStatus pending, approved, declined or expired
eligible true or false
tag Participants with this tag
includeRemoved true to include participants removed from the program. Default false.

Sorted by externalId. List items don't include the detail fields.

Get an enrolled participant

GET /programs/{id}/participants/{externalId} → 200 Enrollment detail

This is the call to power a participant's own rewards view.

{
  "externalId": "P-1001",
  "enrolledOn": "2026-08-01",
  "kybStatus": "approved",
  "kybApprovedOn": "2026-08-01",
  "eligible": true,
  "tags": ["gold"],
  "removedAt": null,
  "createdAt": "2026-10-13T16:06:03.196Z",
  "updatedAt": "2026-10-13T16:06:03.196Z",
  "addresses": [{ "address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU", "attachedOn": "2026-08-01" }],
  "currentPeriod": {
    "periodId": "2026-10",
    "status": "OPEN",
    "estimatedReward": "1.917808",
    "calculatedAt": "2026-10-13T16:06:06.349Z",
    "pendingRecalculation": false,
    "lines": [
      {
        "ruleKey": "usdpt_holdings",
        "ruleVersion": 1,
        "address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
        "amount": "1.917808",
        "explanation": {
          "measure": { "type": "daily_balance", "value": "20000.000000" },
          "condition": { "type": "always", "met": true },
          "payout": { "type": "annual_rate", "rate": "0.035", "base": "20000.000000", "beforeLimits": "1.917808" },
          "limits": {}
        }
      }
    ],
    "adjustments": []
  },
  "earningBalance": "20000.000000",
  "balancesThrough": "2026-10-05"
}

Participants removed from the program are still returned, with removedAt set. Returns 404 Participant not found if the participant isn't enrolled in the program.

Enroll a participant

POST /programs/{id}/participants → 201 Enrollment detail

The participant must already exist: create them first.

Body field Type Required Description
externalId string Yes An existing participant
enrolledOn date No Default today. Not in the future, and within the program's dates.
eligible boolean No Default true
POST /api/v1/rewards/programs/2e369621-ef27-499f-88c0-15032f767c54/participants
Idempotency-Key: enroll-P-1001

{ "externalId": "P-1001", "enrolledOn": "2026-08-01" }

Errors:

  • 404 participant_not_found: no participant with that externalId
  • 409 participant_already_enrolled: already in the program, or removed from it
  • 409 participant_removed: the participant has been removed
  • 400 field_moved_to_org_participant: kybStatus, kybApprovedOn or tags in the body. Set them on the participant.
  • 400 enrolledOn cannot be in the future, 400 enrolledOn cannot be before the program starts on <date>
  • 409 Program has ended; it cannot enroll participants

To enroll many participants at once, upload a CSV.

Update an enrollment

PATCH /programs/{id}/participants/{externalId} → 200 Enrollment detail

Body field Type Description
eligible boolean Required

enrolledOn can't change. kybStatus, kybApprovedOn or tags return 400 field_moved_to_org_participant: update the participant instead.

PATCH /api/v1/rewards/programs/2e369621-ef27-499f-88c0-15032f767c54/participants/P-1002
Idempotency-Key: mark-P-1002-ineligible

{ "eligible": false }

Remove from a program

DELETE /programs/{id}/participants/{externalId} → 204

The participant stops earning in this program, and its open periods are recalculated. They stay enrolled in their other programs. Their history in the program is kept: they can still be read, and appear in lists with includeRemoved=true. A removed enrollment can't be added again.