Skip to main content

Registering players and recording movements via the API

API reference for the Trackdesk endpoints that register players, record and read player metric movements, and list metric codes, with authentication, field reference, and example calls.

Written by Trackdesk Team

This article is the API reference for the Trackdesk endpoints your integration uses to register a new player, to record changes to that player's metrics over time, and to inspect the history of those changes. For the conceptual background (what a player is, what a movement records, what the dashboard shows), see Players in Trackdesk and Player movements.

Throughout the API, dashboard terms map to client_* field names: a Player is a client, a Brand is a revenue_origin, and a Player movement is a client_metric_movement. See the iGaming glossary for the full mapping.


Authentication and base URL

Every request authenticates with a personal access token sent in the X-Api-Key header.

To generate a token, navigate to Settings → Integrations → Personal access tokens, click Generate new token, and copy the value somewhere safe; you will not be able to see it again after closing the dialog.

All endpoints are served from your workspace's tenant subdomain. For example, if your dashboard is at https://acme.trackdesk.com, your tenant ID is acme and requests go to https://acme.trackdesk.com/.... The path prefix depends on the endpoint and is shown in each endpoint's heading below.

All requests use Content-Type: application/json. A successful response returns HTTP 200 with an empty JSON body for the write endpoints, or with the listed data for the read endpoints.


Register a player

Endpoint: POST /tracking/client/v1

Creates a new player record, linked to a previously tracked click. Call this whenever your platform creates a new account that should be attributed to an affiliate. Once the player exists, your integration can record movements against them.

The request body has three required fields:

  • externalId: your platform's identifier for the player (the user ID, account number, or player ID from your database). 1–255 characters. Must be unique per brand: trying to register the same external ID a second time under the same brand returns an error.

  • revenueOriginId: the UUID of the brand the player belongs to. Find it on the brand record in your dashboard; see Where Can I Find My Revenue Origin ID? for the exact location.

  • cid: the UUID of the original tracking click that referred the player, captured by your tracking integration when the visitor first lands on your site.

Example request body:

{
"externalId": "player-42",
"revenueOriginId": "0d7a8b6e-1f43-4f8d-9e2c-2b1f3a4e7c11",
"cid": "b1f3c5a7-9d2e-4a8b-bc11-7c2e0a9f4d6e"
}

A successful call returns 200 OK with an empty body. The most common errors are:

  • conversion source not exists: the cid does not match any tracked click. Verify that the click was actually recorded by your tracking integration.

  • revenue origin not exists: the revenueOriginId does not match any brand in your workspace.

  • duplicate client: a player with that externalId already exists under this brand.

Record a player movement

Endpoint: POST /tracking/clients/v1/metrics/movements

Records one or more metric updates against an existing player. Each request can update up to 20 metrics in one atomic call. The player must already be registered; this endpoint does not auto-create players.

The request body fields are:

  • externalClientId: the same externalId you used when registering the player. 1–255 characters. Required.

  • revenueOriginId: the same brand UUID the player was registered under. Required.

  • metrics: a list of 1–20 metric updates. Each metric code must be unique within the list. Each entry has three fields, described below.

  • requestId: an optional idempotency key (1–100 characters). If your integration retries the same call with the same requestId for the same player, the second attempt is rejected so the balance is not updated twice. Use a stable value from your platform's transaction or event ID. See Player movements for the full idempotency model.

Each entry in the metrics list contains:

  • code: the metric code exactly as defined on your workspace's player metrics page. Lowercase alphanumeric with hyphens (kebab-case), 1–100 characters. To discover the codes defined in your workspace, call GET /api/node/clients/v1/metrics (see below).

  • valueType: either CLIENT_METRIC_VALUE_TYPE_ABSOLUTE (the value replaces the current balance) or CLIENT_METRIC_VALUE_TYPE_RELATIVE (the value is added to the current balance; can be negative). See Player movements for guidance on choosing the right type.

  • value: the numeric value as a string. For example "100", "19.99", or "-5". Must be a whole number if the metric is defined as an integer metric.

Example request body:

{
"externalClientId": "player-42",
"revenueOriginId": "0d7a8b6e-1f43-4f8d-9e2c-2b1f3a4e7c11",
"requestId": "deposit-2026-05-28-001",
"metrics": [
{
"code": "deposits-total",
"valueType": "CLIENT_METRIC_VALUE_TYPE_RELATIVE",
"value": "250.00"
},
{
"code": "ngr",
"valueType": "CLIENT_METRIC_VALUE_TYPE_ABSOLUTE",
"value": "1875.50"
}
]
}

A successful call returns 200 OK with an empty body. The most common errors are:

  • client not found: no player matches the externalClientId and revenueOriginId pair. Register the player first.

  • metric not found: one of the metric codes is not defined on your workspace. Check the spelling against the codes returned by the metric-listing endpoint.

  • invalid integer metric value: a decimal value was sent for a metric defined as integer-typed. Send a whole number.

  • duplicate metric movement request: a movement with the same requestId already exists for this player. The original call was processed; the retry was correctly rejected.

List available metric codes

Endpoint: GET /api/node/clients/v1/metrics

