Skip to main content
AvailableVerified against 76c1f85Markdown

Link players

A Link connects one player at your platform to a PlayerPal Account. The player confirms it on a PlayerPal page, and you receive an Alias: a random, pseudonymous identifier that only you and PlayerPal can resolve. You use that Alias for everything afterwards, including activity, facts and status checks. No player data reaches PlayerPal until a Link exists, and you never send PlayerPal a name, email address or phone number.

You will
  • Create a link intent with a signed operator assertion
  • Open the PlayerPal link page and receive a result code
  • Exchange the code for the player alias
  • Keep alias status in sync after an Unlink
You need
  • The integration key PlayerPal issued to you
  • Your operator ID (pp_opr_…) or integration ID (pp_int_…)
  • Operators: a JSON Web Key Set registered with PlayerPal
  • The origin of the page that opens the popup
Time
About 30 minutes
For
Operators and game providers

Watch first. This shows the one-time linking flow in five short scenes.

Linking happens once, started by the casinoCasinoyour partnerPlayerPalholds the linkPlayerapproves onceNo activity flows for this player until the link is complete.
0:00 / 0:46

A one-time step

The casino starts linking, the player approves once, and no activity flows for that player until the link completes.

How linking works​

The first Link creates the player's PlayerPal Account. At a later partner, the player either signs in to that Account, which joins the new relationship to it, or creates a separate Account. There is no later "claim" step. This guide follows the operator-led flow; provider-led linking differs only in a few places.

1Set up the integration key

Linking routes use the integration key PlayerPal issues during onboarding, not HMAC signing. Send it as x-api-key: <your-integration-key> or Authorization: Bearer <your-integration-key>.

  • The audience comes from a header, never from the body: x-operator-id for an operator, x-integration-id for a game provider integration.
  • Keep the key server-side. Never ship it to a browser or app.
Integration key and audience
playerpal.sh
# Linking and activity routes use the integration key PlayerPal issued with
# your integration, plus the ID of the operator or integration you act for.
INTEGRATION_KEY="${PP_INTEGRATION_KEY:-}"
OPERATOR_ID="${PP_OPERATOR_ID:-}" # pp_opr_… (operators)
INTEGRATION_ID="${PP_INTEGRATION_ID:-}" # pp_int_… (game providers)
Environment
export PP_API_BASE="https://<playerpal-api-host>"
export PP_INTEGRATION_KEY="<your-integration-key>"
export PP_OPERATOR_ID="pp_opr_XXXX" # operators
export PP_INTEGRATION_ID="pp_int_XXXX" # game providers

4Exchange the result

POST/v1/link-results/exchangeExchange a one-time link result code for the player aliasReference →

Your backend exchanges the code for the alias. The code works once, and only while the intent is still valid; after that the call returns 404 result_not_found.

Store player_id against your local player. Pass each pp_plr_prv_… alias in provider_aliases to the matching game provider in your existing game launch data, so game launch never calls PlayerPal.

Checkpoint

You get 201 with a player_id starting pp_plr_opr_, and one entry in provider_aliases for each integration you requested. Exchanging the same code again returns 404 result_not_found.

POST /v1/link-results/exchange
playerpal.sh
# When the player confirms, the PlayerPal link page posts a one-time result code to your page (window.postMessage, origin-checked against return_origin).
curl -sS -X POST "$API/v1/link-results/exchange" \
-H "x-api-key: $INTEGRATION_KEY" -H "x-operator-id: $OPERATOR_ID" \
-H 'Content-Type: application/json' \
-d "{\"result_code\":\"$RESULT_CODE\"}"
# 201 {player_id, status, provider_aliases, ...}: store player_id against your account
Response201
{
"connection_id": "<id>",
"player_id": "pp_plr_opr_XXXXXXXXXXXXXXXXXXXXXXXXXX",
"status": "active",
"effective_from": "2026-10-15T12:01:30.000Z",
"provider_aliases": [
{ "integration_id": "pp_int_XXXX", "player_id": "pp_plr_prv_XXXXXXXXXXXXXXXXXXXXXXXXXX" }
]
}

5Keep aliases in sync

GET/v1/aliases/{playerAlias}Status of a player alias issued to your audienceReference →

