How Players Are Matched
This guide explains how POST /api/v1/matches/verified decides which WPPR player each submitted player object resolves to — and how to avoid creating duplicate profiles.
When you submit a match, every player object is resolved independently. The API either links the player to an existing WPPR profile or creates a new one. Understanding the order lets you predict the outcome and send the right fields the first time.
Resolution Order
For each player, the API tries these in order and stops at the first that resolves:
| # | Signal | When it resolves | Notes |
|---|---|---|---|
| 1 | wppr_id | Always, if present | Authoritative. If you know the WPPR ID, send it. |
| 2 | source_player_id | Only if this ID was recorded on a previous successful submission from your source | The mapping is built the first time; a brand-new source_player_id resolves no one on its own. |
| 3 | phone_number | If the number matches an existing profile that can be safely reused | Fallback, to avoid duplicates. Only reached when 1 and 2 don't resolve. If the number is instead tied to a different, already-claimed player, the request is rejected with 400 (see below). |
| 4 | Create new | None of the above matched | A new external / unclaimed profile is created. |
Names are never used to auto-match a player. Two players with the same name are still two different players unless a signal above links them.
The First-Submission Rule
This is the most common source of confusion.
Sending a source_player_id does not, by itself, resolve an existing player. It resolves a player only after a previous successful submission has recorded the link. That link is created the first time you send the player — usually via their wppr_id, or by matching their phone, or by creating a new profile.
The first time you submit a player who already exists in WPPR, include their wppr_id. That establishes the source_player_id → player link. From your next submission onward, source_player_id alone resolves them — no wppr_id or phone_number needed.
Example
First submission — you know the player already exists in WPPR, so you send the wppr_id:
{
"first_name": "Alex",
"last_name": "Example",
"wppr_id": "PTEST01",
"source_player_id": "[email protected]"
}
This creates the mapping [email protected] → PTEST01.
Every later submission — the mapping now exists, so this is enough:
{
"first_name": "Alex",
"last_name": "Example",
"source_player_id": "[email protected]"
}
Why Phone Number Is a Fallback, Not a Key
phone_number is optional and is only used at step 3 — when a player has no wppr_id and no previously-recorded source_player_id. It exists to catch the case where you re-send a known player under a new identifier, so the API reuses their profile instead of creating a duplicate. In most cases a matching number links the player to that existing profile.
A phone number belongs to only one player. In the narrower case where the number is tied to a different, already-claimed player that can't be safely auto-linked, the request is rejected instead. This is what stops two profiles from ending up with the same number.
If a source_player_id maps to a different player, or a phone_number is tied to a different already-claimed player that can't be safely auto-linked, the API returns a 400 that names the blocking wppr_id so you can fix the submission. It never merges two distinct players automatically.
Conflicts and How to Resolve Them
phone_number already linked to another player
You sent a phone_number tied to a different, already-claimed player that couldn't be safely auto-linked, and that player wasn't matched by wppr_id or a known source_player_id.
{
"error": true,
"message": "The submitted phone_number (447700900123) is already linked to PTEST01 (Alex Example). Send wppr_id PTEST01 for this player if it is the same person, or omit phone_number to create a separate player.",
"statusCode": 400
}
Fix:
- Same person? Send
wppr_id: PTEST01for that player. - Different person? Omit or change
phone_number.
source_player_id already linked to another player
You sent a source_player_id that is already mapped to a different player than the wppr_id in the same request.
{
"error": true,
"message": "The submitted source_player_id (partner-player-2) already belongs to P77777 (Mapped Player) for team1.player2 (Player Two), but this request sent wppr_id PCD275. Send wppr_id P77777 with source_player_id partner-player-2, or send a different source_player_id if PCD275 is a different player. If PCD275 should replace P77777, ask support to merge or correct the existing player mapping before retrying.",
"statusCode": 400
}
Fix:
- Same person? Send the
wppr_idthe message names, with thatsource_player_id. - Different person? Use a different
source_player_id. - Mapping is wrong? Ask support to merge or correct it before retrying.
Best Practices
- Send
wppr_idwhenever you know it — it's the only signal that never depends on prior state. - Give every player a stable
source_player_id(email, internal user ID, UUID) and always reuse the same value for the same person. This is what keeps your players consistent over time. - Include
wppr_idon the first submission of a player who already exists in WPPR, so thesource_player_idlink is established from the start. - Treat a
400conflict as actionable: read the blockingwppr_idin the message and correct the submitted identity — don't retry the same payload unchanged.