[{"data":1,"prerenderedAt":1530},["ShallowReactive",2],{"content-\u002Fresources\u002Fmore\u002Fstablecoin-payin-api-integration-quickstart":3,"resources-category-stablecoin-payin-api-integration-quickstart":1515},{"title":4,"seoTitle":5,"description":6,"date":7,"updated":8,"category":9,"faq":10,"howto":29,"body":50},"How to integrate a stablecoin payin API: a developer quickstart","Stablecoin payin API integration: developer quickstart","Stablecoin payin API integration in one afternoon: authenticate, verify a customer, link a wallet, take a deposit, and handle webhooks, with cURL.","2026-10-26",null,"payments",[11,14,17,20,23,26],{"q":12,"a":13},"What is the minimum I need to integrate a stablecoin payin API?","An API key, a verified customer, a destination wallet, and a webhook endpoint. On BlindPay you also accept the terms of service for the customer first. With those four objects you can create a payin quote, create the payin, show the payer deposit details, and watch the deposit turn into stablecoins.",{"q":15,"a":16},"How long does it take to build a first payin on BlindPay?","The calls themselves are short: terms of service, customer, wallet, quote, payin. On a development instance the customer is approved automatically and the payin completes about 30 seconds after you create it. Production adds real KYC review and real bank timing, so plan the test run separately from the launch.",{"q":18,"a":19},"Do I use a virtual account or a payin quote to collect money?","Use a payin quote when each payment is a one-off and you want to lock the amount and rate first. Use a virtual account when a customer will receive repeated deposits and you want a permanent account number. Each deposit into a virtual account creates its own payin, so your webhook handler stays the same.",{"q":21,"a":22},"Which BlindPay webhook events should I handle for payins?","Handle payin.new, payin.update, and payin.complete. Add virtualAccount.complete if you use virtual accounts, and customer.update to see KYC status changes. Read the status field in each payload instead of inferring state from the event name, because payin.complete covers delivered, refunded, and failed outcomes.",{"q":24,"a":25},"Is there an SDK for BlindPay payins, or do I call the REST API?","BlindPay ships official SDKs for Node.js, Python, Go, PHP, and Swift, all wrapping the same REST API, plus an OpenAPI 3.1 spec for generating a client in other languages. The cURL calls below work from any language. Check each SDK repository's README for the exact method names before you use one.",{"q":27,"a":28},"How do I test a failed or refunded payin without real money?","On a BlindPay development instance, set the payin quote's request_amount to 66600 ($666.00) to force a failed payin, or 77700 ($777.00) to force a refunded one. Any other amount completes after about 30 seconds. Run both outcomes through your webhook handler and your ledger before you go live.",{"name":30,"steps":31},"How to integrate a stablecoin payin API",[32,35,38,41,44,47],{"name":33,"text":34},"Create a development API key","Create an API key on a development instance in the dashboard and send it as a Bearer token. Keys are scoped to one instance and shown once.",{"name":36,"text":37},"Create and verify a customer","Generate a terms-of-service URL, have the customer accept it, then create the customer with the tos_id. Development customers are approved automatically.",{"name":39,"text":40},"Link a destination wallet","Register the wallet that receives the stablecoins, either an external blockchain wallet (bw_) the customer controls or a managed wallet (bl_).",{"name":42,"text":43},"Collect the deposit","Create a virtual account for a permanent account number, or create a payin quote and a payin to get one-time deposit details for the payer.",{"name":45,"text":46},"Register a webhook endpoint","Register an HTTPS endpoint, fetch its signing secret, verify every call on the raw body, and track the payin from payin.new to payin.complete.",{"name":48,"text":49},"Test the failure paths, then switch to production","Force a failed and a refunded payin with the sandbox amounts, then create a production instance, a new key, and a new webhook endpoint.",{"type":51,"value":52,"toc":1500},"minimark",[53,57,66,71,95,99,102,105,132,140,144,147,182,192,196,203,277,287,466,497,501,504,568,571,641,659,663,666,732,762,787,791,794,874,898,961,996,1000,1003,1063,1100,1201,1205,1211,1260,1276,1280,1283,1376,1386,1390,1393,1423,1427,1450,1466,1470,1490,1496],[54,55,56],"p",{},"To integrate a stablecoin payin API, create an API key, create and verify a customer, and link a destination wallet. Then create a virtual account, or a payin quote and a payin, to show the payer where to send money. Handle webhooks as the deposit becomes stablecoins. BlindPay lets you run all of it on a development instance first.",[54,58,59,60,65],{},"A payin is the \"money in\" direction: a payer sends fiat, and the API delivers stablecoins to a wallet. If the term is new, start with ",[61,62,64],"a",{"href":63},"\u002Fresources\u002Fmore\u002Fwhat-is-a-stablecoin-payin-api","what a stablecoin payin API is",". This guide is the hands-on version, with the exact calls from BlindPay's docs.",[67,68,70],"h2",{"id":69},"key-takeaways","Key takeaways",[72,73,74,78,86,89,92],"ul",{},[75,76,77],"li",{},"Five objects carry the flow: an API key, a customer, a wallet, a payin (or a virtual account), and a webhook endpoint.",[75,79,80,81,85],{},"Amounts are integers in minor units. ",[82,83,84],"code",{},"10000"," means $100.00.",[75,87,88],{},"A payin quote expires after 5 minutes, and a created payin cannot be canceled.",[75,90,91],{},"Development instances approve customers automatically and complete payins about 30 seconds after creation.",[75,93,94],{},"Verify webhook signatures on the raw body, and test a failed and a refunded payin before launch.",[67,96,98],{"id":97},"what-do-i-need-before-i-start","What do I need before I start?",[54,100,101],{},"You need a BlindPay account, a development instance, and an API key for it. You also need an HTTPS endpoint that can receive webhooks. Everything else is created through the API.",[54,103,104],{},"Keep these definitions handy:",[72,106,107,114,120,126],{},[75,108,109,113],{},[110,111,112],"strong",{},"Customer:"," the person or business receiving the stablecoins. Every payin belongs to a customer who passed KYC (know your customer) checks.",[75,115,116,119],{},[110,117,118],{},"Wallet:"," the address that receives the stablecoins. A stablecoin is a token pegged to a currency, such as USDC for the US dollar.",[75,121,122,125],{},[110,123,124],{},"Payin:"," one deposit, created from a quote.",[75,127,128,131],{},[110,129,130],{},"Virtual account:"," a dedicated bank account whose deposits settle to a wallet on their own.",[54,133,134,135,139],{},"The stablecoin delivered on a development instance is USDB, BlindPay's test token. In production you use USDC or USDT. The pieces of the flow are covered in more depth in ",[61,136,138],{"href":137},"\u002Fresources\u002Fmore\u002Fhow-a-stablecoin-payin-works-step-by-step","how a stablecoin payin works step by step",".",[67,141,143],{"id":142},"how-do-i-authenticate","How do I authenticate?",[54,145,146],{},"Send your API key as a Bearer token on every request. Keys are scoped to one instance, so a development key will not work on production, and the dashboard shows a key only once, at creation.",[148,149,154],"pre",{"className":150,"code":151,"language":152,"meta":153,"style":153},"language-bash shiki shiki-themes github-light","curl https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fcustomers \\\n  --header 'Authorization: Bearer YOUR_API_KEY'\n","bash","",[82,155,156,173],{"__ignoreMap":153},[157,158,161,165,169],"span",{"class":159,"line":160},"line",1,[157,162,164],{"class":163},"s7eDp","curl",[157,166,168],{"class":167},"sYBdl"," https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fcustomers",[157,170,172],{"class":171},"sYu0t"," \\\n",[157,174,176,179],{"class":159,"line":175},2,[157,177,178],{"class":171},"  --header",[157,180,181],{"class":167}," 'Authorization: Bearer YOUR_API_KEY'\n",[54,183,184,185,188,189,139],{},"The base URL is the same for development and production. Which environment you hit depends only on which instance the key belongs to. Keep the key on your server and out of client code. Development instances are capped at 40 requests per minute per instance, so a loop that hammers the API will return ",[82,186,187],{},"429"," with ",[82,190,191],{},"development_instance_rate_limit_exceeded",[67,193,195],{"id":194},"how-do-i-create-and-verify-a-customer","How do I create and verify a customer?",[54,197,198,199,202],{},"Two calls: generate a terms-of-service URL, then create the customer with the ",[82,200,201],{},"tos_id"," it produces.",[148,204,206],{"className":150,"code":205,"language":152,"meta":153,"style":153},"curl --request POST \\\n  --url https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Fe\u002Finstances\u002Fin_000000000000\u002Ftos \\\n  --header 'Authorization: Bearer YOUR_API_KEY' \\\n  --header 'Content-Type: application\u002Fjson' \\\n  --data '{\n    \"idempotency_key\": \"\u003Cyour_uuid>\",\n    \"redirect_url\": \"https:\u002F\u002Fexample.com\u002Ftos-accepted\"\n  }'\n",[82,207,208,220,230,240,250,259,265,271],{"__ignoreMap":153},[157,209,210,212,215,218],{"class":159,"line":160},[157,211,164],{"class":163},[157,213,214],{"class":171}," --request",[157,216,217],{"class":167}," POST",[157,219,172],{"class":171},[157,221,222,225,228],{"class":159,"line":175},[157,223,224],{"class":171},"  --url",[157,226,227],{"class":167}," https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Fe\u002Finstances\u002Fin_000000000000\u002Ftos",[157,229,172],{"class":171},[157,231,233,235,238],{"class":159,"line":232},3,[157,234,178],{"class":171},[157,236,237],{"class":167}," 'Authorization: Bearer YOUR_API_KEY'",[157,239,172],{"class":171},[157,241,243,245,248],{"class":159,"line":242},4,[157,244,178],{"class":171},[157,246,247],{"class":167}," 'Content-Type: application\u002Fjson'",[157,249,172],{"class":171},[157,251,253,256],{"class":159,"line":252},5,[157,254,255],{"class":171},"  --data",[157,257,258],{"class":167}," '{\n",[157,260,262],{"class":159,"line":261},6,[157,263,264],{"class":167},"    \"idempotency_key\": \"\u003Cyour_uuid>\",\n",[157,266,268],{"class":159,"line":267},7,[157,269,270],{"class":167},"    \"redirect_url\": \"https:\u002F\u002Fexample.com\u002Ftos-accepted\"\n",[157,272,274],{"class":159,"line":273},8,[157,275,276],{"class":167},"  }'\n",[54,278,279,280,188,283,286],{},"Open the returned URL and accept. The browser lands on your ",[82,281,282],{},"redirect_url",[82,284,285],{},"?tos_id=to_..."," in the address bar. In production, the customer accepts the terms themselves.",[148,288,290],{"className":150,"code":289,"language":152,"meta":153,"style":153},"curl --request POST \\\n  --url https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fcustomers \\\n  --header 'Authorization: Bearer YOUR_API_KEY' \\\n  --header 'Content-Type: application\u002Fjson' \\\n  --data '{\n    \"tos_id\": \"to_000000000000\",\n    \"type\": \"individual\",\n    \"kyc_type\": \"standard\",\n    \"email\": \"email@example.com\",\n    \"tax_id\": \"12345678\",\n    \"address_line_1\": \"8 The Green\",\n    \"address_line_2\": \"#12345\",\n    \"city\": \"Dover\",\n    \"state_province_region\": \"DE\",\n    \"country\": \"US\",\n    \"postal_code\": \"02050\",\n    \"ip_address\": \"127.0.0.1\",\n    \"phone_number\": \"+13022006100\",\n    \"proof_of_address_doc_type\": \"UTILITY_BILL\",\n    \"proof_of_address_doc_file\": \"https:\u002F\u002Fexample.com\u002Fproof-of-address.jpg\",\n    \"first_name\": \"John\",\n    \"last_name\": \"Doe\",\n    \"date_of_birth\": \"1998-01-01T00:00:00Z\",\n    \"id_doc_country\": \"US\",\n    \"id_doc_type\": \"PASSPORT\",\n    \"id_doc_front_file\": \"https:\u002F\u002Fexample.com\u002Fpassport-front.jpg\",\n    \"selfie_file\": \"https:\u002F\u002Fexample.com\u002Fselfie.jpg\"\n  }'\n",[82,291,292,302,310,318,326,332,337,342,347,353,359,365,371,377,383,389,395,401,407,413,419,425,431,437,443,449,455,461],{"__ignoreMap":153},[157,293,294,296,298,300],{"class":159,"line":160},[157,295,164],{"class":163},[157,297,214],{"class":171},[157,299,217],{"class":167},[157,301,172],{"class":171},[157,303,304,306,308],{"class":159,"line":175},[157,305,224],{"class":171},[157,307,168],{"class":167},[157,309,172],{"class":171},[157,311,312,314,316],{"class":159,"line":232},[157,313,178],{"class":171},[157,315,237],{"class":167},[157,317,172],{"class":171},[157,319,320,322,324],{"class":159,"line":242},[157,321,178],{"class":171},[157,323,247],{"class":167},[157,325,172],{"class":171},[157,327,328,330],{"class":159,"line":252},[157,329,255],{"class":171},[157,331,258],{"class":167},[157,333,334],{"class":159,"line":261},[157,335,336],{"class":167},"    \"tos_id\": \"to_000000000000\",\n",[157,338,339],{"class":159,"line":267},[157,340,341],{"class":167},"    \"type\": \"individual\",\n",[157,343,344],{"class":159,"line":273},[157,345,346],{"class":167},"    \"kyc_type\": \"standard\",\n",[157,348,350],{"class":159,"line":349},9,[157,351,352],{"class":167},"    \"email\": \"email@example.com\",\n",[157,354,356],{"class":159,"line":355},10,[157,357,358],{"class":167},"    \"tax_id\": \"12345678\",\n",[157,360,362],{"class":159,"line":361},11,[157,363,364],{"class":167},"    \"address_line_1\": \"8 The Green\",\n",[157,366,368],{"class":159,"line":367},12,[157,369,370],{"class":167},"    \"address_line_2\": \"#12345\",\n",[157,372,374],{"class":159,"line":373},13,[157,375,376],{"class":167},"    \"city\": \"Dover\",\n",[157,378,380],{"class":159,"line":379},14,[157,381,382],{"class":167},"    \"state_province_region\": \"DE\",\n",[157,384,386],{"class":159,"line":385},15,[157,387,388],{"class":167},"    \"country\": \"US\",\n",[157,390,392],{"class":159,"line":391},16,[157,393,394],{"class":167},"    \"postal_code\": \"02050\",\n",[157,396,398],{"class":159,"line":397},17,[157,399,400],{"class":167},"    \"ip_address\": \"127.0.0.1\",\n",[157,402,404],{"class":159,"line":403},18,[157,405,406],{"class":167},"    \"phone_number\": \"+13022006100\",\n",[157,408,410],{"class":159,"line":409},19,[157,411,412],{"class":167},"    \"proof_of_address_doc_type\": \"UTILITY_BILL\",\n",[157,414,416],{"class":159,"line":415},20,[157,417,418],{"class":167},"    \"proof_of_address_doc_file\": \"https:\u002F\u002Fexample.com\u002Fproof-of-address.jpg\",\n",[157,420,422],{"class":159,"line":421},21,[157,423,424],{"class":167},"    \"first_name\": \"John\",\n",[157,426,428],{"class":159,"line":427},22,[157,429,430],{"class":167},"    \"last_name\": \"Doe\",\n",[157,432,434],{"class":159,"line":433},23,[157,435,436],{"class":167},"    \"date_of_birth\": \"1998-01-01T00:00:00Z\",\n",[157,438,440],{"class":159,"line":439},24,[157,441,442],{"class":167},"    \"id_doc_country\": \"US\",\n",[157,444,446],{"class":159,"line":445},25,[157,447,448],{"class":167},"    \"id_doc_type\": \"PASSPORT\",\n",[157,450,452],{"class":159,"line":451},26,[157,453,454],{"class":167},"    \"id_doc_front_file\": \"https:\u002F\u002Fexample.com\u002Fpassport-front.jpg\",\n",[157,456,458],{"class":159,"line":457},27,[157,459,460],{"class":167},"    \"selfie_file\": \"https:\u002F\u002Fexample.com\u002Fselfie.jpg\"\n",[157,462,464],{"class":159,"line":463},28,[157,465,276],{"class":167},[54,467,468,469,472,473,476,477,480,481,484,485,488,489,492,493,496],{},"Save the ",[82,470,471],{},"id"," (",[82,474,475],{},"re_...","). On a development instance the customer is approved automatically, and placeholder document URLs are fine. In production, standard KYC for an individual takes about 60 seconds, while business verification (KYB) is a manual review of 3 hours to 1 business day. The status moves from ",[82,478,479],{},"verifying"," to ",[82,482,483],{},"approved",", ",[82,486,487],{},"rejected",", or ",[82,490,491],{},"compliance_request",", and a ",[82,494,495],{},"customer.update"," webhook announces each change. Do not request quotes until the customer is approved.",[67,498,500],{"id":499},"where-do-the-stablecoins-land","Where do the stablecoins land?",[54,502,503],{},"Register the destination wallet before you quote, because the quote needs its ID. BlindPay offers two destinations for payins.",[505,506,507,526],"table",{},[508,509,510],"thead",{},[511,512,513,517,520,523],"tr",{},[514,515,516],"th",{},"Destination",[514,518,519],{},"ID prefix",[514,521,522],{},"Who holds the keys",[514,524,525],{},"Notes",[527,528,529,550],"tbody",{},[511,530,531,535,540,543],{},[532,533,534],"td",{},"External blockchain wallet",[532,536,537],{},[82,538,539],{},"bw_",[532,541,542],{},"The customer",[532,544,545,546,549],{},"Pass ",[82,547,548],{},"blockchain_wallet_id"," on the quote",[511,551,552,555,560,563],{},[532,553,554],{},"Managed wallet (beta)",[532,556,557],{},[82,558,559],{},"bl_",[532,561,562],{},"BlindPay custodies the balance",[532,564,545,565,549],{},[82,566,567],{},"wallet_id",[54,569,570],{},"Here is a direct-address registration for an external wallet on a testnet. On a development instance only testnet networks are accepted.",[148,572,574],{"className":150,"code":573,"language":152,"meta":153,"style":153},"curl --request POST \\\n  --url https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fcustomers\u002Fre_000000000000\u002Fblockchain-wallets \\\n  --header 'Authorization: Bearer YOUR_API_KEY' \\\n  --header 'Content-Type: application\u002Fjson' \\\n  --data '{\n    \"name\": \"John personal wallet\",\n    \"network\": \"base_sepolia\",\n    \"is_account_abstraction\": true,\n    \"address\": \"0x...\"\n  }'\n",[82,575,576,586,595,603,611,617,622,627,632,637],{"__ignoreMap":153},[157,577,578,580,582,584],{"class":159,"line":160},[157,579,164],{"class":163},[157,581,214],{"class":171},[157,583,217],{"class":167},[157,585,172],{"class":171},[157,587,588,590,593],{"class":159,"line":175},[157,589,224],{"class":171},[157,591,592],{"class":167}," https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fcustomers\u002Fre_000000000000\u002Fblockchain-wallets",[157,594,172],{"class":171},[157,596,597,599,601],{"class":159,"line":232},[157,598,178],{"class":171},[157,600,237],{"class":167},[157,602,172],{"class":171},[157,604,605,607,609],{"class":159,"line":242},[157,606,178],{"class":171},[157,608,247],{"class":167},[157,610,172],{"class":171},[157,612,613,615],{"class":159,"line":252},[157,614,255],{"class":171},[157,616,258],{"class":167},[157,618,619],{"class":159,"line":261},[157,620,621],{"class":167},"    \"name\": \"John personal wallet\",\n",[157,623,624],{"class":159,"line":267},[157,625,626],{"class":167},"    \"network\": \"base_sepolia\",\n",[157,628,629],{"class":159,"line":273},[157,630,631],{"class":167},"    \"is_account_abstraction\": true,\n",[157,633,634],{"class":159,"line":349},[157,635,636],{"class":167},"    \"address\": \"0x...\"\n",[157,638,639],{"class":159,"line":355},[157,640,276],{"class":167},[54,642,643,644,472,647,650,651,654,655,658],{},"Double-check the address. BlindPay cannot recover stablecoins delivered to a wrong one. For Brazilian customers, also send ",[82,645,646],{},"is_self_custody",[82,648,649],{},"true"," or ",[82,652,653],{},"false","); omitting it returns ",[82,656,657],{},"self_custody_required",". The network is read from the wallet, so you never pass a network on a payin quote. For help choosing a chain, see the guide to which blockchain network to use for payins.",[67,660,662],{"id":661},"how-do-i-create-a-virtual-account-for-payins","How do I create a virtual account for payins?",[54,664,665],{},"Create a virtual account when a customer will receive repeated deposits and you want one permanent account number instead of fresh instructions each time. The customer must already be approved and have a wallet.",[148,667,669],{"className":150,"code":668,"language":152,"meta":153,"style":153},"curl --request POST \\\n  --url https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fcustomers\u002Fre_000000000000\u002Fvirtual-accounts \\\n  --header 'Authorization: Bearer YOUR_API_KEY' \\\n  --header 'Content-Type: application\u002Fjson' \\\n  --data '{\n  \"banking_partner\": \"\u003Cbanking_partner>\",\n  \"token\": \"USDC\",\n  \"blockchain_wallet_id\": \"bw_000000000000\"\n}'\n",[82,670,671,681,690,698,706,712,717,722,727],{"__ignoreMap":153},[157,672,673,675,677,679],{"class":159,"line":160},[157,674,164],{"class":163},[157,676,214],{"class":171},[157,678,217],{"class":167},[157,680,172],{"class":171},[157,682,683,685,688],{"class":159,"line":175},[157,684,224],{"class":171},[157,686,687],{"class":167}," https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fcustomers\u002Fre_000000000000\u002Fvirtual-accounts",[157,689,172],{"class":171},[157,691,692,694,696],{"class":159,"line":232},[157,693,178],{"class":171},[157,695,237],{"class":167},[157,697,172],{"class":171},[157,699,700,702,704],{"class":159,"line":242},[157,701,178],{"class":171},[157,703,247],{"class":167},[157,705,172],{"class":171},[157,707,708,710],{"class":159,"line":252},[157,709,255],{"class":171},[157,711,258],{"class":167},[157,713,714],{"class":159,"line":261},[157,715,716],{"class":167},"  \"banking_partner\": \"\u003Cbanking_partner>\",\n",[157,718,719],{"class":159,"line":267},[157,720,721],{"class":167},"  \"token\": \"USDC\",\n",[157,723,724],{"class":159,"line":273},[157,725,726],{"class":167},"  \"blockchain_wallet_id\": \"bw_000000000000\"\n",[157,728,729],{"class":159,"line":349},[157,730,731],{"class":167},"}'\n",[54,733,734,737,738,742,743,746,747,750,751,754,755,758,759,139],{},[82,735,736],{},"banking_partner"," takes one of the values listed in the ",[61,739,741],{"href":740},"\u002Fdocs\u002Fvirtual-accounts-create","create a virtual account docs",", and which one is available depends on your instance and the customer. On a development instance use ",[82,744,745],{},"USDB"," as the ",[82,748,749],{},"token",". Business customers must have fields such as ",[82,752,753],{},"account_purpose"," and ",[82,756,757],{},"source_of_wealth"," filled in first, or the call fails with ",[82,760,761],{},"missing_required_fields",[54,763,764,765,768,769,771,772,774,775,778,779,782,783,139],{},"On a development instance the account is approved immediately, with a fake routing number. In production it goes through compliance review (",[82,766,767],{},"pending_review",") and then bank review (",[82,770,479],{},") before it becomes ",[82,773,483],{},", and approval is not guaranteed at either stage. A ",[82,776,777],{},"virtualAccount.complete"," webhook tells you when the account number is live. Each deposit then creates a payin and fires ",[82,780,781],{},"payin.new",". The model is explained in ",[61,784,786],{"href":785},"\u002Fresources\u002Fmore\u002Fvirtual-accounts-for-stablecoin-payins","virtual accounts for stablecoin payins",[67,788,790],{"id":789},"how-do-i-create-a-one-time-payin-instead","How do I create a one-time payin instead?",[54,792,793],{},"Quote first, then create the payin. The quote locks the amount, fees, and destination for 5 minutes.",[148,795,797],{"className":150,"code":796,"language":152,"meta":153,"style":153},"curl --request POST \\\n  --url https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fpayin-quotes \\\n  --header 'Authorization: Bearer YOUR_API_KEY' \\\n  --header 'Content-Type: application\u002Fjson' \\\n  --data '{\n    \"blockchain_wallet_id\": \"bw_000000000000\",\n    \"currency_type\": \"sender\",\n    \"cover_fees\": true,\n    \"request_amount\": 10000,\n    \"payment_method\": \"ach\",\n    \"token\": \"USDB\"\n  }'\n",[82,798,799,809,818,826,834,840,845,850,855,860,865,870],{"__ignoreMap":153},[157,800,801,803,805,807],{"class":159,"line":160},[157,802,164],{"class":163},[157,804,214],{"class":171},[157,806,217],{"class":167},[157,808,172],{"class":171},[157,810,811,813,816],{"class":159,"line":175},[157,812,224],{"class":171},[157,814,815],{"class":167}," https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fpayin-quotes",[157,817,172],{"class":171},[157,819,820,822,824],{"class":159,"line":232},[157,821,178],{"class":171},[157,823,237],{"class":167},[157,825,172],{"class":171},[157,827,828,830,832],{"class":159,"line":242},[157,829,178],{"class":171},[157,831,247],{"class":167},[157,833,172],{"class":171},[157,835,836,838],{"class":159,"line":252},[157,837,255],{"class":171},[157,839,258],{"class":167},[157,841,842],{"class":159,"line":261},[157,843,844],{"class":167},"    \"blockchain_wallet_id\": \"bw_000000000000\",\n",[157,846,847],{"class":159,"line":267},[157,848,849],{"class":167},"    \"currency_type\": \"sender\",\n",[157,851,852],{"class":159,"line":273},[157,853,854],{"class":167},"    \"cover_fees\": true,\n",[157,856,857],{"class":159,"line":349},[157,858,859],{"class":167},"    \"request_amount\": 10000,\n",[157,861,862],{"class":159,"line":355},[157,863,864],{"class":167},"    \"payment_method\": \"ach\",\n",[157,866,867],{"class":159,"line":361},[157,868,869],{"class":167},"    \"token\": \"USDB\"\n",[157,871,872],{"class":159,"line":367},[157,873,276],{"class":167},[54,875,876,879,880,883,884,887,888,472,890,893,894,897],{},[82,877,878],{},"currency_type: \"sender\""," means ",[82,881,882],{},"request_amount"," is in the fiat the payer sends. With ",[82,885,886],{},"cover_fees: true"," the sender pays the fee on top, and the wallet receives the full amount. Save the quote ",[82,889,471],{},[82,891,892],{},"qu_...","), then create the payin from it, with an ",[82,895,896],{},"Idempotency-Key"," header so a network retry cannot create two:",[148,899,901],{"className":150,"code":900,"language":152,"meta":153,"style":153},"curl https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fpayins\u002Fevm \\\n  --request POST \\\n  --header 'Content-Type: application\u002Fjson' \\\n  --header 'Authorization: Bearer YOUR_API_KEY' \\\n  --header 'Idempotency-Key: \u003Cyour_uuid>' \\\n  --data '{\n  \"payin_quote_id\": \"qu_000000000000\"\n}'\n",[82,902,903,912,921,929,937,946,952,957],{"__ignoreMap":153},[157,904,905,907,910],{"class":159,"line":160},[157,906,164],{"class":163},[157,908,909],{"class":167}," https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fpayins\u002Fevm",[157,911,172],{"class":171},[157,913,914,917,919],{"class":159,"line":175},[157,915,916],{"class":171},"  --request",[157,918,217],{"class":167},[157,920,172],{"class":171},[157,922,923,925,927],{"class":159,"line":232},[157,924,178],{"class":171},[157,926,247],{"class":167},[157,928,172],{"class":171},[157,930,931,933,935],{"class":159,"line":242},[157,932,178],{"class":171},[157,934,237],{"class":167},[157,936,172],{"class":171},[157,938,939,941,944],{"class":159,"line":252},[157,940,178],{"class":171},[157,942,943],{"class":167}," 'Idempotency-Key: \u003Cyour_uuid>'",[157,945,172],{"class":171},[157,947,948,950],{"class":159,"line":261},[157,949,255],{"class":171},[157,951,258],{"class":167},[157,953,954],{"class":159,"line":267},[157,955,956],{"class":167},"  \"payin_quote_id\": \"qu_000000000000\"\n",[157,958,959],{"class":159,"line":273},[157,960,731],{"class":167},[54,962,963,964,967,968,971,972,754,975,978,979,982,983,986,987,991,992,139],{},"The path says ",[82,965,966],{},"evm"," for every payment method and network. The name is historical. For ",[82,969,970],{},"ach"," the response carries ",[82,973,974],{},"memo_code",[82,976,977],{},"blindpay_bank_details",", which you show to the payer. Other rails return a ",[82,980,981],{},"pix_code",", a ",[82,984,985],{},"clabe",", or an instruction object, listed in the ",[61,988,990],{"href":989},"\u002Fdocs\u002Fpayins","payins docs",". The same flow for other rails is covered in ",[61,993,995],{"href":994},"\u002Fresources\u002Fmore\u002Fhow-to-integrate-a-crypto-on-ramp-api","how to integrate a crypto on-ramp API",[67,997,999],{"id":998},"how-do-i-listen-for-payin-events","How do I listen for payin events?",[54,1001,1002],{},"Register an HTTPS endpoint, then fetch its signing secret.",[148,1004,1006],{"className":150,"code":1005,"language":152,"meta":153,"style":153},"curl --request POST \\\n  --url https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fwebhook-endpoints \\\n  --header 'Authorization: Bearer YOUR_API_KEY' \\\n  --header 'Content-Type: application\u002Fjson' \\\n  --data '{\n    \"url\": \"https:\u002F\u002Fexample.com\u002Fwebhook\",\n    \"events\": []\n  }'\n",[82,1007,1008,1018,1027,1035,1043,1049,1054,1059],{"__ignoreMap":153},[157,1009,1010,1012,1014,1016],{"class":159,"line":160},[157,1011,164],{"class":163},[157,1013,214],{"class":171},[157,1015,217],{"class":167},[157,1017,172],{"class":171},[157,1019,1020,1022,1025],{"class":159,"line":175},[157,1021,224],{"class":171},[157,1023,1024],{"class":167}," https:\u002F\u002Fapi.blindpay.com\u002Fv1\u002Finstances\u002Fin_000000000000\u002Fwebhook-endpoints",[157,1026,172],{"class":171},[157,1028,1029,1031,1033],{"class":159,"line":232},[157,1030,178],{"class":171},[157,1032,237],{"class":167},[157,1034,172],{"class":171},[157,1036,1037,1039,1041],{"class":159,"line":242},[157,1038,178],{"class":171},[157,1040,247],{"class":167},[157,1042,172],{"class":171},[157,1044,1045,1047],{"class":159,"line":252},[157,1046,255],{"class":171},[157,1048,258],{"class":167},[157,1050,1051],{"class":159,"line":261},[157,1052,1053],{"class":167},"    \"url\": \"https:\u002F\u002Fexample.com\u002Fwebhook\",\n",[157,1055,1056],{"class":159,"line":267},[157,1057,1058],{"class":167},"    \"events\": []\n",[157,1060,1061],{"class":159,"line":273},[157,1062,276],{"class":167},[54,1064,1065,1066,1069,1070,1073,1074,1077,1078,1081,1082,484,1085,1088,1089,1092,1093,1095,1096,139],{},"An empty ",[82,1067,1068],{},"events"," array subscribes to everything. The response returns the endpoint ID (",[82,1071,1072],{},"we_...","), and ",[82,1075,1076],{},"GET ...\u002Fwebhook-endpoints\u002F{id}\u002Fsecret"," returns the ",[82,1079,1080],{},"whsec_..."," signing secret. Every call is signed with ",[82,1083,1084],{},"svix-id",[82,1086,1087],{},"svix-timestamp",", and ",[82,1090,1091],{},"svix-signature"," headers. Verify on the raw body before parsing, and deduplicate on ",[82,1094,1084],{},". The details are in ",[61,1097,1099],{"href":1098},"\u002Fresources\u002Fmore\u002Fstablecoin-api-webhooks-reconciliation","stablecoin API webhooks and reconciliation",[505,1101,1102,1115],{},[508,1103,1104],{},[511,1105,1106,1109,1112],{},[514,1107,1108],{},"Event or status",[514,1110,1111],{},"What it means",[514,1113,1114],{},"What your system should do",[527,1116,1117,1129,1142,1158,1173,1188],{},[511,1118,1119,1123,1126],{},[532,1120,1121],{},[82,1122,781],{},[532,1124,1125],{},"Payin created, or a virtual account deposit arrived",[532,1127,1128],{},"Create your payment record in a pending state",[511,1130,1131,1136,1139],{},[532,1132,1133],{},[82,1134,1135],{},"payin.update",[532,1137,1138],{},"An intermediate step, such as an arrival check or review",[532,1140,1141],{},"Update tracking detail, no customer message needed",[511,1143,1144,1152,1155],{},[532,1145,1146,188,1149],{},[82,1147,1148],{},"payin.complete",[82,1150,1151],{},"completed",[532,1153,1154],{},"Stablecoins delivered to the wallet",[532,1156,1157],{},"Credit the customer, once",[511,1159,1160,1167,1170],{},[532,1161,1162,188,1164],{},[82,1163,1148],{},[82,1165,1166],{},"refunded",[532,1168,1169],{},"Deposit returned to the sender",[532,1171,1172],{},"Close the payment, notify the customer",[511,1174,1175,1182,1185],{},[532,1176,1177,188,1179],{},[82,1178,1148],{},[82,1180,1181],{},"failed",[532,1183,1184],{},"The payin did not go through",[532,1186,1187],{},"Close the payment, inspect before messaging",[511,1189,1190,1195,1198],{},[532,1191,1192],{},[82,1193,1194],{},"status: on_hold",[532,1196,1197],{},"Held for manual review",[532,1199,1200],{},"Keep pending, answer any information request quickly",[67,1202,1204],{"id":1203},"how-do-i-test-in-the-sandbox","How do I test in the sandbox?",[54,1206,1207,1208,1210],{},"A development instance runs the whole flow without real money. Every payin completes about 30 seconds after creation and delivers USDB. To force a different outcome, set the quote's ",[82,1209,882],{},":",[505,1212,1213,1224],{},[508,1214,1215],{},[511,1216,1217,1221],{},[514,1218,1219],{},[82,1220,882],{},[514,1222,1223],{},"Result",[527,1225,1226,1238,1250],{},[511,1227,1228,1234],{},[532,1229,1230,1233],{},[82,1231,1232],{},"66600"," ($666.00)",[532,1235,1236],{},[82,1237,1181],{},[511,1239,1240,1246],{},[532,1241,1242,1245],{},[82,1243,1244],{},"77700"," ($777.00)",[532,1247,1248],{},[82,1249,1166],{},[511,1251,1252,1255],{},[532,1253,1254],{},"Any other amount",[532,1256,1257,1259],{},[82,1258,1151],{}," after about 30 seconds",[54,1261,1262,1263,754,1265,1267,1268,1271,1272,139],{},"Also note that on development the ",[82,1264,974],{},[82,1266,981],{}," are the literal placeholder ",[82,1269,1270],{},"\u003Cdevelopment>",", so you cannot pay them for real. The full pre-launch list is in ",[61,1273,1275],{"href":1274},"\u002Fresources\u002Fmore\u002Fhow-to-test-virtual-accounts-sandbox","how to test a virtual accounts integration",[67,1277,1279],{"id":1278},"what-mistakes-should-i-avoid","What mistakes should I avoid?",[54,1281,1282],{},"These are the five errors that cost the most time on a first build.",[505,1284,1285,1298],{},[508,1286,1287],{},[511,1288,1289,1292,1295],{},[514,1290,1291],{},"Symptom",[514,1293,1294],{},"Likely cause",[514,1296,1297],{},"Fix",[527,1299,1300,1315,1334,1347,1365],{},[511,1301,1302,1307,1312],{},[532,1303,1304,1306],{},[82,1305,761],{}," on virtual account create",[532,1308,1309,1310],{},"Business customer is missing fields such as ",[82,1311,753],{},[532,1313,1314],{},"Update the customer, then retry",[511,1316,1317,1322,1327],{},[532,1318,1319,1321],{},[82,1320,657],{}," on wallet create",[532,1323,1324,1325],{},"Brazilian customer, no ",[82,1326,646],{},[532,1328,1329,1330,650,1332],{},"Send ",[82,1331,649],{},[82,1333,653],{},[511,1335,1336,1341,1344],{},[532,1337,1338],{},[82,1339,1340],{},"request_amount_must_be_a_whole_currency_unit",[532,1342,1343],{},"MXN, COP, or ARS amount not a multiple of 100",[532,1345,1346],{},"Round to a whole currency unit",[511,1348,1349,1355,1358],{},[532,1350,1351,188,1353],{},[82,1352,187],{},[82,1354,191],{},[532,1356,1357],{},"More than 40 requests per minute",[532,1359,1360,1361,1364],{},"Back off using the ",[82,1362,1363],{},"Retry-After"," header",[511,1366,1367,1370,1373],{},[532,1368,1369],{},"Payin create is rejected",[532,1371,1372],{},"Quote older than 5 minutes",[532,1374,1375],{},"Create a new quote and retry",[54,1377,1378,1379,1381,1382,1385],{},"Two further habits prevent most production incidents. Send an ",[82,1380,896],{}," on every create, and read ",[82,1383,1384],{},"status"," from each payload instead of guessing from the event name. For the idempotency rules, see the BlindPay idempotency docs.",[67,1387,1389],{"id":1388},"what-changes-in-production","What changes in production?",[54,1391,1392],{},"Production adds real checks and real timing. A production instance can take up to 3 business days to provision, and you need a new API key, because keys are scoped to one instance.",[1394,1395,1396,1399,1414,1417,1420],"ol",{},[75,1397,1398],{},"Submit real KYC documents for every customer.",[75,1400,1401,1402,1404,1405,480,1407,650,1410,1413],{},"Change ",[82,1403,749],{}," from ",[82,1406,745],{},[82,1408,1409],{},"USDC",[82,1411,1412],{},"USDT",", and use mainnet networks.",[75,1415,1416],{},"Remove any logic that relies on the 666.00 and 777.00 test amounts.",[75,1418,1419],{},"Create a new webhook endpoint on the production instance. Development webhook settings do not carry over.",[75,1421,1422],{},"Expect real bank timing: ACH and wire payins wait up to 5 business days, while Pix, SPEI, Transfers, and PSE payins wait up to 30 minutes before they fail.",[67,1424,1426],{"id":1425},"where-does-blindpay-fit","Where does BlindPay fit?",[54,1428,1429,1430,1433,1434,754,1437,1440,1441,1444,1445,1449],{},"BlindPay offers the API calls above plus tooling around them. The REST API has an OpenAPI 3.1 spec you can download from ",[82,1431,1432],{},"https:\u002F\u002Fapi.blindpay.com\u002Fdoc"," to generate a typed client. Official SDKs exist for Node.js, Python, Go, PHP, and Swift; this guide shows cURL because the SDK docs do not include a payin example, so check each SDK's README for method names. For scripting, the BlindPay CLI covers the same flow, for example ",[82,1435,1436],{},"blindpay payin_quotes create",[82,1438,1439],{},"blindpay payins create --quote-id qu_...",", with a ",[82,1442,1443],{},"--json"," flag for structured output (see the ",[61,1446,1448],{"href":1447},"\u002Fblog\u002Fcli","CLI announcement",").",[54,1451,1452,1453,1456,1457,1461,1462,139],{},"BlindPay's pricing is a monthly plan plus per-transaction fees that vary by rail and network and appear in the quote before you create the payin. See ",[61,1454,1455],{"href":1455},"\u002Fpricing",". The same pattern of customer, virtual account, and webhook appears across payin providers, so the structure here transfers even if you compare vendors. Agents can run this flow too: see ",[61,1458,1460],{"href":1459},"\u002Fresources\u002Fmore\u002Fbuild-a-neobank-with-ai-agents","building with AI agents",", or copy the ",[61,1463,1465],{"href":1464},"\u002Fprompts\u002Fintegrate-payins","payins integration prompt",[67,1467,1469],{"id":1468},"sources","Sources",[72,1471,1472,1480,1485],{},[75,1473,1474,1475,1479],{},"BlindPay docs, ",[61,1476,1478],{"href":1477},"\u002Fdocs\u002Fquickstart-payin","Bank transfer to stablecoins quickstart"," (checked on 2026-10-26)",[75,1481,1474,1482,1479],{},[61,1483,1484],{"href":989},"Payins",[75,1486,1474,1487,1479],{},[61,1488,1489],{"href":740},"Create a virtual account",[54,1491,1492],{},[1493,1494,1495],"em",{},"This article is for general information only and is not legal, tax, or financial advice.",[1497,1498,1499],"style",{},"html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":153,"searchDepth":175,"depth":175,"links":1501},[1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514],{"id":69,"depth":175,"text":70},{"id":97,"depth":175,"text":98},{"id":142,"depth":175,"text":143},{"id":194,"depth":175,"text":195},{"id":499,"depth":175,"text":500},{"id":661,"depth":175,"text":662},{"id":789,"depth":175,"text":790},{"id":998,"depth":175,"text":999},{"id":1203,"depth":175,"text":1204},{"id":1278,"depth":175,"text":1279},{"id":1388,"depth":175,"text":1389},{"id":1425,"depth":175,"text":1426},{"id":1468,"depth":175,"text":1469},[1516,1521,1526],{"path":1517,"title":1518,"description":1519,"date":1520},"\u002Fresources\u002Fmore\u002Fhow-to-integrate-a-stablecoin-api","Integrating a stablecoin API: a developer's guide to cross-border payments with BlindPay","Step-by-step integration of a stablecoin API: authenticate, onboard a customer, create a virtual USD account, quote and send a cross-border payout, and handle signed webhooks. Endpoints, statuses, and SDKs for BlindPay.","2026-09-17",{"path":1522,"title":1523,"description":1524,"date":1525},"\u002Fresources\u002Fmore\u002Fpayin-webhooks-idempotency-reconciliation","Payin webhooks, idempotency, and reconciliation for stablecoin payins","Payin webhooks reconciliation: verify signatures, dedupe events, model a payin state machine, and match each virtual account deposit to your ledger.","2026-10-27",{"path":1527,"title":1528,"description":1529,"date":7},"\u002Fresources\u002Fmore\u002Fneobank-launch-checklist-30-60-90","Neobank launch checklist: a 30, 60, and 90 day plan for an MVP","A 90-day neobank MVP plan: team roles, regulatory path, vendors, build milestones, pilot users, launch criteria, and the metrics to watch after day 90.",1794399377490]