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.

CustodialOn-rampSolana and StellarUSDC

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.


Summary

AreaStatus
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 omnibusPartner-owned (see Crediting pattern)
QuickstartCustodial 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.

DimensionCustodial cash-outCustodial cash-in
Widget modeoff-rampon-ramp
Partner on-chain action before commitRequired (onDepositAddress)None
Omnibus wallet fieldcustodianCashOutWallet (source)custodianCashInWallet (destination)
User action after commitPick up cash at agentPay fiat at store with confirmation code
Partner post-settlement actionN/A (funds already sent)Credit internal customer balance

  1. 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.
  2. Session

    • POST /v1/sessions requires customerIdentifier for custodial partners.
    • JWT carries walletType: 'custodial' and customerIdentifier.
    • Response includes walletType for SDK configuration.
  3. Validate / commit

    • Same cash-in endpoints as non-custodial: quote → validate → commit.
    • For custodial sessions, the API overrides destinationWalletAddress with your configured custodianCashInWallet.
  4. Profile prefill

    • GET /v1/profiles/me (custodial only) looks up KYC by customerIdentifier.
  5. Widget

    • Use mode: 'on-ramp' with walletType: 'custodial'.
    • Pass custodianCashInWallet as wallet.address in createRamps.
    • Use onTransactionCreated to persist id after validate, plus mgiTransactionId when the payload includes it (see RAMPS_TRANSACTION_CREATED).

Widget integration

TypeScript
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.

SolanaStellar
Omnibus addressBase58 public keyG-address
USDC identifierSPL mint from the environment table in the custodial overview. Not Token-2022Circle issuer for that environment. The destination account needs a USDC trustline
Partner memoNoneNone. You do not attach a memo to receive cash-in
When you creditRamps status completed, confirmed with GET /status?sync=trueSame

Lifecycle

TEXT
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.


Crediting pattern

When USDC lands in your omnibus wallet, you must credit the correct internal customer.

FieldSourceUsed for
customerIdentifierSession JWT (POST /v1/sessions)MG profile/KYC, internal user lookup
mgiTransactionId (optional)Quote / validate; RAMPS_TRANSACTION_CREATED when returnedWebhook correlation. Fall back to the Ramps transactionId when absent
Ramps transactionIdValidate; RAMPS_TRANSACTION_CREATED.idGET /status, view mode

Recommended flow:

TEXT
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.


Open items

ItemNotes
Ramps-side crediting APIPartners own ledger updates today
E2E sandbox checklistQA

DocumentTopic
Custodial cash-in quickstart15-minute on-ramp for Solana and Stellar
Embed the widgetWeb SDK, iframe, React Native WebView
Custodial integration overviewShared setup, sessions, profiles, security (all custodial flows)
Transaction status & webhooksPolling, status webhooks (all flows)
RAMPS_TRANSACTION_CREATEDWidget creation event
Web custodial cash-outOff-ramp custodial guide