Deposits and Withdrawals
Integrate the ProphetX Wallet so users can deposit and withdraw funds.
Upfront verification and terms (optional)This guide walks through a just-in-time pattern — users are prompted to accept ProphetX's terms and complete identity verification the first time they open a deposit or withdrawal. If you prefer, you can run these steps upfront during your registration flow so the wallet flows open straight into the modal.
useOneClickSignup— for a brand-new user: creates the ProphetX user from a phone number. There is no bundled first-deposit option — trigger a deposit separately afterward if you want that.useKyc/useIdUpload— for an existing user whose automated KYC came backFAILURE.- Terms acceptance — there's no SDK hook for this; you host
/tandcyourself in a raw iframe.
What you'll do
- Set up the SDK — install and configure the ProphetX SDK for your stack.
- Open a deposit — let them fund their account via your frontend.
- View transaction history — show users their recent transactions and statuses.
- Display the balance — show the user's current wallet balance.
- Add withdrawals — let them withdraw funds.
Before you begin
Complete the User Setup guide first — you'll need a created and verified ProphetX user before continuing.
Complete the Session Token Setup guide — your backend signs short-lived JWTs that authorize the embed to act on behalf of each user.
Step 1: Set up the SDK
Install the package for your stack:
npm install @prophetx/sdk-reactThen set up the provider:
The ProphetXProvider wraps your component tree. The token prop is optional — you can mount the provider at app startup and pass the token once it's available. The SDK passes it to the iframe when any flow opens. If the token expires mid-flow, onSessionExpired lets you return a fresh one. Optional theme and locale props let you brand the embed and switch language ("en" or "es").
import { useState, useEffect, useCallback } from "react";
import { ProphetXProvider } from "@prophetx/sdk-react";
import { App } from "./App";
export function Root() {
const [token, setToken] = useState<string>();
// Calls YOUR backend token endpoint.
const refreshToken = useCallback(async () => {
const res = await fetch("/api/prophetx-token");
const { token } = await res.json();
setToken(token);
return token;
}, []);
useEffect(() => { refreshToken() }, [refreshToken]);
return (
<ProphetXProvider
environment="sandbox" // "production" | "staging" | "sandbox"
token={token}
onSessionExpired={refreshToken}
>
<App />
</ProphetXProvider>
);
}Step 2: Open a deposit
With the provider in place, opening a deposit is a single function call. The SDK renders a modal that handles the full deposit flow. When the user completes it, onSuccess fires with a transactionId and amount — store the transactionId on your backend to track deposit status.
User PrerequisitesWhen the embed opens it validates the user's gates (
kyc,terms,email-verify,phone-verify,not-suspended) viaGET /embed/v1/token/validate. KYC is not exempt — it's a required gate for the deposit permission exactly like the others, so a session token alone does not imply KYC is complete. If any required gate is unmet,onErrorfires with a single generic code,PERMISSION_DENIED, carrying apending_gatesarray naming which ones:
pending_gatesincludesterms— there's no SDK hook for this; host/tandcyourself in a raw iframe (see Terms flow). Terms acceptance genuinely is enforced on deposit/withdraw — every deposit/withdraw permission requires the terms gate, so a user who's never accepted anything will getPERMISSION_DENIEDwithtermsinpending_gates, same as any other unmet gate.pending_gatesincludeskyc— this user already exists (they hold an embed token), so route them touseKyc/useIdUpload(document verification), notuseOneClickSignup(that's only for creating a brand-new user, before any token exists).pending_gatesincludesemail-verify/phone-verify— there's no embedded flow; handle these on your side (re-send verification email, OTP UI).pending_gatesincludesnot-suspended— not user-resolvable; direct the user to support.
onGeofenceBlocked(stateCode, reason)is also wired on this flow. In the current architecture, geo-eligibility is enforced when your backend mints the embed token rather than per-request, so this shouldn't fire in normal operation with a validly-issued token — but the SDK still defines and can deliver it, so wire a handler defensively.
import { useDeposit } from "@prophetx/sdk-react";
export function DepositButton() {
const { open, close, isOpen } = useDeposit();
return (
<button
onClick={() =>
open({
onSuccess: (transactionId, amount) => {
console.log("Deposited", amount, "tx:", transactionId);
},
onError: (code, message) => {
console.error("Deposit error:", code, message);
},
})
}
>
Deposit
</button>
);
}Step 3: View transaction history
Once a user completes a deposit or withdrawal, you'll want to give them visibility into their recent transactions — including the current status of each one.
Rather than exposing this through the SDK, transaction history is served directly by the ProphetX backend, server-to-server — there is no embed-facing transactions endpoint. Your server calls GET /private/v1/transactions, then returns the results to your client to render in your UI. See Wallets for the full request/response shape, pagination, and the TransactionType enum.
The transactionId returned by onSuccess in the deposit and withdrawal flows corresponds to the refId query parameter on that endpoint, so you can filter straight to the ledger rows for one transaction.
Step 4: Display the balance
Use the wallet hook to fetch and display the user's current balance. refetch is manual — call it on mount and after any deposit or withdrawal completes.
import { useEffect } from "react";
import { useWallet } from "@prophetx/sdk-react";
export function BalanceDisplay() {
const { balance, loading, error, refetch } = useWallet();
useEffect(() => { refetch() }, [refetch]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Failed to load balance.</p>;
return (
<p>{balance ? `${balance.totalBalance} ${balance.currency}` : "—"}</p>
);
}Step 5: Add withdrawals
Withdrawals follow the same pattern as deposits — same open() call, same callbacks, same onError codes, and the same gate-driven prerequisite check described in Step 2. Before the withdraw (or deposit) modal renders, the SDK validates the user's session token against the kyc/terms/email-verify/phone-verify/not-suspended gates. If any required gate is unmet, onError fires with PERMISSION_DENIED and a pending_gates array (which may include kyc, terms, or both) — and the withdraw UI never appears.
Withdraw specifically may also surface a literalonError("KYC_REQUIRED", message)per the frontend SDK's own source — but that code's backend origin is unconfirmed (it doesn't appear anywhere in isv-backend). See the caveat in Error Codes before building against it; treat it the same asPERMISSION_DENIEDwithpending_gates: ["kyc"]if it does fire.
Handle this by checking pending_gates: if terms is missing, host /tandc yourself (no SDK hook exists for it); if kyc is missing, open useKyc/useIdUpload (this user already exists, so it's re-verification, not signup). Once the relevant success signal fires, retry the withdrawal.
import { useWithdraw } from "@prophetx/sdk-react";
export function WithdrawButton() {
const { open, close, isOpen } = useWithdraw();
return (
<button
onClick={() =>
open({
onSuccess: (transactionId, amount) => {
console.log("Withdrew", amount, "tx:", transactionId);
},
onError: (code, message) => {
console.error("Withdraw error:", code, message);
},
})
}
>
Withdraw
</button>
);
}
What your user sees when a gate is missingIf only
termsis pending, host/tandcyourself in an iframe (no SDK hook exists for this — see Terms flow) to show just the T&C acceptance screen. Ifkycis pending — this only happens for a user re-verifying after an earlier automatedFAILURE, since a session token implies a ProphetX user already exists —useKyc/useIdUploadopens IDComply's document-upload flow in a second browser tab, not a phone/OTP screen.Document verification terminates with one of three callbacks:
onKycSuccess(verified — final, no further async step),onKycPending(still under review), oronKycFailed(rejected, with aretryableflag). Build your retry-withdrawal prompt offonKycSuccessonly.
Once a user has cleared verification and accepted T&C, the kyc and terms gates report completed: true, PERMISSION_DENIED stops firing for those reasons, and future deposits and withdrawals open straight into the modal. ProphetX tracks verification state on the backend — you don't need to cache or check it yourself.
Next steps
Now that you have a working integration, explore these guides to polish it:
- API Reference — full props, hooks, and types for all three SDKs
- Theming Guide — customize colors, dark mode, and CSS variables
- postMessage Protocol — low-level event reference for custom integrations
- Error Codes — every error code, what causes it, and what to do
Updated 15 days ago
