Guide
Custodial cash-in (on-ramp)
Receive USDC into your omnibus wallet on Solana or Stellar when users pay at MoneyGram stores. Credit only after status is completed.
Custodial cash-in (on-ramp) · Solana and Stellar
This guide covers custodial cash-in: receiving USDC into your omnibus wallet when a user pays fiat at a MoneyGram store. It applies to partners with walletCustody: 'custodial'.
For transaction status delivery (widget events, polling, status webhooks), see Transaction status & webhooks. For the widget creation event, see RAMPS_TRANSACTION_CREATED.
For the shared custodial setup (onboarding, omnibus wallets, sessions with customerIdentifier, profile APIs, security), see the Custodial integration overview. This guide covers only what is specific to cash-in.
| Area | Status |
|---|---|
| Custodial cash-in API (validate, commit, status) | Implemented |
Omnibus destination override (custodianCashInWallet) | Implemented |
Custodial session + KYC prefill (GET /v1/profiles/me) | Implemented |
Widget on-ramp with walletType: 'custodial' | Implemented |
| Internal balance crediting when USDC lands in omnibus | Partner-owned (see Crediting pattern) |
| Quickstart | Custodial cash-in quickstart. Host setup is Embed the widget |
Custodial cash-out is documented in the Web custodial cash-out guide. The partner debits an internal balance and sends USDC from a hot wallet when the widget fires RAMPS_DEPOSIT_ADDRESS.
Custodial cash-in is the reverse: the user pays fiat at a store and USDC is delivered to your omnibus cash-in wallet. There is no partner on-chain action before commit and no onDepositAddress step.
| Dimension | Custodial cash-out | Custodial cash-in |
|---|---|---|
| Widget mode | off-ramp | on-ramp |
| Partner on-chain action before commit | Required (onDepositAddress) | None |
| Omnibus wallet field | custodianCashOutWallet (source) | custodianCashInWallet (destination) |
| User action after commit | Pick up cash at agent | Pay fiat at store with confirmation code |
| Partner post-settlement action | N/A (funds already sent) | Credit internal customer balance |
-
Partner configuration
walletCustody: 'custodial'on the partner record (set at onboarding).custodianCashInWallet: address that receives on-ramp USDC. Solana: base58 public key. Stellar: G-address with a USDC trustline.- Configurable in the partner portal (Settings → Profile) or during onboarding.
-
Session
POST /v1/sessionsrequirescustomerIdentifierfor custodial partners.- JWT carries
walletType: 'custodial'andcustomerIdentifier. - Response includes
walletTypefor SDK configuration.
-
Validate / commit
- Same cash-in endpoints as non-custodial: quote → validate → commit.
- For custodial sessions, the API overrides
destinationWalletAddresswith your configuredcustodianCashInWallet.
-
Profile prefill
GET /v1/profiles/me(custodial only) looks up KYC bycustomerIdentifier.
-
Widget
- Use
mode: 'on-ramp'withwalletType: 'custodial'. - Pass
custodianCashInWalletaswallet.addressincreateRamps. - Use
onTransactionCreatedto persistidafter validate, plusmgiTransactionIdwhen the payload includes it (see RAMPS_TRANSACTION_CREATED).
- Use
const chain = 'solana' // or 'stellar' const { sessionToken, walletType, widgetUrl } = await fetch('https://playground.xramps.moneygram.com/api/v1/sessions', { method: 'POST', headers: { 'x-api-key': SECRET_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ customerIdentifier: user.id, walletAddress: CUSTODIAN_CASH_IN_WALLET, chain, }),}).then(r => r.json()) createRamps({ sessionToken, widgetUrl, wallet: { address: CUSTODIAN_CASH_IN_WALLET, chain, asset: 'USDC', walletType, }, transaction: { type: 'on-ramp' }, onTransactionCreated: (tx) => { // Always persist tx.id. tx.mgiTransactionId is optional, so persist it only when present. // See RAMPS_TRANSACTION_CREATED.md // customerIdentifier: from your session, not the widget payload }, onComplete: (tx) => { // Confirmation code shown. Not the same as MGI status completed },})
Do not implement onDepositAddress for cash-in. That callback is custodial cash-out only.
What differs by chain
You do not send the cash-in payment. MoneyGram delivers USDC to custodianCashInWallet. The address and the asset identifier differ.
| Solana | Stellar | |
|---|---|---|
| Omnibus address | Base58 public key | G-address |
| USDC identifier | SPL mint from the environment table in the custodial overview. Not Token-2022 | Circle issuer for that environment. The destination account needs a USDC trustline |
| Partner memo | None | None. You do not attach a memo to receive cash-in |
| When you credit | Ramps status completed, confirmed with GET /status?sync=true | Same |
Partner backend Widget / partner app Ramps API MGI / chain │ │ │ │ │── POST /sessions ───────│ (customerIdentifier) │ │ │◀─ { token, custodial }──│ │ │ │ │── quote (cash-in) ───────▶│ │ │ │── validate ──────────────▶│ → custodianCashInWallet │ │ │◀─ RAMPS_TRANSACTION_CREATED (after validate) │ │ │── commit ────────────────▶│ │ │ │◀─ confirmationCode ───────│ │ │ │ [user pays at store] │ │ │ │── status notification ───▶│ USDC → omnibus wallet │ │── credit internal balance for customerIdentifier ────│ │
Status delivery (poll vs webhook) is documented in Transaction status & webhooks.
When USDC lands in your omnibus wallet, you must credit the correct internal customer.
| Field | Source | Used for |
|---|---|---|
customerIdentifier | Session JWT (POST /v1/sessions) | MG profile/KYC, internal user lookup |
mgiTransactionId (optional) | Quote / validate; RAMPS_TRANSACTION_CREATED when returned | Webhook correlation. Fall back to the Ramps transactionId when absent |
Ramps transactionId | Validate; RAMPS_TRANSACTION_CREATED.id | GET /status, view mode |
Recommended flow:
1. Create session with customerIdentifier.2. On RAMPS_TRANSACTION_CREATED (or validate response): store { rampsTransactionId, customerIdentifier, amount, and mgiTransactionId when present }.3. User pays at store (confirmation code from commit).4. Receive status webhook or poll GET /status?sync=true.5. When status === 'completed', credit internal balance for customerIdentifier.
customerIdentifier is not in the widget event payload. You already have it from the session you issued.
See Transaction status & webhooks for webhook setup and the generic handler pattern.
| Item | Notes |
|---|---|
| Ramps-side crediting API | Partners own ledger updates today |
| E2E sandbox checklist | QA |
| Document | Topic |
|---|---|
| Custodial cash-in quickstart | 15-minute on-ramp for Solana and Stellar |
| Embed the widget | Web SDK, iframe, React Native WebView |
| Custodial integration overview | Shared setup, sessions, profiles, security (all custodial flows) |
| Transaction status & webhooks | Polling, status webhooks (all flows) |
| RAMPS_TRANSACTION_CREATED | Widget creation event |
| Web custodial cash-out | Off-ramp custodial guide |