Embedded UI (tokens + `/embed/v1`)

ProphetX provides drop-in UI components that handle trading and payment flows inside your frontend. They authenticate to ProphetX directly using a short-lived JWT that your backend mints for each user session, then call /embed/v1/* routes from the user's browser.

Your only responsibility on the backend is to mint those tokens. You don't call /embed/v1 yourself.

Auth on GET /tokens: user-scoped (sub + subsig) — see Authentication.


1. How the pieces fit together

Your frontend                    ProphetX-hosted embedded components
     │                                       │
     │  fetches a token                      │
     ├──► your backend                       │
     │       │                               │
     │       │ GET /private/v1/tokens        │
     │       └──────────────────────────────►│
     │                                       │
     │  token + permissions + gates          │
     │◄──────────────────────────────────────│
     │                                       │
     │  init component with token            │
     ├──► embedded UI                        │
     │                                       │
     │                                       │  /embed/v1/* calls
     │                                       ├──► ProphetX API
     │                                       │
     │                                       │  validates token, returns
     │                                       │◄──── responses

The token is the only ProphetX credential that ever touches the browser. It's short-lived and scoped to a single user session.


2. GET /private/v1/tokens

User-scoped. No body.

Response (TokenResponse):

{
  "token": "eyJhbGciOi...",
  "isvId":  "00000000-0000-0000-0000-000000000000",
  "userId": "00000000-0000-0000-0000-000000000000",
  "expiration": "2026-05-12T12:05:00Z",
  "permissions": {
    "trade":    { "granted": true,  "description": "Can submit orders" },
    "withdraw": { "granted": false, "description": "Can withdraw funds", "denyReason": "KYC pending" }
  },
  "gates": {
    "kyc":   { "completed": true,  "description": "KYC verification" },
    "terms": { "completed": false, "description": "Accept current terms" }
  }
}

What to do with each field:

FieldUse
tokenHand to the embedded UI component as its auth credential. Don't log it.
expirationRefresh before it expires; embedded components also surface refresh hooks.
permissionsMap of permission key → { granted, description, denyReason? }. Drive your UI: only render trading controls if permissions.trade.granted. Show denyReason to explain a "no".
gatesOnboarding gates the user must clear. Use these to drive an onboarding flow that walks them through any completed: false gate.

permissions and gates are maps with arbitrary keys — new ones can appear without an API version bump. Don't hard-code an exhaustive switch; iterate the keys you got.


3. What /embed/v1/* exposes

You don't call these directly, but here's what the embedded UI is doing under the hood so you know what's reachable from a logged-in user's browser session.

MethodPathPurpose
GET/embed/v1/payment/methodsList available payment methods (proxies to the funds system).
POST/embed/v1/payment/init-providerInitialize a payment provider session.
POST/embed/v1/payment/depositStart a deposit.
POST/embed/v1/payment/deposit-resultFinalize a deposit.
POST/embed/v1/payment/withdrawStart a withdrawal.
POST/embed/v1/payment/withdraw-resultFinalize a withdrawal.
GET/embed/v1/termsSame payload as /private/v1/terms.
GET/embed/v1/terms/USERIDWhether this user has accepted the current terms.
POST/embed/v1/terms/USERIDRecord acceptance.
GET/embed/v1/walletThe calling user's Wallet row. Always user-scoped — the token's sub decides whose wallet is returned, so the component cannot read another user's wallet or the ISV pot. Same shape as /private/v1/wallets.
GET/embed/v1/token/validateValidate an embed token and return the same TokenResponse payload.

The terms routes are a straight passthrough to the private equivalents in User Onboarding (users, KYC, terms), so you can either handle T&Cs on your own backend or let the embedded UI handle them.

The payment routes are the only path for deposits and withdrawals — the private ISV API doesn't expose money-movement endpoints. Your frontend embeds the payment component, and the component calls /embed/v1/payment/* itself.


4. Token rotation

Tokens are short-lived (the expiration claim). Refresh them before they expire. A common pattern:

  1. On session start, fetch a token.
  2. Schedule a refresh just before expiration.
  3. When refreshing, fetch a new token from GET /private/v1/tokens and pass it to the embedded component.
  4. On the user logging out, drop the token. It can't be revoked from your side, but it will expire shortly.

Don't try to reuse a token across users or sessions — one token, one user, one short window.


5. Curl

BASE="https://isv-staging-api.betprophet.co/private/v1"

# Mint a token for a user
curl "$BASE/tokens" -H "Authorization: Bearer $JWT_USER"

The response goes back to your frontend over your own authenticated session with the user — not directly to the browser as a redirect from this API.