Before you start: the OMS API is in early access. Every endpoint, including the ones in this guide, requires an early-access API key. Request access before you begin. Cash-in is enabled per project; contact us to turn it on.Authenticate by exchanging your API key and secret for a bearer token at
POST /auth/token, then send it as Authorization: Bearer {accessToken} on every request. Every POST request also requires an Idempotency-Key header. See Get started for the full flow.Prerequisites
- A customer (
cst_) that has completed onboarding for your project. If the customer is not yet verified,POST /cash-insreturns403withKYCPendingorKYCRequired. - A destination wallet: an OMS wallet (
wlt_) owned by the customer, or an external wallet given by on-chain address or registered external account (ext_). - A webhook endpoint subscribed to
cashIn.*andtransaction.statusChanged, registered withPOST /webhooksor in the OMS Dashboard. See Receive webhooks.
Step 1: Find a cash location
Look up retail locations near the customer withGET /cash-locations. Pass flow=cash_in, the search coordinates, and the cash network identifier your project was enabled with in provider. Add customerId to return only locations the customer is eligible to use (their KYC country and, in the US, their state).
id (loc_) and a cashLocationReference. Keep both: you pass them on the cash-in as cash.locationId and cash.locationReference. Only offer locations where buyAllowed is true. The response also reports the network’s maxTransaction and maxDailyPerCustomer limits; show them to the customer before they set out.
Step 2: Create the cash-in
source(required):assetisusd.indicatedAmountis optional: the amount the customer expects to deposit, used for the estimate inpricing. The final amount is whatever cash the customer hands over.destination(required): theassetandnetworkto deliver, plus exactly one way to identify the wallet:wallet.id(an OMS wallet),wallet.blockchainAddress, orwallet.externalAccount(anext_ID). An OMS wallet must belong to the customer and holddestination.assetondestination.network; otherwise the request returns422withDestinationWalletMismatch.cash(required):locationIdandlocationReferencefrom step 1.sponsorGas(optional): onlytrueis supported; OMS covers the on-chain gas for delivery.
Response, 201 Created
depositInstructions.cashInCode: the code they present at the counter.source.details.locationNameandsource.details.locationAddress: where to go.expiresAt: the deadline to deposit.- The estimate in
pricing:pricing.source.amountis the cash amount,pricing.destination.amountis what reaches the wallet, andpricing.feeslists every fee. Until the deposit, these are based onindicatedAmount.
transactionId is absent until the deposit is confirmed. OMS sends cashIn.created when the code is issued.
Step 3: The customer deposits cash
The customer presents the code at the location and hands over the cash. This happens outside OMS. When the location confirms the deposit, the cash-in moves toprocessing and OMS sends cashIn.statusChanged. In sandbox, simulate this step with the sandbox endpoints.
If the customer needs a new code before expiresAt (for example, a lost or mistyped code), refresh it.
Step 4: Confirm completion
OMS recalculatespricing on the cash actually deposited, converts it, and delivers the crypto. The cash-in moves to completed, completedAt and transactionId are set, and OMS sends cashIn.statusChanged. Read the final state with GET /cash-ins/{cashInId}:
source and destination are unchanged from step 2 and omitted here.
The cash-in’s transaction
The deposit creates a transaction (transactionId) whose precursor is { "type": "cashIn", "id": "ci_..." }. Its source is a cash instrument and its destination is the wallet. OMS sends transaction.statusChanged on each change, with the transaction in data.resource:
processing (for example processing.fundsPulled once the cash is collected). For the on-chain hash, fetch the payout transfer with GET /transfers/{transferId} and read tracking.txHash.
Refresh the code
To replace the code before it expires, call refresh. The response is the full cash-in with a newdepositInstructions.cashInCode; the old code stops working.
expiresAt. OMS sends cashIn.updated with changedFields: ["depositInstructions"].
Expiry and failures
- The code expires unused. The cash-in moves to
expiredand OMS sendscashIn.statusChanged. No transaction is created and no money moved. Create a new cash-in if the customer still wants to deposit. - The cash-in fails. The cash-in moves to
failedwith afailureobject (code,message,retryable). - Delivery fails after the deposit. The cash-in’s transaction moves to
failed(for examplefailed.outboundFailed) and itsfailuresays why. OMS sendstransaction.statusChanged. - Review. A transaction can be held for review. It stays
processingwithsubStatusprocessing.underReview, then continues tocompletedorfailed. You do not need to act, and you should not surface the review to the customer.
Track cash-ins
Read one cash-in withGET /cash-ins/{cashInId}, or list them with GET /cash-ins, filtered by customerId, status, createdAfter, createdBefore, or search. Results are newest first.
Each event carries the resource under
data.resource. See Webhook events for the envelope.
Test in sandbox
In sandbox, three endpoints stand in for the retail counter. Each takes the currentcashInCode and the cash amount. They return 404 outside sandbox.
- Present the code:
POST /cash-ins/{cashInId}/simulate/present-codewith{ "cashInCode", "amount" }. The response includes aconfirmationId. - Hand over the cash:
POST /cash-ins/{cashInId}/simulate/deposit-cashwith{ "cashInCode", "amount", "originalConfirmationId" }, passing theconfirmationIdfrom step 1. The cash-in proceeds toprocessingand thencompleted. - Or cancel instead:
POST /cash-ins/{cashInId}/simulate/cancelwith the same body asdeposit-cashto cancel the authorization before the cash is handed over.
Key points
- Find the location first.
GET /cash-locations?flow=cash_ingives theidandcashLocationReferencethe cash-in needs, plus the network’s limits. - The amount is set at the counter.
indicatedAmountonly drives the estimate;pricingis recalculated on the cash actually deposited. - One code, one deadline. Refresh replaces the code but never extends
expiresAt. - Two resources to follow. The cash-in (
cashIn.*events) and the transaction it creates (transaction.statusChanged). - The on-chain hash is on the transfer. Read
tracking.txHashfrom the transaction’s payout transfer.