Accepting deposits

Sessions in depth — routing, the hosted page, expiry, rendering your own QR, and crediting the end user safely.

A deposit is money in from your end user. It starts with a session: OmiCloud creates the transaction, asks the chosen gateway for a hosted checkout, and hands you the URL. The end user pays on the gateway's page. OmiCloud never touches the funds.

your app ──POST /v1/sessions──▶ OmiCloud ──▶ gateway ──▶ hosted URL
                                                             │
end user ◀──────────── you redirect / open ──────────────────┘
end user ──pays on gateway page──▶ gateway ──callback──▶ OmiCloud ──webhook──▶ you

1. Know the limits first

Read the routes before you render an amount field, and validate against them client-side so the end user never sees AMOUNT_TOO_LOW:

curl "https://api.omicloud.io/v1/routes?direction=deposit&currency=USDT&country=PH" \
  -H "Authorization: Bearer omi_live_…"

Routes change rarely — cache them for an hour.

2. Create the session

curl -X POST https://api.omicloud.io/v1/sessions \
  -H "Authorization: Bearer omi_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: dep-20260902-001" \
  -d '{
    "amount": "100",
    "currency": "USDT",
    "network": "TRX",
    "endUserId": "player-8812",
    "country": "PH",
    "operatorReference": "dep-20260902-001",
    "redirectUrl": "https://operator.example/wallet?dep=dep-20260902-001",
    "metadata": { "campaign": "sept" }
  }'

Field by field:

FieldWhy it matters
amount, currency, networkWhat the end user will be asked to pay. Amount is a decimal string.
endUserIdYour id for the end user. Drives routing (previouslyUsed) and lets you search transactions per user. Strongly recommended.
countryAffects which gateways are eligible. Send the end user's country, not yours.
gatewayOmit it. OmiCloud routes. Force a gateway only when you have a reason (see below).
operatorReferenceYour own id. Comes back on every read and every webhook. Make it the same as the Idempotency-Key and you have one id to log.
redirectUrlWhere the hosted page sends the end user afterwards. The domain must be allow-listed on your account in the console.
metadataUp to 20 string values, returned untouched.

The response:

{
  "id": "ses_01j6y9f0k3qz",
  "transaction": "txn_8f3a1c9e02b4",
  "state": "processing",
  "gateway": "dce",
  "url": "https://pay.dcepay.io/s/eyJhbGciOi…",
  "expiresAt": "2026-09-02T10:20:00Z",
  "deposit": { "address": "TQn9…bLSE", "network": "TRX", "currency": "USDT", "amount": "100" }
}

Store transaction against your own record immediately — that is the reference every later event will carry.

3. Send the end user to pay

Two options:

Hosted page (default). Open url — as a redirect on web, a full-screen web view on mobile. The gateway's page shows the address, amount, a QR, and a countdown, and handles whatever the gateway needs (some gateways run KYC here). When the end user is done the page sends them to redirectUrl.

Your own UI. For crypto sessions deposit gives you the address, network, currency and amount. Render your own QR and copy button and skip the hosted page entirely. You are then responsible for showing the expiry and for telling the end user to send exactly deposit.amount on exactly deposit.network — a wrong network is lost funds.

Either way, the end user returning to redirectUrl means nothing on its own. Never credit on redirect. Credit on state == settled.

4. Expiry

A session is open for 15 minutes. If no deposit is detected by expiresAt, OmiCloud declines the transaction with declineReason: session_expired and sends session.expired.

If the end user pays after expiry, the gateway may still confirm it. declined → settled is not a legal transition, so OmiCloud does not resurrect the expired transaction. Instead the late deposit is recorded as a new transaction: operatorReference copied from the original, metadata.lateDepositFor set to the expired reference, and a normal transaction.settled event. When you receive a settlement for a reference you did not create, look at that field. This is rare, but it is real money — credit it.

5. Credit the end user

On transaction.settled (or when polling shows state == settled):

  1. Fetch GET /v1/transactions/{reference}. Do not trust the webhook body alone.
  2. Check state == settled and that you have not already credited this reference. Webhooks can be delivered more than once.
  3. Credit netAmount if the end user bears fees, amount if you do.
  4. Record the reference as credited.

Forcing a gateway

Pass gateway when:

  • the end user chose it from quotes you displayed (POST /v1/quotes),
  • you are A/B testing a new gateway,
  • a gateway is having a bad day and OmiCloud's routing has not caught up yet.

A forced gateway you are not connected to returns 403 GATEWAY_NOT_CONNECTED. Nothing is created.

When submission fails

If the gateway rejects the session at creation, you get 502 GATEWAY_ERROR. The transaction was created and is declined; the response has its reference and gatewayDetails. Show the end user an error and let them try again — that is a new session with a new idempotency key.

If gateway was omitted and more than one gateway is eligible, OmiCloud automatically tries the next one before returning an error. events on the transaction shows each attempt.


Did this page help you?