Guide

Hosted embed

The lowest-code path: OroPocket renders the entire buy, sell and KYC experience inside a WebView you host.

You issue a single-use user_code for your user, get back an embed_url, and load it in a WebView. Users created this way are attributed to you, so their buys earn your commission.

1. Issue a user_code

POST/partner/users/init
FieldTypeDescription
mobilestringrequiredThe end user's 10-digit mobile (or 12-digit with 91).
namestringoptionalPrefill the user's name (new users only, max 128).
emailstringoptionalPrefill the user's email (new users only).
metaobjectoptionalFree-form JSON, persisted and echoed back — useful for campaign tagging and support.
flowstringoptionalOptional. Only needed if your token has BOTH capabilities: "api" forces trusted registration instead of the embed flow.
curl -X POST https://api.oropocket.com/partner/users/init \
  -H "Authorization: Bearer oro_live_xxx" \
  -d '{"flow":"embed","mobile":"9876543210","name":"Anya"}'
Response
{
  "success": true,
  "data": {
    "user_code": "usr_xxx",
    "embed_url": "https://connect.oropocket.com/?u=usr_xxx",
    "expires_at": "2026-08-08T09:22:00.000Z",
    "expires_in_seconds": 600,
    "user_existed": false,
    "mode": "live"
  }
}

The code is single-use and expires in 10 minutes — issue it when the user taps into the feature, not ahead of time. Reusing one returns USER_CODE_CONSUMED.

2. Load the embed in a WebView

Point a WebView at the embed_url. Both settings below are required: JavaScript, and DOM storage (the session token lives in localStorage).

Multi-window support is required. Recurring-plan (SIP) mandates open a payment popup, so Android needs setSupportMultipleWindows(true) plus a WebChromeClient.onCreateWindow override (target SDK 24+), and iOS needs createWebViewWith. Without them, mandate flows fail silently. You must also forward upi:// and intent:// URLs to the OS or UPI payments won't open.
val webView = WebView(context)
webView.settings.javaScriptEnabled = true      // required
webView.settings.domStorageEnabled = true      // required (session in localStorage)
webView.settings.setSupportMultipleWindows(true) // required (mandate popup)

webView.webChromeClient = object : WebChromeClient() {
  override fun onCreateWindow(
    view: WebView?, isDialog: Boolean, isUserGesture: Boolean, resultMsg: Message?
  ): Boolean {
    val popup = WebView(context).apply { settings.javaScriptEnabled = true }
    (resultMsg?.obj as? WebView.WebViewTransport)?.webView = popup
    resultMsg?.sendToTarget()
    return true
  }
}

webView.webViewClient = object : WebViewClient() {
  override fun shouldOverrideUrlLoading(v: WebView?, r: WebResourceRequest?): Boolean {
    val url = r?.url?.toString() ?: return false
    if (url.startsWith("upi://") || url.startsWith("intent://")) {
      // hand UPI apps off to the OS
      val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
      context.startActivity(intent)
      return true
    }
    return false
  }
}

webView.loadUrl(embedUrl)

On iOS, list the UPI schemes you intend to open (upi, phonepe, tez, paytmmp, gpay, …) under LSApplicationQueriesSchemes in Info.plist, or canOpenURL fails silently.

Preview the whole embed flow in a phone simulator from the Playground → Embed tab. Sandbox OTP is always 1234.

Host-app configuration (multi-window, UPI deep links, camera for KYC) has its own page: WebView & UPI setup.

3. The session handshake (what the embed does)

You normally never call these — the embedded SPA does, using the user_code as its auth artifact. They take no bearer token. They're documented because they explain the session model, and because attribution is set here.

POST/partner/embed/exchange

Called on load. Either mints a session immediately, or reports that an OTP is needed.

FieldTypeDescription
user_codestringrequiredSingle-use. Reusing one returns 409 USER_CODE_CONSUMED.
FieldTypeDescription
session_tokenstringPresent when the user was already verified — the SPA is signed in.
requires_otpbooleanTrue when an OTP was dispatched instead.
mobile_maskedstringShown to the user so they know where the code went.
otp_channelstringe.g. SMS or WHATSAPP.
user_will_be_createdbooleanTrue when verifying will create a brand-new OroPocket account — this is the case that attributes the user to you.
POST/partner/embed/verify-otp
This is the only moment attribution is set. A brand-new account created here counts as yours for commission; a pre-existing OroPocket user never does. See Attribution & commission.
FieldTypeDescription
user_codestringrequiredThe code being verified.
otpstringrequired4–8 digits. Always 1234 in sandbox.
FieldTypeDescription
session_tokenstringStored in localStorage by the SPA. Valid for 7 days.
userobject{id, name, mobile, email, mobile_verified, email_verified}.
balancesobjectOpening balances for the embedded UI.
is_new_userbooleanTrue when the account was just created (and attributed to you).
needs_profile_setupbooleanThe SPA collects the remaining profile fields.
POST/partner/embed/resend-otp
FieldTypeDescription
user_codestringrequiredMust still be awaiting OTP. Throttled per IP and per code.
FieldTypeDescription
cooldown_secondsnumberWait this long before resending again.

4. Listen for webhooks

You don't poll — configure a webhook and we notify you on buy.completed, sell.completed and more.