Deprecated: do not build new integrations on SMS. Use hosted approval or QR device approval.

Legacy SMS PIN endpoints (deprecated)

This page is kept only for partners whose existing integrations still call the legacy PIN endpoints to clear the sender-approval stage of a cross-game transfer or a peer send. Both endpoints still answer. No removal date is set. Nothing new should be built on them.

Where to go instead

The sender proves who they are with a passkey on INVO’s hosted approval page (web and mobile) or a QR device approval (consoles, TVs, desktop launchers), and your side calls POST /api/sdk/transfers/{id}/approve or POST /api/sdk/send/{id}/approve with that proof. The passkey and device-approval paths are live on every transaction, including one whose initiate response carried verification_method: "sms".

The endpoints, for partners still on them

Transfer

POST $BASE/api/transfers/verify-sms
X-Game-Secret-Key: <game secret>
Content-Type: application/json

{ "transaction_id": "TXN_1757000000_AB12CD", "sms_pin": "483920" }

200 OK
{
  "status": "success",
  "transaction_id": "TXN_...",
  "new_balance": "950.00",
  "claim_code": "KJMRS-47281",
  "claim_instructions": {
    "message": "...",
    "target_game_id": "...", "target_game_name": "...",
    "claim_code_expires_at": "2026-09-05T18:10:00+00:00"
  },
  "transfer_summary": { "amount_initiated": "50.00", "net_amount_for_claim": "45.00", "fees_deducted": "5.00",
                        "source_player_current_available_balance": "950.00" },
  "order_id": "TFRO_..."
}

Peer send

POST $BASE/api/currency-sends/verify-sms
X-Game-Secret-Key: <game secret>
Content-Type: application/json

{ "transaction_id": "TXN_1757000000_AB12CD", "sms_pin": "483920" }

200 OK
{
  "status": "success",
  "transaction_id": "TXN_...",
  "new_balance": "950.00",
  "claim_code": "KJMRS-47281",
  "claim_instructions": {
    "message": "...",
    "receiving_game_id": "...", "receiving_game_name": "...",
    "claim_code_expires_at": "2026-09-05T18:10:00+00:00",
    "receiver_notified": true
  },
  "send_summary": { "amount_sent": "50.00", "net_amount_for_claim": "45.00", "fees_deducted": "5.00",
                    "sender_current_available_balance": "950.00" },
  "order_id": "ORD_1757000000_A1B2C3D4"
}

Each reaches exactly the same state as the matching /approve endpoint: pending_claim, with the same claim code and the same webhooks. The collecting side is unchanged either way, see Collecting, stage by stage.

AnswerMeansAction
400 invalid PINWrong code. The body carries attempts_remaining.Show the remaining attempts. The PIN locks out after three.
400 expired PINThe approval window closed.Start again; the reservation is swept back to the sender.
403 HIGH_VALUE_REQUIRES_APPThis amount is above the ceiling for the PIN path.Approve with a passkey or QR device approval.
403 PASSKEY_RECOVERY_COOLDOWNMoney out is held after a passkey recovery.Show retry_after and stop.
202 GUARDIAN_APPROVAL_PENDINGA minor account whose guardian has not answered. Not a failure, and no PIN attempt is used.Poll /api/transactions/{id}/approval-status, then retry the same call.
410 GUARDIAN_APPROVAL_REJECTED / _EXPIREDTerminal.Stop.
429Too many verification attempts. Carries retry_after.Back off for the stated time.

Re-sending the PIN

POST $BASE/api/transfers/resend-pin
POST $BASE/api/currency-sends/resend-pin
X-Game-Secret-Key: <game secret>
{ "transaction_id": "TXN_..." }

200 { "status": "resent" }
429 { "error": "resend_cooldown", "retry_after": 30 }
400 { "error": "pin_expired" }

These exist only to support the legacy path. resend-pin is not the same as POST /api/sdk/send/{id}/resend-claim, a live endpoint which re-delivers the receiver’s claim link on the original channel after a send has been approved.