FAQ / Approval and Verification
Approval and Verification: Questions and Answers
In-app verification, device enrollment, approving in app, device approval on consoles, passkeys and guardian approval. 74 questions, grouped by the documentation page each one is about. Where a response or webhook carries a figure, read it there rather than recomputing it.
In-App Verification
Read the In-App Verification page
Which endpoint does the device use to get a player token, and how is that token refreshed?
The device never mints its own token: your server calls POST /api/sdk/player-token with X-Game-Secret-Key and hands the returned token to the device, which sends it as Authorization: Bearer <token> on every /api/sdk/* call. The token lives 15 minutes and there is no refresh call. On a 401 SDK_TOKEN_EXPIRED (or 403 PLAYER_TOKEN_STALE), have your server mint a new one and retry once, without re-prompting the player.
Which endpoint registers the device's hardware public key, and does the private key ever leave the device?
POST /api/sdk/device/register, authenticated with the player token, with device_fingerprint, device_public_key and key_algorithm. The keypair is generated in the device's secure hardware and only the public key is sent. The private key never leaves the device and is never transmitted to INVO.
What does GET /api/sdk/transfers/pending return, and why is it authoritative?
It returns {"pending": [...]}, every item awaiting this player in the token's title: identity_gate items (a transfer or send the player started and must approve) and receiving_confirm items (value addressed to the player, waiting to be collected). Each item has transfer_id, kind, flow, amount, currency, counterparty_game, expires_at, step_up_required, held and hold_reason. It is authoritative because it is read live from the transaction records for exactly this player's token, while pushes and webhooks can be delayed, duplicated or out of order. Always re-read it before rendering a prompt.
Which approve endpoint applies to a self-transfer, and which to a send?
A self-transfer (an item with flow: "transfer") is approved at POST /api/sdk/transfers/{id}/approve. A player-to-player send (flow: "send") is approved at POST /api/sdk/send/{id}/approve. Both take the same body and the sender's player token.
Who calls confirm-receipt, the sender or the receiver?
The receiver. Confirm-receipt is called with the recipient's player token, minted by the receiving title (POST /api/sdk/send/{id}/confirm-receipt for a send, POST /api/sdk/transfers/{id}/confirm-receipt for a cross-game transfer). The sender's only call is approve.
In what order do device/begin, poll and approve run on the QR path?
First POST /api/sdk/approvals/device/begin returns a device_code, a user_code and a QR URL. Then you call POST /api/sdk/approvals/device/poll with the device_code until it answers status: "approved". Last, you call the flow's approve or confirm-receipt endpoint with {"device_code": "..."}. The poll proves who approved; only the final call moves money.
How often should the client poll /pending?
Poll on app open, when the app returns to the foreground, and on a light interval while it is active. Every 20 to 30 seconds is plenty while a collect or approve screen is open.
Which webhooks should my backend use for closed-app alerts?
Subscribe to transfer.claim_pending. The copy with direction: "inbound" tells the receiving title that value is waiting, on both rails, and carries to_phone and to_identity_id so you can attribute it. Use it to fire your own push. INVO sends server-to-server webhooks and does not push to devices. Treat the push only as a wake-up and have the app re-read /api/sdk/transfers/pending.
Which platforms use a passkey directly, and what OS versions are required (iOS 16+, Android 9+)?
Mobile apps and mobile web on iOS 16+ and Android 9+ (Face ID, Touch ID, fingerprint) and desktop web (Touch ID, Windows Hello) can use a passkey directly. On mobile apps the recommended route is INVO's hosted page opened in the system browser (channel: "app_browser"). Consoles, TVs and native desktop game clients cannot run a passkey in the client and use the QR device approval flow.
When should a client use QR device approval, and when the hosted email fallback?
Use QR device approval whenever the client cannot run a passkey in-process: consoles, TVs, desktop launchers and native desktop clients, or a browser that blocks the popup. The emailed hosted link is for remote consents where nobody is in your client to approve, such as a guardian approving a minor's transfer. It is not a substitute for the player's own approval.
Is in-app verification enabled per title, and does an existing initiate or claim integration need changes?
Yes, there is a per-title switch. It is on by default for new titles, and a title that is switched off gets 403 sdk_not_enabled_for_tenant when it mints a player token. Your existing initiate and claim calls do not change: in-app verification replaces only the middle step, the sender's approval, with a passkey, device signature or device grant.
Device Enrollment
Read the Device Enrollment page
What headers and body does POST /api/sdk/player-token need, and when should I mint a new token?
Send X-Game-Secret-Key from your server only, with player_email (required), player_phone (recommended, international format) and optionally player_name. A player who is new to your title is created only when player_phone is sent. Mint a new token when the old one expires (15 minutes), on 401 SDK_TOKEN_EXPIRED or 403 PLAYER_TOKEN_STALE, and right before an approve step. If initiate returned identity_rekeyed: true, use the player_token that came back with it.
What do 404 player_not_found and 403 sdk_not_enabled_for_tenant mean on token mint?
404 player_not_found means no player with that email exists in your title and you did not pass player_phone. Pass it and the player is created. 403 sdk_not_enabled_for_tenant (code: "TENANT_NOT_MIGRATED") means in-app verification is switched off for your title, so it cannot use the /api/sdk/* approval surface. Ask INVO to switch it on.
What does enrollment/begin send, and how do I tell the player where the code went using channels?
POST /api/sdk/device/enrollment/begin (player token) sends a 6-digit code, valid for 10 minutes, to the email address on file. It returns {"status": "sent", "channels": [...]}, which lists the channels that actually carried the code. Base your screen text on that list (for example "check your email" when it is ["email"]) instead of assuming a channel.
What does enrollment/verify return for a wrong code?
400 with {"error": "invalid_or_expired_code", "code": "ENROLLMENT_CODE_INVALID"}. The same answer covers an expired code and a code that has used up its attempts, so offer to send a new code.
What is the required body for /api/sdk/device/register?
Only device_fingerprint is required: a stable per-device id of 128 characters or fewer. To enroll an approval key, also send device_public_key (PEM or base64 DER) with key_algorithm. Optional fields are platform (ios, android, web or other), push_token, and rotation_proof, which you send only when replacing an existing key. Invalid values return 400 INVALID_INPUT.
Which key_algorithm values are accepted (EC_P256, ED25519, RSA_2048)?
Exactly those three: EC_P256 (ECDSA with SHA-256), ED25519 and RSA_2048 (PKCS#1 v1.5 with SHA-256). Any other value returns 400 INVALID_INPUT, and every signature must use the algorithm of the registered key.
How is the canonical device_signal message built, and what action value does each endpoint need?
Sign the UTF-8 bytes of "{action}|{transfer_id}|{nonce}|{timestamp}", then send device_signal: {transfer_id, nonce, timestamp, signature}, where timestamp is epoch seconds and signature is base64. action is approve for the /approve endpoints and confirm-receipt for the /confirm-receipt endpoints, so a signature made for one endpoint can never be used at the other.
What timestamp window applies to device_signal (300 seconds), and why must a retry use a fresh nonce?
The timestamp must be within 300 seconds of server time, ahead or behind. Outside that window you get 401 SIGNAL_STALE. Each nonce can be used only once, so a retry with the same signal is refused as 401 SIGNAL_REPLAY. Always generate a new nonce and timestamp and sign again before retrying.
When is a rotation_proof required, and which 409 codes reject a bad one?
You need a rotation_proof when the player already has an active enrolled key and you register a different one, whether on the same device or a new one. The first key and a re-registration of the same key need no proof. The proof is a signature by the current key over "key-rotation|{new_public_key}|{nonce}|{timestamp}". A missing proof returns 409 rotation_requires_proof, and a bad one returns 409 with ROTATION_PROOF_INVALID, ROTATION_PROOF_STALE, ROTATION_PROOF_REPLAY or ROTATION_PROOF_MALFORMED.
Approve in App
Which endpoint lists pending actions, and which header authenticates it?
GET /api/sdk/transfers/pending, authenticated with Authorization: Bearer <player token>. Never send your game secret from the device.
Why should I branch on flow rather than kind?
kind only says whether the item is an approval (identity_gate) or a collection (receiving_confirm). flow ("send" or "transfer") says which endpoint to call, and both rails appear under the same kind. Code that infers the rail from kind leaves half the rows on a collect screen without a working button.
What does held: true mean, and can the player approve that item?
The item is real, but the player cannot act on it from this device yet. hold_reason says why: guardian_pending (a guardian has not answered yet) or device_requires_elevation (this device is not cleared for money actions). Show it as pending and greyed out, not as approvable. An approve call on a guardian-held item returns 202 GUARDIAN_APPROVAL_PENDING.
When is step_up_required present, and what should the client do when it is true?
Every pending item includes step_up_required, a hint based on the item's value. When it is true, prompt the player for a biometric or app PIN before approving and send the result as biometric_verified. The approve endpoint makes the final decision and may still return 202 STEP_UP_REQUIRED when risk step-up is enabled for your title.
What body fields does the approve call need, and which is conditional?
Send exactly one factor: device_fingerprint + device_signal (mobile device key), webauthn_assertion (passkey), or device_code (from an approved device grant). biometric_verified is the conditional field: send it with the honest result when a step-up was required. On the passkey and device-code paths the factor already counts as the step-up.
What should I send for biometric_verified if the player cancels the step-up?
Send false. Report the result honestly and let INVO decide what happens, and never treat the action as complete on the device.
How should I respond to 202 STEP_UP_REQUIRED versus 202 RISK_HOLD?
202 STEP_UP_REQUIRED (status: "step_up_required") means run the biometric or PIN prompt, sign a fresh device_signal and retry with biometric_verified: true. 202 RISK_HOLD (status: "held_for_review") means the action is held for review: do not retry, and show a pending state. Neither one moved money.
Should I retry on 410 GUARDIAN_APPROVAL_REJECTED or EXPIRED, and on 503 GUARDIAN_APPROVAL_CHECK_UNAVAILABLE?
Do not retry either 410. Both are final for that transaction, so show the message and let the player start a new transfer if they want. 503 GUARDIAN_APPROVAL_CHECK_UNAVAILABLE is temporary: retry shortly with a fresh signature.
What does 503 sdk_verification_disabled indicate?
The in-app verification surface is switched off in that environment, so every /api/sdk/* approval call answers this way. Nothing moved. Fall back to your existing verification path and contact INVO. A title that is switched off on its own gets 403 sdk_not_enabled_for_tenant at token mint instead.
What do the verification_state values mean, and when should I stop polling?
On GET /api/transfers/{id}/status and GET /api/currency-sends/{id}/status (server side, X-Game-Secret-Key): awaiting means the sender has not approved yet. approved means the approval succeeded and the transaction is waiting to be claimed. completed means it was claimed and settled, expired means the window lapsed, and failed means verification failed. unknown covers any other status. Stop polling at approved if you only run a sender waiting panel, and at completed, expired or failed otherwise.
What should I do with a 401 SIGNAL_* response?
Nothing moved. For SIGNAL_STALE, SIGNAL_REPLAY, SIGNAL_INVALID or SIGNAL_MALFORMED, sign again with a new nonce and current timestamp, and check the device clock if SIGNAL_STALE repeats. For SIGNAL_TXN_MISMATCH, check that you signed the same transfer_id as the URL. SIGNAL_KEY_INVALID or SIGNAL_ALGO_UNSUPPORTED means the registered key or algorithm is wrong, so re-register the device.
Confirm Receipt
Why does kind: receiving_confirm not identify the rail?
receiving_confirm means only "something to collect", and both rails appear under it: peer sends and cross-game transfers. The rail is in flow: "send" goes to /api/sdk/send/{id}/confirm-receipt and "transfer" goes to /api/sdk/transfers/{id}/confirm-receipt.
Which flow values go to device/begin for a receipt, and why does a grant begun as send fail at confirm-receipt?
For a receipt, begin the grant with flow: "send_receipt" (pending flow: "send") or flow: "transfer_receipt" (pending flow: "transfer"). A device code is bound to the one transaction and the one flow it was started for. A grant started as send or transfer is the sender's approval, so confirm-receipt refuses it with 400 DEVICE_APPROVAL_NOT_APPROVED.
Which 403 codes (not_intended_receiver, WEBAUTHN_NOT_ENABLED_FOR_TENANT, WEBAUTHN_NO_CREDENTIAL) call for a fallback?
403 WEBAUTHN_NOT_ENABLED_FOR_TENANT is the normal answer for a title without its own verified passkey domain, which is every new title. Switch to the device grant (send_receipt or transfer_receipt) or the web SDK's approveHosted(). WEBAUTHN_NO_CREDENTIAL comes back as a 400, not a 403, and means the player has no passkey yet: use the same device grant. 403 not_intended_receiver is not a fallback case: the token belongs to the wrong person or title, or the player's phone does not match the number the value was sent to, so fix the token or the phone.
Which fields appear on transfer.received only for transfers, and which only for sends?
Only the cross-game transfer rail carries gross_amount and fee_breakdown (total_fee, losing_game_fee, gaining_game_fee, invo_fee, net_amount_received). Only the peer send rail carries flow: "currency_send". Both carry amount_received (net), new_balance, currency_name and the sender fields. confirmed_via appears on both when the value was collected through confirm-receipt or the hosted page, and is absent when the legacy claim-code endpoints were used.
Why is the claim code never returned by inbound-pending?
The claim code belongs to the recipient: anyone holding it can collect the value. GET /api/transfers/inbound-pending is a server-side read with your game secret, so it returns only what you need to show and attribute the item (transaction_id, flow, amount, net_amount, source_game, to_phone, to_identity_id, claim_code_expires_at). It never returns the code.
Why should held items stay visible in the collect UI?
A held item is real money waiting for the player, for example on a device not yet cleared for money actions. Show it greyed out with the reason. If you hide it, the player never learns why nothing is arriving, and it can expire and be refunded to the sender.
How should my service dedupe transfer.received when sender and recipient share a title?
Deduplicate every webhook on its idempotency_key, and act on direction. When both sides are in your title you receive both transfer.sent (direction: "outbound") and transfer.received (direction: "inbound"), as well as both copies of events like transfer.claim_pending. Credit your ledger from the inbound copy only.
Device Approval for Consoles, TVs & Browserless Devices
Read the Device Approval for Consoles, TVs & Browserless Devices page
What must the body and Authorization header contain for device/begin, and which flow values are allowed?
Send Authorization: Bearer <player token> with {"transaction_id": "...", "flow": "...", "channel": "..."}. flow is one of transfer, send, send_receipt or transfer_receipt. channel is optional and defaults to qr. An unknown flow or channel returns 400 INVALID_INPUT with valid_flows or valid_channels.
What do the channel values qr, popup and app_browser do, and who may use each?
qr: your server calls begin and the console shows a QR. The page just says approved, and you learn it by polling. popup: only a browser can call begin, through the web SDK's approveHosted(), and the page sends a status-only postMessage to the window that opened it. app_browser: your server calls begin for the Unity or Unreal plugin, and the page returns to invo-sdk-<game_id>://done. Whichever channel you use, poll for the result and settle with the approve or confirm-receipt call.
Why does a server-to-server begin call with channel popup fail with 400 INVALID_INPUT?
A popup grant needs the browser's own https Origin header, which INVO stores as the only window the page may signal. A server-to-server call has no such header, and no body field can supply one. Servers use qr or app_browser.
What do device_code, user_code, verification_uri, verification_uri_complete, expires_in and interval contain?
device_code is the secret your server keeps and presents when polling and settling. user_code is the short code you display (for example K7QP-3MRD). verification_uri is INVO's approval page, and verification_uri_complete is the same page with the code pre-filled. expires_in is the grant's lifetime in seconds (600 today), and interval is the minimum number of seconds between polls (5 today). These are the standard RFC 8628 field names.
How should a console render the QR code and user code?
Encode verification_uri_complete as the QR and print user_code below it for a player who would rather type it at verification_uri. Never display or send the device_code to the client.
How often may a client poll, and what should it do on slow_down?
Poll no faster than interval seconds. Polling too fast returns 400 {"error": "slow_down", "interval": ...}: wait longer before the next poll, then resume at the interval or slower. The other answers are authorization_pending (keep polling), expired_token and invalid_grant (start a new approval), and access_denied (the player declined, so stop).
What does the enrollment object carry, and what are its awaiting_screen, confirmed and denied states?
When a phone without an INVO passkey scans, the authorization_pending body adds enrollment with state, device_label, match_code, recovery and requested_at. awaiting_screen means show a Yes/No prompt on your screen now. confirmed means the answer was Yes, the phone continues to its biometric step, and an approved poll follows. denied means the answer was No and the grant ends with access_denied. The object is absent when the scanning phone is already enrolled.
How is match_code used on the title's Yes/No prompt, and what does recovery: true change?
Show match_code large in the prompt. The phone shows the same code, so the player confirms that the two match rather than guessing from device_label. Send their answer to POST /api/sdk/approvals/device/confirm-enrollment with {"device_code", "decision": "approve" | "deny"}. With recovery: true the phone is replacing a lost INVO passkey, so word the prompt as "Replace your INVO passkey with this phone?". Once recovery completes, money-out approvals may be paused for a while; the refusal tells you when to retry.
Which approve or confirm-receipt endpoint must be called after an approved poll for each flow?
Send {"device_code": "..."} with the player token. send goes to POST /api/sdk/send/{id}/approve, transfer goes to POST /api/sdk/transfers/{id}/approve, send_receipt goes to POST /api/sdk/send/{id}/confirm-receipt, and transfer_receipt goes to POST /api/sdk/transfers/{id}/confirm-receipt. If you stop at the poll, the transaction expires and is refunded.
What does the device_approval.approved webhook carry, and why does it omit the device code?
Its data holds transaction_id, flow, identity_id and method: "device_grant_webauthn". The device code is left out on purpose because webhook bodies end up in logs, and you already hold the code from begin. The event is your cue to make the settle call, not a replacement for it.
How does starting an approval extend a transaction's window?
For transfer and send, begin moves the transaction's approval deadline to at least 5 minutes after the grant expires. A player who approves near the end of the code's life therefore never lands on a transaction that has already expired and been refunded. Receipt flows use the claim window and are not extended.
What does 409 ENROLLMENT_REQUIRES_PROOF require, and how does linkDevice() resolve it?
The player already has an active approval method somewhere else (for example a passkey on your own site, or the INVO app's device key), so a new one cannot be enrolled until that method vouches for it. On the site where the existing passkey lives, call the web SDK's linkDevice() (it wraps POST /api/sdk/device/link/webauthn/begin and /complete). That asserts the existing passkey and creates a single-use link grant, and the player then scans the QR again. Use recovery only if the existing passkey is truly gone.
How is the app_browser return scheme invo-sdk-<game_id>://done derived?
INVO builds it from the game id stored on the grant itself, so the caller cannot supply or change it. The return carries no data. It only tells the app to poll right away.
Platform Step-Up (WebAuthn / Passkeys)
Read the Platform Step-Up (WebAuthn / Passkeys) page
Do web platforms need to configure a domain or relying-party settings for passkeys?
No. Passkeys run on INVO's hosted approval page on INVO's own domain for every title, with nothing to configure. New partner passkey domains are no longer accepted. Titles that verified their own domain earlier keep working until they remove it.
Why does approve/webauthn/begin return 400 WEBAUTHN_NO_CREDENTIAL for a new title, and what fallback runs?
400 WEBAUTHN_NO_CREDENTIAL means the title can run passkeys but this player has not enrolled one yet. Passkeys are enrolled per player, not per title. A brand-new title without its own verified domain gets 403 WEBAUTHN_NOT_ENABLED_FOR_TENANT before that check. In both cases nothing is blocked: use the hosted device approval grant (QR, popup or app browser), which enrolls the player's INVO passkey on the spot, and settle with device_code.
What happens if a player enrolls a second passkey?
Registering the same passkey again is idempotent. Registering a different passkey while one is active returns 409 ENROLLMENT_REQUIRES_PROOF, unless the player first proves the method they already have with a link grant (/api/sdk/device/link/webauthn/* or /api/sdk/device/link/approve).
When does the hosted page return 409 ENROLLMENT_REQUIRES_PROOF with next: "link"?
When a phone scans to enroll an INVO passkey, the identity already has another active approval method (such as a passkey on the partner's own site or the INVO app's device key), and no link grant is waiting. The player must open the site or app where that method lives, choose "Link this phone", and scan again.
How long is the link grant valid, and how do /device/link/approve and /device/link/request differ from the WebAuthn path?
A link grant is single-use and lasts 10 minutes, so send the player straight back to the QR. POST /api/sdk/device/link/approve proves an existing mobile device key with a device_signal. The WebAuthn path (/device/link/webauthn/begin then /complete) proves an existing passkey. POST /api/sdk/device/link/request authorizes nothing: it only pings the player's existing devices so they can approve the link there.
When should recovery be used instead of linking, and what does it do to the existing passkey?
Use recovery only when the player says the passkey is gone (a lost or wiped phone), never just because it lives on another site. Recovery (/api/sdk/device/recover/begin, then /complete with the emailed code) deactivates the identity's passkeys for that title, emails a reset notice to the owner and starts a 24-hour pause on money out. If the identity has no passkey at all, it is refused with 409 PASSKEY_NOT_ENROLLED.
What happens to money-out after a player recovers their passkey?
Money-out may be paused for a while after an account recovery. During the pause, approvals that send money out (/transfers/{id}/approve, /send/{id}/approve, and initiating a transfer or send) return 403 PASSKEY_RECOVERY_COOLDOWN; the body's retry_after and retry_after_seconds tell you when to try again. Signing in, receiving, the pending list and confirm-receipt keep working.
How does a tenant turn on the first-enrolment authorization gate?
Ask INVO. It is a per-title security setting that an INVO administrator turns on for your title, and it is off by default. Before asking, add /api/sdk/device/enrollment/begin and /verify to your flow. Once it is on, a first key or passkey without a verified code returns 409 ENROLLMENT_REQUIRES_AUTHORIZATION.
Are enrollment codes rate limited?
Yes. Requesting codes too often returns 429 rate_limited, which tells you when to retry. Each code is valid only for a short time and allows only a few verify attempts; after that the player requests a new code.
Which channel does enrollment/begin use by default, and does the legacy sms value still send?
Email is the default: the code goes to the email address on file. The legacy body value {"channel": "sms"} still sends a text when a phone is on file, unless the phone was recently changed by the game. SMS is deprecated, so do not build new UI on it, and read channels to see where the code actually went.
Which platforms can run passkey step-up, and which must use QR?
Mobile apps and mobile web (iOS 16+, Android 9+) and desktop web can run a passkey, through INVO's hosted page in the system browser or a popup. Consoles, TVs, desktop launchers and native desktop game clients must use the QR device approval flow, because the engine cannot reach the operating system's authenticator.
Guardian Approval (Minor Accounts)
Read the Guardian Approval (Minor Accounts) page
Which endpoints are gated for minor accounts, and why are claims and item purchases excluded?
The gated endpoints are POST /api/transfers/initiate-transfer, POST /api/currency-sends/initiate-send, POST /api/sdk/transfers/{id}/approve and POST /api/sdk/send/{id}/approve, whichever approval factor is used. Claims are excluded because they bring value in rather than sending it out. Item purchases are excluded because the currency was already authorized when it entered the account, and top-ups are controlled through the payment method.
Can my client detect minor status before initiating?
No. The minor flag is held by INVO and is never returned in an API response. Treat 202 pending_guardian_approval as a normal possible answer to every initiate and approve call.
Which guardian_approval fields should I persist so the UI survives a refresh?
Store the transaction_id and, from the guardian_approval block, approval_id, expires_at, consent_channel and poll_endpoint. After a reload, poll /api/transactions/{id}/approval-status again to recover the current state.
How often should I poll GET /api/transactions/{id}/approval-status, and what header does it need?
Every 5 to 10 seconds, from your server, with X-Game-Secret-Key. Only titles involved in the transaction can read it. Any other caller, or a transaction with no guardian approval, gets 404 not_found.
What are the four state values, and which are terminal?
pending, approved, rejected and expired. approved, rejected and expired are terminal, so stop polling at any of them. After approved, the player still approves with their passkey or device and you make the approve call.
What do the decision_source values email_link, sms_inbound and expiry_job mean?
email_link means the guardian decided on INVO's hosted page from the emailed link. sms_inbound (a legacy value) means they replied from the phone on file. expiry_job means nobody decided before the window closed and INVO expired it. decided_at gives the time.
How should I handle a 503 unavailable from the approval-status endpoint?
503 {"status": "unavailable"} is temporary and not a decision. Keep your waiting screen up and poll again at your next interval.
Do I need a new client_request_id after a rejected or expired decision?
Yes. A rejected or expired transaction is finished. To try again, start a new transaction with a new client_request_id.
When does consent_channel equal sms, and how should waiting-screen copy change?
consent_channel is sms only when the guardian has no verified email or the email could not be sent, so the request went to the phone on file. Say "we emailed your parent" for email and "we contacted your parent" for sms. Polling and the approval window work the same either way.
Should new UI use the guardian resend endpoint?
No. POST /api/sdk/approvals/guardian/{approval_id}/resend is a legacy endpoint that texts the guardian once. It is kept only for integrations that already call it. Design around the emailed link.
How long is the approval window, and does a resend extend it?
The guardian has 15 minutes from initiation. A resend does not extend the window, and the emailed link keeps working until it expires.
How do I flag a sandbox test wallet as a minor and test expiry?
You cannot set the minor flag yourself: ask your INVO contact to flag a sandbox test wallet as a minor with a guardian. To test expiry, start a transfer or send from that player and leave the guardian's email unanswered for the 15-minute window. Polling then reports expired with decision_source: "expiry_job", which can arrive a few minutes after the deadline.
Back to the FAQ hub. Something not answered here? Email partners@invo.network.