What Your Server Must Call, In Order
The numbered steps from Peer Send and Cross-Game Transfer, as a tick-list. Work down it; every box is a call you make or a check you do. The receiving side is detailed on Collecting.
The two boxes people skip
The green boxes, approve and confirm-receipt, are the only calls that move money. Everything before them produces a proof of who, and a status read only reports. If your integration ends at “the poll said approved”, every transaction you create will expire and refund, and nothing in your logs will look like an error.
Reading the list
- •
BASEishttps://invo.network, orhttps://sandbox.invo.network/sandboxin sandbox. - • SERVER calls carry
X-Game-Secret-Key, server-side only. - • CLIENT calls, and every
/api/sdk/*call except player-token, carryAuthorization: Bearer <player token>: one player, one title, short-lived, no refresh. - • There is nothing to configure for passkeys: the approval always runs on INVO’s own page.
Peer send, the sending side
- 1.SERVER
POST /api/sdk/player-token, game secretBody hasplayer_emailandplayer_phone(+player_name).created: true= registered now.phone_mismatch: true= update the phone withPOST /api/sdk/players/phone(Player Identity) and mint again;409 PHONE_CHANGE_REQUIRES_VERIFICATIONthere = the player has a verified factor, nothing changed, keep the current number and contact INVO support.404 player_not_foundwithhint: "pass player_phone to create the player"= you left out the phone: mint again with it, or register first withPOST /api/sdk/players. - 2.SERVER
POST /api/currency-sends/initiate-send, game secretStoredtransaction_id. Ifidentity_rekeyed: true, switched to the returnedplayer_token.202= guardian gate, wait for it.403 PASSKEY_RECOVERY_COOLDOWN= stop, showretry_after. Readclaim_delivery_planned("email"|"none"| legacy"sms") and tell the sender how the receiver will collect.receiver_player_emailis optional and recommended. - 3.SERVERMint the sender’s token again, right before approvalThe client gets a fresh token (or step 2’s
player_token), never one minted at login. - 4.CLIENT or SERVERThe sender approves,
flow: "send"Web:approveHosted({ transactionId, flow: "send" })from a click (it also runs step 5). Consoles, desktop launchers, popup blocked:POST /api/sdk/approvals/device/begin, show the QR,POST /api/sdk/approvals/device/polluntil"approved". Mobile app: the plugin’s hosted approval. - 5.SERVER or SDKmoves money
POST /api/sdk/send/{id}/approve, sender’s player tokenSent{ device_code }. Got200 { status: "approved", next: "pending_claim", claim_delivery }.claim_delivery: "email"= the claim link went to the receiver’s email already on file for that phone;"none"= nothing sent, the receiver collects in the game;"sms"= legacy.PLAYER_TOKEN_STALE/SDK_TOKEN_EXPIRED= mint, retry once.TRANSACTION_NOT_PENDING= already done.SEND_APPROVE_FAILED= never retry, read status, quoteerror_ref. - 6.SERVERWebhook
transfer.claim_pending, orGET /api/currency-sends/{id}/statusverification_state: "approved". Still"awaiting"after the player approved = step 5 was never called. - 6b.CLIENToptional
POST /api/sdk/send/{id}/resend-claim, sender’s player tokenLive; re-delivers the claim link on the original channel.200carriesclaim_delivery.409 CLAIM_DELIVERY_WITHHELD= the receiver email is not on file for that phone, nothing is sent, the receiver collects in the game: do not retry.429 RESEND_COOLDOWN= waitretry_after.
Cross-game transfer, the source title
- 1.SERVER
POST /api/sdk/player-token, source game secretBody hasplayer_emailandplayer_phone(+player_name). Handlecreated,phone_mismatchand404as for a send. - 2.SERVER
POST /api/transfers/initiate-transfer, source game secretStoredtransaction_id. Ifidentity_rekeyed: true, switched to the returnedplayer_token.403 TRANSFER_POLICY_VIOLATION= destination not permitted. - 3.SERVERMint the player’s token again, right before approvalFresh token to the client (or step 2’s
player_token). - 4.CLIENT or SERVERThe player approves,
flow: "transfer"Web:approveHosted({ transactionId, flow: "transfer" })from a click. Consoles, desktop launchers, popup blocked: QR device grant, poll to"approved". Mobile app: the plugin’s hosted approval. - 5.SERVER or SDKmoves money
POST /api/sdk/transfers/{id}/approve, player tokenGot200withclaim_code,claim_code_expires_atandclaim_delivery, and stored both the code and its expiry (approve also emails the code to the sender’s own address on file whenclaim_deliveryis"email"). Stale or expired token = mint, retry once.TRANSFER_APPROVE_FAILED= never retry. - 6.SERVERWebhook
transfer.claim_pending, orGET /api/transfers/{id}/statusverification_state: "approved". With the source secret the status also returns the claim code. It has no fee, game-name orcompleted_atfields: amounts come fromtransfer.received.
The claim code came back to you in step 5, and the source title can read it again from the status endpoint.
Both flows, the receiving side
- 7.RECEIVING SERVER
POST /api/sdk/player-token, receiving game secretRecipient’s email and phone, so a first-time recipient is registered there. - 8.RECEIVING CLIENT
GET /api/sdk/transfers/pending, recipient’s tokenTook the item withkind: "receiving_confirm"and branched on itsflow(sendortransfer), never onkind. - 9a.RECEIVING CLIENT or SERVERThe recipient approves,
flow: "send_receipt"or"transfer_receipt"approveHosted()on the web, QR grant elsewhere. A sender’s flow here fails asDEVICE_APPROVAL_NOT_APPROVED. - 9b.RECEIVING SERVER or SDKmoves money
POST /api/sdk/{send|transfers}/{id}/confirm-receipt, recipient’s tokenGot200 { status: "completed", amount_received }.409 receiver_not_enrolled_use_claim_code: send = hosted claim page orclaim-currency; transfer =claim-transfer.CONFIRM_RECEIPT_FAILED= never retry. - 10.SERVERWebhooks
transfer.sentandtransfer.receivedBoth handled, including collects made outside your title (hosted page, Invo app). Deduplicate onidempotency_key.
The claim-code fallback, kept off the list on purpose
These endpoints still work, but they are a fallback, not steps in a new integration. Approval is always the hosted page or a QR grant; any code goes by email.
| Legacy route | Replaced by | Notes |
|---|---|---|
POST /api/currency-sends/claim-currencyPOST /api/transfers/claim-transfer | Step 9b, confirm-receipt; or the hosted claim page (sends only) | A code authorises whoever holds it. Kept for a recipient who cannot use a passkey. |
Next
Every code in this list, with its meaning and required action, is on the error reference.