Cross-Game Transfers
Enable secure currency transfers between different titles in your ecosystem, whether you run a game or a platform. Players move their virtual currency across title boundaries and approve each transfer with a passkey or a QR device approval.
Overview
How Cross-Game Transfers Work
- Initiate Transfer: Source title initiates transfer with player verification and target recipient phone
- Approval: The player approves with a passkey or a QR device approval, and your side calls the approve endpoint
- Collect at the destination: the transfer waits in the player's pending list in the destination title (
GET /api/sdk/transfers/pending), and the player collects it there withPOST /api/sdk/transfers/{id}/confirm-receipt, using a passkey or a QR device approval - Completion: Funds are credited and both titles receive their fee shares. A transfer nobody collects returns to the player when the claim window closes
The player's phone is required
Cross-game transfers require target_player_phone at initiation. The destination matches the transfer to the player by that phone.
- • Transfer initiation now requires
target_player_phoneparameter - • Collecting matches the player's current phone at the destination against the addressed number
- • The claim code is a legacy fallback being retired, not the collection path; see Claim Code (fallback)
Currency moves between games and platforms
Currency moves freely across the network. Games and platform tenants (video, creator and social platforms) exchange currency with each other in both directions, through transfers and sends, exactly as two games do. Destination lists include both kinds of tenant.
- •
409 GAME_TO_PLATFORM_BLOCKEDand409 PLATFORM_TO_GAME_BLOCKEDare returned only if INVO restricts movement between games and platforms for the network. Nothing is reserved when either fires.
Payment routes still differ by type: platform tenants buy currency through the card route only. See Platform Partners.
Currency cannot move into a platform store title from outside it
The rule is about currency moving between titles, not about what a player buys with it. A title distributed through a platform store (a per-title setting INVO manages) cannot receive currency transferred or sent from a title outside that store. Currency bought through a platform store is subject to that store's rules and can only move between titles on that store. Titles that are not store-distributed are not affected.
- • Transferring or sending from a title outside the store into a store-distributed title returns
409 NON_STEAM_VALUE_INTO_STEAM_BLOCKED - • Moving currency bought through a platform store to a title outside that store returns
409 STEAM_VALUE_NON_TRANSFERABLE: currency bought through a platform store can only move between titles on that store. Handle it. - • Destination lists already leave out store-distributed titles a non-store title cannot reach, so a picker built from them will not surface this error. Each destination's
accepts_steam_origin_valuesays whether it accepts store-bought currency; read it rather than assuming
✅ Key Benefits
- • Passkey or QR device approval
- • Automatic fee distribution
- • Real-time balance updates
- • Comprehensive fraud protection
- • Support for different currencies
⚡ Fee Structure
- • Total Fee: 10% of transfer amount
- • Source Game: 3.5% fee share
- • Target Game: 3.5% fee share
- • Platform: 3% platform fee
Authentication
All transfer API endpoints authenticate with your title's secret key:
Transfer Flow
Initiate Transfer
POST /api/transfers/initiate-transferRequest Body:
{
"client_request_id": "unique_transfer_id_123",
"source_player_name": "John Doe",
"source_player_email": "john@example.com",
"source_player_phone": "+1234567890",
"target_player_email": "jane@example.com",
"target_player_phone": "+0987654321",
"target_game_id": "456",
"amount": "100.00"
}Success Response:
{
"status": "success",
"message": "Transfer initiated. Approve on your enrolled device to continue.",
"transaction_id": "TXN_1234567890",
"order_id": "TFRO_1640995200_ABC123",
"transfer_details": {
"source_game": "Game A",
"target_game": "Game B",
"target_game_id": "456",
"currency": "Gold Coins",
"currency_id": 1,
"amount_initiated": "100.00",
"fees_preview": {
"total_fee": "10.00",
"source_game_fee": "3.50",
"target_game_fee": "3.50",
"invo_fee": "3.00",
"net_amount": "90.00"
}
}
}Approve the transfer
POST /api/sdk/transfers/{id}/approveThe player proves who they are with a passkey (web and mobile) or a QR device approval (consoles, TVs and desktop builds), and your side calls the approve endpoint with that proof. The full request, every factor and every answer are on Cross-Game Transfer, stage by stage.
Success Response:
{
"status": "approved",
"next": "pending_claim",
"transaction_id": "TXN_1234567890",
"claim_code": "KJMRS-47281",
"claim_code_expires_at": "2024-01-02T10:00:00Z",
"claim_delivery": "email"
}claim_delivery says how the claim code also reached the sender: email (mailed to the sender’s own bound address), none (nothing sent; use the code in this response), or sms (a legacy value). The same code is also returned to the sending game by GET /api/transfers/{id}/status once approved.
Guardian Approval Holds
When the transfer was initiated by a minor with a guardian on file, the approve endpoint instead returns one of:
- • 202
GUARDIAN_APPROVAL_PENDING: held until the guardian approves; the request goes by email where one is verified - • 410
GUARDIAN_APPROVAL_REJECTED/GUARDIAN_APPROVAL_EXPIRED: guardian rejected, or the approval window lapsed - • 503
GUARDIAN_APPROVAL_CHECK_UNAVAILABLE: approval status could not be confirmed; retry shortly
Collect at the destination
The destination title shows the transfer from the player's pending list and collects it with POST /api/sdk/transfers/{id}/confirm-receipt (passkey or QR device approval). Every call and answer is on Collecting, stage by stage. The claim-code route below is a legacy fallback being retired, kept for a player with no account at the destination; do not build new screens on it.
POST /api/transfers/claim-transferRequest Body:
{
"claim_code": "KJMRS-47281",
"target_player_name": "Jane Smith",
"target_player_email": "jane@example.com",
"target_player_phone": "+1987654321",
"target_currency_id": "2"
}Success Response:
{
"status": "success",
"message": "Transfer claimed successfully.",
"transaction_id": "TXN_1234567890",
"transfer_details": {
"amount_received": "90.00",
"source_game": "Game A",
"target_currency": "Silver Coins",
"target_player": "Jane Smith",
"new_balance": "590.00"
},
"completion_time": "2024-01-01T12:30:00Z",
"order_id": "TFRO_1640995200_ABC123"
}Additional Endpoints
Get Transfer Status
GET /api/transfers/{transaction_id}/statusCheck the current status of a transfer transaction.
Get Transfer History
GET /api/transfers/transfer-history?player_email=user@example.comRetrieve transfer history for a specific player.
Security Status
GET /api/transfers/security-status?player_email=user@example.comCheck security restrictions and rate limits for a player.
Security Features
🛡️ Fraud Protection
- • Passkey or QR device approval on every transfer
- • Rate limiting per player and IP
- • Velocity limits (hourly/daily)
- • Duplicate transfer detection
- • Circuit breaker protection
⚙️ Rate Limits
- • Request limits: per endpoint, per player and per title; see Rate Limits
- • On a 429: wait the
Retry-Afterseconds - • Daily Amount: one platform-wide limit per player per day, counted across all titles and set by INVO (not per title)
Error Handling
Common Error Responses
400 - Bad RequestMissing required fields, invalid amounts, or malformed data
401 - UnauthorizedInvalid or missing secret key
404 - Not FoundTransaction, title, or player not found
409 - ConflictDuplicate client_request_id or transfer already processed
429 - Too Many RequestsRate limit exceeded, includes retry_after header
Best Practices
💡 Implementation Tips
- • Always use unique client_request_id values to prevent duplicates
- • Implement proper error handling for all API responses
- • Store transaction_id for status checking and support
- • Validate phone numbers before initiating transfers
- • Handle rate limit responses gracefully with exponential backoff
- • Monitor transfer completion rates and failure reasons
⚠️ Important Notes
- • A transfer nobody collects before the claim window closes returns to the source player
- • All timestamps are in UTC format
Testing in Sandbox
Use the sandbox environment for testing transfers without real charges: