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": "Ship Busters",
                        "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.
400 invalid claim codeWrong code. Carries attempts_remaining.Show the remaining attempts, and stop before the lockout.
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 attempts on this phone, email or network. Carries locked_dimension, retry_after_seconds and a Retry-After header.Stop and show the wait.
404No transfer with that code for your title.Check the code and the destination title.

The lockout is deliberately aggressive and fails closed. 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