- Auto-onramp — a fiat credential on a crypto wallet. The payer sends fiat; the wallet is credited in crypto.
- Auto-offramp — a crypto credential on a fiat wallet. The payer sends crypto; the wallet is credited in fiat.
Create a cross-currency credential
Pass an explicitcurrency on POST /v1/wallets/:id/credentials that is different from the wallet’s currency. Currency codes are uppercase (NGN, USDC, GHS, USDT, KES, …).
network is validated against the credential’s own currency, not the wallet’s. So an NGN credential uses bank; a USDC credential uses base / ethereum / solana.
Eligibility
Cross-currency is admitted only when every condition below holds. Any failure returns a400 with invalid_input.
- The pair must be crypto ↔ fiat. Crypto ↔ crypto and fiat ↔ fiat are rejected.
- Both currencies must advertise the matching direction capability:
- Auto-onramp requires
canOnrampon the credential currency AND on the wallet currency. - Auto-offramp requires
canOfframpon both.
- Auto-onramp requires
- The credential currency must resolve to a supported currency row. An unknown code returns
404not_found.
The set of currencies that support
canOnramp / canOfframp is fixed by GlobalStack. If your wallet’s currency isn’t eligible for a given direction, cross-currency provisioning will fail — reach out to support to confirm coverage.Deposit lifecycle
A cross-currency deposit tells you twice: once when the money arrives, and again as the conversion moves. The arrival event waits for the FX quote, so it can tell you what the deposit is worth in your wallet’s currency — the number you actually care about.What deposit.received carries
The event body is the transaction resource — the same shape as
GET /v1/transactions/{transactionId} — with source_* in the
credential’s currency and destination_* in your wallet’s currency:
wallet, customer and credential blocks tell you which credential was funded without a
second API call — useful when you hand out one virtual account per customer. They appear only on
cross-currency deposit events.
A cross-currency deposit never reaches a terminal
deposit.* state. It stays at received for
its whole life, and the conversion.* topics carry the outcome — do not wait for
deposit.confirmed or deposit.failed on this path. Subscribe to both the deposit and
conversion categories to see the full lifecycle.Held deposits
If an arriving deposit breaches the customer’s rolling money-in limit, you getdeposit.held
instead, with metadata.hold_reason set to tier_limit and a null destination_amount — no
conversion is quoted while the deposit is under review. When GlobalStack releases it, the
conversion runs at the then-current rate and you receive deposit.received carrying the
quoted amount, followed by the usual conversion.* sequence.
Same-currency credentials
Same-currency credentials keep the original lifecycle:deposit.received on arrival,
deposit.held if a tier gate applies, then deposit.confirmed when the credit settles (or
deposit.failed if it does not). Their payloads do not carry the wallet, customer and
credential blocks, and their destination_amount equals source_amount — there is no
conversion.
Fees
Auto-conversion fees follow the FX conversion contract: the destination amount is net of fees, and the fee breakdown is on theconversion.* webhook payloads. See failure reasons for the codes surfaced on a failed conversion.
Recovery on failure
Ifconversion.failed (or conversion.returned) fires, funds stay on the credential’s own balance — no auto-retry (FX failures are typically structural, not transient). Contact GlobalStack support to drain the parked funds; ops will run a fresh conversion at the then-current rate. Once the drain lands, the wallet balance is credited and the credential balance clears.