Integrations
Circle: wallets, compliance and USDC
FurlPay integrates four separate Circle products. Each has its own credentials and its own on/off state, so one can be configured without the others. This page lists what each integration does, who holds the keys, and what a deployment must set.
Integration is not partnership or endorsement
At a glance
| Parameter | Type | Description |
|---|---|---|
| User-controlled walletsoptional | CIRCLE_W3S_* | MPC wallets the user controls with a PIN or biometric in Circle's SDK. Returns 503 until both the API key and app ID are set. |
| Compliance Engineoptional | CIRCLE_COMPLIANCE_API_KEY | Address screening layered on top of the built-in sanctions check. Unset means it is skipped. |
| CCTP V2optional | no key | Burn-and-mint transfers of native USDC between networks. FurlPay builds the transaction data; the user's wallet signs both legs. |
| Paymasteroptional | CIRCLE_PAYMASTER_ADDRESS_* | Pay network gas in USDC from an ordinary account. Not enabled: no delegate contract is allowlisted. |
User-controlled wallets
The server creates a Circle user for the signed-in account, then hands the user's own device a 60-minute user token and encryption key. Circle's SDK uses those to show its PIN or biometric screen and complete each challenge.
- Who holds what. The Circle API key never leaves the server. The key shares belong to Circle and the user. FurlPay never holds a share and cannot move funds from these wallets.
- Identity mapping. The Circle user ID is derived from a hash of the FurlPay user ID, so Circle is not given FurlPay's internal identifiers.
- Networks. Ethereum, Arbitrum, Base, Polygon, Solana, Avalanche and OP Mainnet, plus their testnets. A sandbox key (
TEST_API_KEY:prefix) can only create testnet wallets; the API reports the environment and offers only matching networks. - Creation is a challenge. No wallet exists until the user completes the setup challenge on their device. Poll the challenge endpoint for the real status; it is read from Circle each time.
| Method | Path | Description |
|---|---|---|
| GET | /api/wallets/circle | The user's Circle wallets with balances, available networks and environment |
| POST | /api/wallets/circle | Start PIN setup and wallet creation: { blockchains[], accountType? } |
| GET | /api/wallets/circle/challenge?id= | Status of a challenge: PENDING, IN_PROGRESS, COMPLETE, FAILED or EXPIRED |
| POST | /api/wallets/circle/transfer | Create a transfer challenge from one of the user's wallets |
Transfers. FurlPay moves nothing. It creates a transfer challenge and Circle executes the transfer only after the user approves it in the SDK. Before creating the challenge the server enforces its own policy:
- the wallet must be one Circle lists under this user's token;
- USDC only, so the amount is in US dollars and the verification-tier limit applies correctly;
- the amount must not exceed the balance Circle reports;
- both parties are screened, and a
reviewresult refuses with409; - the client's idempotency key is passed to Circle, so a retry returns the same challenge.
Compliance Engine
When configured, every address screen also asks Circle's screening API. The stricter of the two verdicts wins. Circle's screening covers Ethereum, Polygon, Arbitrum, Avalanche, OP Mainnet and Solana. It does not cover Base, so Base addresses receive the built-in sanctions check only, and the result records which layers ran.
Once configured, the integration fails closed: a network error, a non-2xx response or a response without a result is a block. Results are cached for 60 seconds. See Screening and cases.
CCTP V2
Cross-chain USDC uses Circle's burn-and-mint protocol with no wrapped tokens. FurlPay returns unsigned transaction data and polls the attestation service; the user's wallet signs and submits the burn and the mint. Details and limits are in CCTP Cross-Chain.
USDC contracts and the issuer blocklist
Payment, payroll and on-chain reads use Circle-issued native USDC addresses, checked against Circle's published contract list. When a merchant payment is verified, the payer and the destination are also checked against the USDC contract's on-chain blocklist. A payment involving a blocked address is held for review rather than credited.
Paymaster (not enabled)
The code describes a scheme where an ordinary account delegates to a smart-account implementation (EIP-7702) and pays gas in USDC through Circle Paymaster. It is not usable today: the allowlist of delegate contracts ships empty, because an unverified delegate address could drain an account. GET /api/transfers/sponsored reports which pieces are missing. Always call it before asking a user to sign a delegation.
Setup
CIRCLE_W3S_API_KEYandCIRCLE_W3S_APP_ID— both required for wallets.CIRCLE_W3S_API_URLoptionally overrides the base URL.CIRCLE_COMPLIANCE_API_KEY— enables address screening.CIRCLE_COMPLIANCE_API_URLoptionally overrides the base URL.CCTP_IRIS_API_URLandCCTP_TESTNET— attestation service settings.FURLPAY_MAX_CCTP_USDsets the per-transaction cap (default 500 USD).CIRCLE_PAYMASTER_ADDRESS_ARBITRUM,_BASE,_ETHEREUMand the matchingERC4337_BUNDLER_URL_*— required for the paymaster scheme, together with a reviewed delegate allowlist entry.- The web client loads Circle's
@circle-fin/w3s-pw-web-sdkto complete challenges.
