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
/partner/users/init| Field | Type | Description | |
|---|---|---|---|
| mobile | string | required | The end user's 10-digit mobile (or 12-digit with 91). |
| name | string | optional | Prefill the user's name (new users only, max 128). |
| string | optional | Prefill the user's email (new users only). | |
| meta | object | optional | Free-form JSON, persisted and echoed back — useful for campaign tagging and support. |
| flow | string | optional | Optional. 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"}'{
"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).
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.
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.
/partner/embed/exchangeCalled on load. Either mints a session immediately, or reports that an OTP is needed.
| Field | Type | Description | |
|---|---|---|---|
| user_code | string | required | Single-use. Reusing one returns 409 USER_CODE_CONSUMED. |
| Field | Type | Description |
|---|---|---|
| session_token | string | Present when the user was already verified — the SPA is signed in. |
| requires_otp | boolean | True when an OTP was dispatched instead. |
| mobile_masked | string | Shown to the user so they know where the code went. |
| otp_channel | string | e.g. SMS or WHATSAPP. |
| user_will_be_created | boolean | True when verifying will create a brand-new OroPocket account — this is the case that attributes the user to you. |
/partner/embed/verify-otp| Field | Type | Description | |
|---|---|---|---|
| user_code | string | required | The code being verified. |
| otp | string | required | 4–8 digits. Always 1234 in sandbox. |
| Field | Type | Description |
|---|---|---|
| session_token | string | Stored in localStorage by the SPA. Valid for 7 days. |
| user | object | {id, name, mobile, email, mobile_verified, email_verified}. |
| balances | object | Opening balances for the embedded UI. |
| is_new_user | boolean | True when the account was just created (and attributed to you). |
| needs_profile_setup | boolean | The SPA collects the remaining profile fields. |
/partner/embed/resend-otp| Field | Type | Description | |
|---|---|---|---|
| user_code | string | required | Must still be awaiting OTP. Throttled per IP and per code. |
| Field | Type | Description |
|---|---|---|
| cooldown_seconds | number | Wait 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.