Approve a Send, the legacy SMS PIN
This page documents the legacy fallback for clearing a peer send’s sender-approval stage: a numeric PIN sent by text and typed back. It still works, and it is being retired. If you are integrating now, use the passkey or device-approval path instead.
Where to go instead
A send is approved by the sender proving who they are with a passkey (web and mobile) or a device approval grant (consoles, TVs, native Steam and desktop builds), and then your side calling POST /api/sdk/send/{id}/approve with that proof. That path is stronger, costs nothing to deliver, and is the one the platform is built around.
Why this is being retired
A code in a text message is bearer value: it authorises whoever reads the message first, and a number can be taken over. It is also the most expensive channel we have. Every other notification in these flows has already moved off it, the receiving title is told by webhook, consent and guardian approval go by email, and a new phone is vouched for by a code shown on the player’s own screen. The one message still sent as a matter of course is the claim link to a recipient we can reach no other way, and that is because a phone number is all the sender gave us.
Do not build a new integration on this endpoint. No removal date is set; treat it as legacy and plan the passkey path.
The endpoint, for partners still on it
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"
}It reaches exactly the same state as /api/sdk/send/{id}/approve: pending_claim, the recipient notified, and the same transfer.claim_pending webhooks to both titles. The collecting side is unchanged either way, see Collecting, stage by stage.
| Answer | Means | Action |
|---|---|---|
400 invalid PIN | Wrong code. The body carries attempts_remaining. | Show the remaining attempts. The PIN locks out after three. |
400 expired PIN | The approval window closed. | Start a new send; the reservation is swept back to the sender. |
403 HIGH_VALUE_REQUIRES_APP | This amount is above the ceiling for the message-based path. | Approve with a passkey or device grant, the path you should be on anyway. |
403 PASSKEY_RECOVERY_COOLDOWN | Money out is held after a passkey recovery. | Show retry_after and stop. |
202 GUARDIAN_APPROVAL_PENDING | A minor account whose guardian has not answered. Not a failure. | Poll /api/transactions/{id}/approval-status, then retry. |
410 GUARDIAN_APPROVAL_REJECTED / _EXPIRED | Terminal. | Stop. |
429 | Too many verification attempts. Carries retry_after. | Back off for the stated time. |
Re-sending the 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" }This exists only to support the legacy path. It is not the same thing as POST /api/sdk/send/{id}/resend-claim, which re-delivers the recipient’s collect notification after a send has been approved and is documented on Peer Send.
If you are reading this to build something
You are on the wrong page. verification_method: "sms" on an initiate response tells you a fallback PIN was sent, it does not tell you to use it. The passkey and device-grant paths are live on that same transaction, and they are what your integration should call.