Approve in App
The sender approves a transfer or send from inside your app. The device polls the pending list, renders the prompt, and posts a hardware signature instead of an SMS PIN.
1. Poll the pending list
/api/sdk/transfers/pending
The authoritative list of actions awaiting this player
Poll this — there is no push to wire up
Call it on app open / login and periodically while foregrounded. Always render prompts from this response (amount, currency, whether a step-up is needed) — never from anything cached. Authorization: Bearer <player token>
Response
{
"pending": [
{
"transfer_id": "TX_abc123",
"kind": "identity_gate", // sender must approve
"flow": "transfer", // "transfer" → /transfers/{id}/approve
"amount": "100.00", // "send" → /send/{id}/approve
"currency": "GoldCoins",
"counterparty_game": "Game B", // destination
"expires_at": "2026-01-01T18:05:00Z", // display only
"step_up_required": false, // only present when risk step-up is enabled for your game;
// if true, prompt biometric/PIN before approving
"held": false, // true → not actionable yet (e.g. guardian pending)
"hold_reason": null
}
]
}Items with kind: "receiving_confirm" are incoming sends to confirm — see Confirm Receipt. An item with held: true is awaiting something else (e.g. a guardian) — show it as pending, not approvable.
Detecting completion (poll /status)
GET /api/sdk/transfers/pending is for discovery — it's the player's to-do list, authed with the player token. To detect that a specific transfer's sender approval has completed (so you can dismiss a waiting panel), poll that one transfer's /status instead.
/api/transfers/{id}/statusStatus of one self-transfer
/api/currency-sends/{id}/statusStatus of one player-to-player send
These are server-side endpoints — NOT the SDK player token
Unlike the /api/sdk/* calls, both /status endpoints are authed with your X-Game-Secret-Key from your server — never the device player token. Poll them server-side.
They return a verification_state that flips awaiting → approved the moment the sender clears the gate — whether in-app or via SMS:
verification_state | Meaning |
|---|---|
| awaiting | Sender hasn't cleared the gate yet — keep the waiting panel up |
| approved | Sender cleared the gate (in-app or SMS) — dismiss the waiting panel |
| completed | Fully resolved (claimed / credited) |
| expired | Window lapsed — stop polling |
| failed | Did not succeed — stop polling |
Use /status for a waiting panel keyed to one transfer; use /pending for discovery of what's awaiting the player.
2. Approve with a device signature
Pick the endpoint by the item's flow. The body is identical for both.
/api/sdk/transfers/{id}/approveSelf-transfer (your own balance between your games)
/api/sdk/send/{id}/approvePlayer-to-player send (sender side)
Authenticated with the player token
Authorization: Bearer <player token>Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| device_fingerprint | string | Yes | Which enrolled device is approving |
| device_signal | object | Yes | Signature over the action — see Device Enrollment |
| biometric_verified | boolean | If stepped up | Honest result of the biometric / PIN step-up, when required |
Success Response
{
"status": "approved",
"next": "pending_claim",
"transaction_id": "TX_abc123"
}Other responses to handle
| Status | Meaning / what to do |
|---|---|
| 202 STEP_UP_REQUIRED | May be returned when risk step-up is enabled for your game — prompt biometric / PIN, then retry with biometric_verified: true. A no-op when the flag is off. |
| 202 RISK_HOLD | May be returned when risk step-up is enabled for your game — held for review; do not retry, show a pending state. A no-op when the flag is off. |
| 202 pending_guardian_approval | Awaiting the guardian (minor) — show pending |
| 410 GUARDIAN_APPROVAL_REJECTED | Guardian rejected the action — stop; do not retry |
| 410 GUARDIAN_APPROVAL_EXPIRED | Guardian approval window lapsed — stop; do not retry |
| 503 GUARDIAN_APPROVAL_CHECK_UNAVAILABLE | Transient — the guardian check is temporarily unavailable; retry shortly |
| 503 sdk_verification_disabled | In-app verification is off (feature / flag) for this game — fall back to the SMS flow |
| 401 SIGNAL_* | Bad/stale/replayed signature — re-sign with a fresh device_signal |
| 400 (not pending) | Already resolved — treat as "already done" |
After approval
A self-transfer moves to pending_claim and you claim it as usual. A send moves to pending_claim and the receiver is notified to confirm (Confirm Receipt). The downstream claim flow is unchanged.
Example — approve a transfer (Unity)
IEnumerator ApproveTransfer(string playerToken, string transferId,
string deviceFingerprint, object deviceSignal)
{
var url = "https://invo.network/api/sdk/transfers/" + transferId + "/approve";
var body = JsonConvert.SerializeObject(new {
device_fingerprint = deviceFingerprint,
device_signal = deviceSignal, // { transfer_id, nonce, timestamp, signature }
biometric_verified = true
});
using (var req = new UnityWebRequest(url, "POST"))
{
req.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(body));
req.downloadHandler = new DownloadHandlerBuffer();
req.SetRequestHeader("Authorization", "Bearer " + playerToken);
req.SetRequestHeader("Content-Type", "application/json");
yield return req.SendWebRequest();
// 202 STEP_UP_REQUIRED → run biometric, re-sign, retry.
}
}Report biometric_verified honestly. If the step-up fails or the user cancels, send false — Invo decides what happens. Never complete an action locally.