Returns every player metric defined on your workspace, so your integration can validate metric codes at startup or build a configuration UI. The endpoint takes no parameters and uses the same X-Api-Key authentication as the tracking endpoints.

The response contains two arrays, integerMetrics and decimalMetrics, listing the metrics of each type separately. Each entry has:

  • id: the internal UUID of the metric.

  • name: the display name shown on the dashboard.

  • code: the metric code your integration sends in the metrics list of a movement.

  • monetary: whether the metric represents an amount of money (always false for integer metrics).

  • visible: whether the metric is currently shown as a column on the Player list and Player movements pages in the dashboard.

Example response body:

{
"integerMetrics": [
{
"id": "7e2c1a4b-9f3d-4b2e-8a11-b6c2e0a9f4d6",
"name": "Total bets",
"code": "bets",
"monetary": false,
"visible": true
}
],
"decimalMetrics": [
{
"id": "1a7c5e2f-3b4d-49a8-bcd1-72e0a9f4d6c8",
"name": "Lifetime deposits",
"code": "deposits-total",
"monetary": true,
"visible": true
}
]
}

List recorded movements

Endpoint: POST /api/node/clients/v1/metrics/movements/list

Returns the recorded movement history with paging and filtering. Each row carries the player it belongs to, every metric value the movement updated, the original request payload, the revshare contribution this movement made, and a reference to the settlement that paid it out (if any). This is the same data the dashboard's Players → Player movements log uses.

The endpoint is a POST because the filter object is structured. The request body has two required top-level fields:

  • pagination: an object with limit (1–500) and offset (starting at 0).

  • filters: an object with one required time range and four optional list filters, described below.

Inside filters:

  • timeRange: required. An object with from and to timestamps in RFC 3339 format (for example "2026-05-01T00:00:00Z"). from must be earlier than to.

  • clientIds: optional. A list of internal player UUIDs to filter by. Values must be unique.

  • clientExternalIds: optional. A list of your platform's player identifiers (the same values you sent as externalClientId when recording movements). Each 1–255 characters, unique.

  • revenueOriginIds: optional. A list of brand UUIDs. Useful when you operate multiple brands and want movements for a subset.

  • requestIds: optional. A list of idempotency keys to look up specific movements your integration recorded. Each 1–100 characters, unique.

Example request body:

{
"pagination": { "limit": 50, "offset": 0 },
"filters": {
"timeRange": {
"from": "2026-05-01T00:00:00Z",
"to": "2026-05-28T23:59:59Z"
},
"clientExternalIds": ["player-42"]
}
}

The response has two top-level fields:

  • rows: the movements that matched, one row per movement.

  • pagination: an object with hasMore (whether more results exist beyond the current page) and totalCount (the total number of matching movements, when available).

Each row contains:

  • client: an object identifying the player: id (internal UUID), externalId, and revenueOriginId.

  • integerMetrics: the integer metrics this movement updated, as a list of { code, value } entries.

  • decimalMetrics: the decimal metrics this movement updated, as a list of { code, value } entries. Decimal values are returned as strings to preserve precision.

  • requestData: the exact JSON payload your integration sent, stored verbatim for auditing.

  • requestId: the idempotency key sent with the original call, if any.

  • totalRevshare: the revshare contribution this movement generated, as a decimal string. Always present; "0" when no Player RevShare applies.

  • accountSettlementId: the UUID of the settlement that paid this movement's revshare out, if it has already been settled. Absent on movements still contributing to an open balance.

  • createdAt: the timestamp when the movement was recorded, in RFC 3339 format.

Example response body:

{
"rows": [
{
"client": {
"id": "c9e4f7d1-2a8b-4c3d-9e5f-1a2b3c4d5e6f",
"externalId": "player-42",
"revenueOriginId": "0d7a8b6e-1f43-4f8d-9e2c-2b1f3a4e7c11"
},
"integerMetrics": [],
"decimalMetrics": [
{ "code": "deposits-total", "value": "250.00" }
],
"requestData": "{\"externalClientId\":\"player-42\",\"metrics\":[...]}",
"requestId": "deposit-2026-05-28-001",
"totalRevshare": "12.50",
"createdAt": "2026-05-28T10:34:12Z"
}
],
"pagination": {
"totalCount": 1,
"hasMore": false
}
}

End-to-end flow

A typical integration runs these steps in order for every new player on your platform:

  1. Capture the click: when a referred visitor lands on your site, your tracking integration receives the click UUID. Store this value alongside the visitor's session so it survives until signup.

  2. Register the player: when the visitor creates an account on your platform, call POST /tracking/client/v1 with the new account's externalId, the brand's revenueOriginId, and the saved cid.

  3. Record movements: on every event that updates a tracked metric (a deposit, a wager settlement, an NGR update), call POST /tracking/clients/v1/metrics/movements with the player's externalClientId, the metric code, the value type, and the value. Include a requestId so retries are safe.

Once movements start flowing, the player's balance updates in real time, the movements appear in the Players → Player movements log, and any offer with Player RevShare configured starts paying the referring affiliate on each movement. To reconcile from your own systems, call POST /api/node/clients/v1/metrics/movements/list with the player's externalClientId or your stored requestId.


Related articles

Did this answer your question?