The player Unlinks from their PlayerPal Account, not from your site. Check an alias with x-operator-id for an operator alias or x-integration-id for a provider alias. The answer is active or revoked; an alias outside your audience returns 404 alias_not_found, the same as an unknown one.

After an Unlink:

  • The alias status becomes revoked. The alias is never reassigned, and a new Link mints a new alias.
  • Activity before the Unlink time can still qualify; activity at or after it does not earn Points.
  • Facts sent for a revoked alias are rejected as player_ref_unknown.
  • Unlinking an operator also revokes the provider aliases issued for that operator's integrations. A provider-led Unlink affects only that integration.
  • Play at your site continues, and Points already earned stay with the player.

PlayerPal can send you an Unlink notice; see Webhooks. Because a notice can be missed, check alias status when you need certainty.

GET /v1/aliases/{playerAlias}
playerpal.sh
curl -sS "$API/v1/aliases/$ALIAS" \
-H "x-api-key: $INTEGRATION_KEY" -H "x-operator-id: $OPERATOR_ID"
# 200 {"status":"active"} or {"status":"revoked"}
Response200
{ "status": "revoked" }
Not your alias404
{
"statusCode": 404,
"message": "alias_not_found",
"error": "Not Found"
}

The aliases you receive​

AliasScopeWho can resolve it
pp_plr_opr_…One player at one operatorThat operator and PlayerPal
pp_plr_prv_…One player at one provider-operator integrationThat integration and PlayerPal

Aliases are random 128-bit values, so two partners cannot match players by comparing them. The same player at Provider A × Operator A and Provider A × Operator B has two different provider aliases. Aliases are minted only for the audiences named on the completed intent; an integration added later has no alias for existing players yet.

Planned

A call for operators to request a provider alias for an integration enabled after the original Link is designed but not built.

Provider-led linking​

A game provider can start a Link for one provider-operator integration without the operator's backend.

Operator-ledProvider-led
Audience headerx-operator-idx-integration-id
Bodyoperator_assertion, return_origin, optional requested_provider_integrationsreturn_origin only
Exchange headerNonex-integration-id
You receivepp_plr_opr_… plus a pp_plr_prv_… per requested integrationOne pp_plr_prv_… for your integration

A provider-led request that also sends x-operator-id, operator_assertion, requested_provider_integrations, operator_id or integration_id is rejected with scope_override_rejected. Scope always comes from your credentials and configuration, never from a body field.

{
"integration_id": "pp_int_XXXX",
"player_id": "pp_plr_prv_XXXXXXXXXXXXXXXXXXXXXXXXXX",
"status": "active",
"effective_from": "2026-10-15T12:01:30.000Z"
}

Errors​

ResponseCauseFix
400 return_origin_required, return_origin_invalidMissing origin, or one with a path, query or fragmentSend a bare origin such as https://casino.example
400 operator_assertion_requiredNo assertion on an operator-led intentSign and send operator_assertion
400 assertion_invalidBad signature, algorithm or aud, or a missing iss, sub or jtiSign with a key in your registered key set and include every claim
400 assertion_expiredAssertion older than five minutes or with a longer lifetimeSign a fresh assertion per intent
400 assertion_iss_mismatchiss differs from x-operator-idUse your operator ID in both
400 assertion_forbidden_claimsPersonal data claims in the assertionRemove them; send only an opaque sub
400 jwks_unavailablePlayerPal could not load your key setCheck the key set you registered is reachable and not empty
400 integration_not_authorized_for_operatorA requested integration is not yoursRequest only integrations configured for your operator
400 scope_override_rejectedOperator fields on a provider-led requestSend only return_origin
400 result_code_requiredNo result_code in the exchangePass the code from the link page
400 provider_alias_missingThe Link has no alias for your integrationCheck you exchanged a code from your own intent
400 player_alias_required, audience_requiredAlias status call without an alias or audience headerSend the alias and x-operator-id or x-integration-id
401 assertion_replayA jti was reusedGenerate a new jti per assertion
401 machine_auth_requiredIntegration key missing or wrongSend it as x-api-key or Authorization: Bearer
404 party_not_found, integration_not_foundUnknown operator or integration IDUse the IDs PlayerPal gave you
404 result_not_foundCode already used, unknown or expiredStart a new intent
404 alias_not_foundAlias unknown or not issued to youTreat the player as not linked

The error code is in the message field. See Idempotency and errors.