---
title: "Migrate from Privy to BlindPay"
description: "Keep Privy for embedded and server wallets, move the stablecoin-to-fiat offramp leg to BlindPay: register the Privy wallet, quote and execute payouts, and verify webhooks."
date: "2026-08-26"
category: "migrations"
products: ["payouts", "quotes", "compliance", "webhooks"]
---

Paste this prompt into your coding agent to move the stablecoin-to-fiat offramp leg of a Privy (docs.privy.io) integration to BlindPay, keeping Privy in place for embedded and server wallets.

## Prompt

```text
You are migrating my application's stablecoin-to-fiat offramp leg from Privy (docs.privy.io) to BlindPay. Privy stays in place as the wallet layer: users' stablecoins keep living in their Privy embedded or server wallets. This migration only replaces whatever converts those stablecoins to fiat and pays it out.

Before writing code, read these sources and follow them over any prior knowledge:
- https://blindpay.com/docs/llms.txt (read the quickstarts, customers, bank accounts, quotes, blockchain wallets, and webhooks pages)
- The OpenAPI spec: curl https://api.blindpay.com/doc
- https://docs.privy.io/wallets/funding/overview
- https://docs.privy.io/recipes/off-ramp-guide
Privy itself does not settle fiat; it hands off a signed onchain transfer to a third-party offramp provider. Confirm in the Privy docs which mechanism this codebase actually wired up (one of those providers, a different funding integration, or a manual flow) instead of assuming; if the code shows something not covered by these pages, phrase it as unconfirmed rather than inventing an endpoint.

Do the migration in this order:
1. Inventory the current offramp path from the codebase: the funding or offramp provider integration in use, every stored Privy wallet ID (embedded or server) that sends the crypto leg, any stored provider addresses or transaction hashes, and how completion is detected today (webhook, polling, or manual confirmation). Produce a written mapping from what we actually use to BlindPay concepts: the payee becomes a BlindPay customer (re_) with KYC/KYB, their payout destination becomes a bank account (ba_) on the relevant rail, and the Privy wallet that currently sends to the offramp provider's address becomes the sender_wallet_address on a BlindPay payout instead. Flag anything with no direct equivalent rather than guessing.
2. Register each Privy wallet as a BlindPay external wallet: fetch the sign-message challenge (GET /v1/instances/{instance_id}/customers/{customer_id}/blockchain-wallets/sign-message), sign it with the Privy wallet's own signing call (the embedded wallet's provider or the server wallet RPC endpoint, per the Privy docs), submit the signature to POST /v1/instances/{instance_id}/customers/{customer_id}/blockchain-wallets, and persist the returned bw_ ID against the customer record. Re-onboard each payee's KYC/KYB on BlindPay through terms of service, customer creation, and document upload driven by webhooks, since KYC does not transfer between providers; sequence this ahead of the cutover so approved customers are ready when a flow switches over.
3. Rebuild money movement on BlindPay's explicit two-step model: request a quote (POST /v1/instances/{instance_id}/quotes), sign and confirm the returned approve call through the same Privy wallet, then execute the payout (POST /v1/instances/{instance_id}/payouts/evm) with sender_wallet_address set to the Privy wallet's address, before the quote's expires_at (about 5 minutes). Store qu_, po_, and bw_ IDs alongside whatever ID the old offramp provider used for the same transfer.
4. Replace the old offramp completion signal with BlindPay's Svix-signed webhooks: verify svix-id, svix-timestamp, and svix-signature against the raw request body with whsec_, dedup on svix-id, and update payout state on payout.new, payout.update, and payout.complete.
5. Cut over per payee or corridor behind a feature flag: dual-run new payouts on BlindPay while the old offramp path finishes any in-flight transfers, keep both completion signals live during the window, and only retire the old offramp integration once a full dual-run settlement cycle has completed and reconciled.

Constraints:
- Develop against a BlindPay development instance (USDB on testnets, KYC auto-approves) first, and exercise the $666.00 forced-failed and $777.00 forced-refunded sentinel amounts before touching production traffic.
- Keep BlindPay and Privy API keys server-side.
- Store and compare all amounts as integer minor units; never use floating point for money math.
- Produce a written migration report before changing code: the mapping table, the confirmed old-offramp mechanism, gaps, and the re-KYC plan.

Deliverables: the migration report, the external-wallet registration module (challenge fetch, Privy signing call, bw_ persistence), the quote-to-payout orchestration with expiry handling, webhook handlers for the three payout events, and a cutover checklist.
```

## How to use

1. Point the agent at your codebase so it inventories the real offramp provider or manual flow instead of assuming one from the Privy docs list.
2. Review the migration report, especially the confirmed old-offramp mechanism and the re-KYC sequencing, before approving code changes.
3. Dual-run at least one full settlement cycle per payee or corridor, including the sentinel failure and refund cases, before retiring the old offramp path.

## Related docs

- [Blockchain wallets](https://blindpay.com/docs/blockchain-wallets)
- [Payout quotes](https://blindpay.com/docs/payout-quotes)
- [Privy funding overview](https://docs.privy.io/wallets/funding/overview)
- [Privy off-ramp guide](https://docs.privy.io/recipes/off-ramp-guide)
