Claim a Transfer, the fallback route

This page documents the claim-code fallback for collecting a cross-game transfer at the destination. It is not the primary route. It is kept for the case the stronger route cannot serve: a player who has no account in the destination title yet, or no Invo passkey.

How collecting actually works

The player’s client lists what is waiting for her with GET /api/sdk/transfers/pending (her destination-title player token), reads the item’s flow field, and settles with POST /api/sdk/transfers/{id}/confirm-receipt using a passkey or a device grant. No code, nothing typed.

Collecting, stage by stage

Both rails, both surfaces, every call in curl, JavaScript and Python, including the flow field that decides which endpoint to call.

Why the code is the fallback

Receiving is meant to be exactly as strong as sending. A code authorises whoever holds it; a passkey authorises the person. On a transfer the exposure is smaller than on a peer send, the claim code is returned in-band to the caller at approve time, rather than into a message, but it is still the weaker proof, so it should not be the button players reach for first.

Use it when confirm-receipt answers 409 receiver_not_enrolled_use_claim_code, there is no player row for this identity in the destination title, and this route creates one as part of collecting.

The endpoint

POST $BASE/api/transfers/claim-transfer
X-Game-Secret-Key: <DESTINATION game secret>
Content-Type: application/json

{
  "claim_code": "KJMRS-47281",                 // returned by /api/sdk/transfers/{id}/approve
  "target_player_name": "Ada",
  "target_player_email": "ada@example.com",
  "target_player_phone": "+15555550100",       // E.164; must match the number the transfer was addressed to
  "target_currency_id": 34,                    // which of your currencies to credit
  "target_player_id": 4321                     // optional; only when resolving an account-selection prompt
}

200 OK
{
  "status": "success",
  "transaction_id": "TXN_...",
  "new_balance": "1045.00",                    // canonical post-write balance; display this
  "currency_name": "Gold",
  "transfer_details": { "amount_received": "45.00", "source_game": "Example Game",
                        "target_currency": "Gold", "target_player": "Ada", "new_balance": "1045.00" },
  "completion_time": "2026-09-04T18:22:00+00:00",
  "order_id": "TFRO_..."
}

On success it credits the player, completes the transaction, and fires transfer.received to your title and transfer.sent to the source title, identical to the confirm-receipt route. Fees were stamped when the transfer was initiated; nothing is recomputed here.

AnswerMeansAction
200 needs_account_selectionNot an error, despite the 200. The phone matches more than one of your players.Show the masked candidates and re-submit with target_player_id.
404 invalid claim codeNo transfer with that code is waiting in your title (a wrong or mistyped code, or one meant for another title). The body carries only message.Ask the player to check the code. Repeated wrong codes are refused for a while; the response tells you when.
400 Invalid claim code. with attempts_remainingThe code matches a transfer waiting in your title, but the claim did not verify. attempts_remaining says how many tries are left. At 0 the transfer fails and the sender is refunded.Show the remaining count and let the player try again. Never retry automatically.
400 expiredThe claim window closed; the sender is refunded by the sweep.The player must start a new transfer from the source title.
400 WRONG_CLAIM_ENDPOINTThat code belongs to a peer send, not a transfer.The body names expected_endpoint (/api/currency-sends/claim-currency) and the fields it wants.
403 phone mismatchThe submitted phone is not the number the transfer was addressed to.Stop. This is the check that stops a code being redeemed by whoever found it.
409 PHONE_SHARE_APPROVAL_REQUIREDThe number belongs network-wide to a different Invo identity; that person must consent.Follow the consent flow in the body, then retry. Consent is requested by email where a verified address exists.
429 CLAIM_LOCKEDToo many failed claim attempts. Carries retry_after_seconds and a Retry-After header.Stop and show the wait.

Failed attempts are limited. Never let a player try codes in a loop, and never retry automatically on an invalid code, you will lock out the legitimate player.

To find inbound transfers waiting for one of your players without knowing the transaction id, use GET /api/transfers/inbound-pending with your title's secret key and player_email or player_phone. It never returns claim codes, documented on Collecting, stage by stage.

If you are reading this to build a collect screen

You are on the wrong page. Build the screen from GET /api/sdk/transfers/pending. Branch on the item’s flow, and settle with confirm-receipt. Keep this endpoint behind the 409 receiver_not_enrolled_use_claim_code branch.

Go to Collecting, stage by stage