Skip to main content
Use this flow when your users are outside Brazil and need USD onramp (WIRE/ACH → USDC) or offramp (USDC → USD bank payout) through Avenia. Pods exposes foreign KYC separately from the Brazilian Pix flow documented in Set up Pods Ramp. Brazilian profiles use cpf and brlaEnabled; foreign profiles use taxId / taxIdCountry and usdEnabled.
EUR capability is planned but not available yet. Poll eurEnabled on status responses only for forward compatibility — it stays false today.

Prerequisites

Send all requests from your backend with x-api-key. Never expose the API key in browser or mobile clients.

End-to-end flow

1

Complete Sumsub verification

The applicant finishes your Sumsub WebSDK level. Confirm reviewStatus: completed and reviewResult.reviewAnswer: GREEN before generating a share token.
2

Submit share token to Pods

POST /api/v1/kyc/foreign/sumsub-share-token with taxId, taxIdCountry, shareToken, sumsubApplicantId, email, and walletAddress.
3

Poll KYC status

GET /api/v1/kyc/status?kycUserId=… until status is approved. Level 1 approval does not enable USD ramps yet — usdEnabled stays false.
4

Unlock USD

POST /api/v1/kyc/currency-unlock with { "currency": "usd", "kycUserId": "…" }. Poll status again until usdEnabled is true (Avenia USD KYC can take one to a few minutes after submit).
5

Quote and settle onramp

GET /v2/swap/quote with originChain=fiat, destinationChain (ethereum | base | polygon), tokenIn=USD, USDC tokenOut, amountIn or amountOut, destinationAddress, and optional usdPaymentMethod=ACH or WIRE. With destinationAddress, the response includes ACH/WIRE deposit instructions — no EVM tx to sign. Poll GET /v2/swap/status/{quoteId} until fulfilled.

1. Submit foreign share token

Response:
Pods stores only a salted hash and last four digits of the tax id — never the full value.

2. Poll status (Level 1 approved, USD still locked)

Example shape after Level 1 is approved but before currency unlock (live capture pattern):
Wait until status is approved. For rejections, read the normalized reason object (not providerResult).
status: approved alone is not enough for USD ramps. You must unlock USD and wait until usdEnabled is true.

3. Unlock USD capability

The success response is the full KYC status object. Immediately after a successful submit, usdEnabled is often still false while Avenia processes the USD KYC attempt:
Poll GET /api/v1/kyc/status until usdEnabled is true:

Currency-unlock errors

Example — missing identification document:
Documents are collected during Level 1 / Sumsub — POST /currency-unlock does not accept document uploads in the body.

4. Onramp: USD ACH/WIRE → USDC (Polygon example)

Prerequisite: KYC profile for destinationAddress has usdEnabled: true. Pass exactly one of amountIn or amountOut. Both are supported; they answer different product questions:
ACH and WIRE charge an Avenia payment-rail fee (feeBreakdown.charges with kind: paymentRail), deducted from the USD sent — not charged on top in the bank app. With amountIn, USDC out is net of that fee. With amountOut, Pods asks Avenia for an exact USDC receive amount in one quote (outputAmount); tokenIn.amount / paymentInstructions.amount are the USD the user must send (fee included).WIRE In Fee is ~USD 10 — a small amountIn (e.g. $5) can yield 0 USDC. Prefer amountOut (or a larger amountIn) for WIRE.Quote/Action deadlines for USD onramps default to 24 hours (USD_ONRAMP_QUOTE_EXPIRATION_MINUTES=1440), not the generic 5-minute crypto quote TTL. When the Avenia ticket is created, Pods extends quote.deadline and Action.expireAt to at least paymentInstructions.expiresAt.

Exact USD send (amountIn)

Live response shape (ACH, send USD 5.00 → ~3.983876 USDC on Polygon):

Exact USDC receive (amountOut)

Live response shape (ACH, receive exactly 4 USDC → send USD 5.02):
Same amountOut=4000000 with usdPaymentMethod=WIRE returns exact tokenOut.amount=4000000 and a larger USD send (live smoke: tokenIn.amount=1404 / ~USD 14.04, WIRE fee ~USD 10.04).

How to present deposit instructions

There is no EVM transaction to sign (transactionData is [], chainId is null). Top-level id is the Action id; quote.quoteId is for GET /v2/swap/status/{quoteId}. Also see Move Money with Pods Ramp for the shared quote surface and Pix parallels.

5. USD beneficiary (offramp resolve-or-create)

Pass bank account fields on GET /v2/swap/quote with originAddress. Pods lists existing Avenia beneficiaries for the KYC subaccount; if account number + routing match, it reuses that id, otherwise it creates one and stores it on the quote (rawQuote.beneficiaryUsdBankAccountId, internal). Required query fields: Inspect is not exposed as a separate KYC API — beneficiaries are created or reused automatically on offramp quote from the flat bank fields (Pix-style destination).

6. USD↔USDC ramps (summary)

Supported USDC networks for v1 follow Avenia’s USD→USDC allowlist: ethereum, base, and polygon. When Avenia supports the destination chain, it delivers USDC directly to the user’s wallet (no Deframe inventory hop).

Token addresses (USDC)

Preferred offramp quote (bank fields → resolve-or-create, then executable payload):
amountIn is in USDC smallest units (1000000 = 1 USDC). Optional usdPaymentMethod is WIRE (default) or ACH. Track progress with GET /v2/swap/status/{quoteId} and GET /actions/{id}.
Offramp and onramp both require usdEnabled: true on the KYC profile tied to the wallet (destinationAddress on onramp, originAddress on offramp). Pods rejects quotes when the wallet has no unlocked USD capability.

Status fields reference

API reference

Open the Ramp group in the API Reference tab for full request/response schemas:
  • POST /api/v1/kyc/foreign/sumsub-share-token
  • POST /api/v1/kyc/currency-unlock
  • GET /api/v1/kyc/status
  • GET /v2/swap/quote
  • GET /v2/swap/status/{quoteId}
  • GET /actions/{id}

Captured examples (repo fixtures)

Internal live captures used to author this guide (do not ship secrets from local .env):