@capxul/sdk-react
v0.2.0-alpha.3
Published
React provider + hooks for the @capxul/sdk headless client.
Downloads
1,331
Maintainers
Readme
@capxul/sdk-react
React provider + hooks for Capxul. Wraps @capxul/sdk
in a synchronously-mounting provider with lazy bootstrap and a
discriminated-union return shape on every read hook.
Alpha —
0.xis pre-release. APIs can change between alpha versions. Pin to an exact version and read the changelog before upgrading.
Install
pnpm add @capxul/sdk @capxul/sdk-react react @tanstack/react-query
# or: npm install @capxul/sdk @capxul/sdk-react react @tanstack/react-queryPeer requirements:
- React
>= 19.0.0 @tanstack/react-query^5@xstate/react^5(only if you use the flow hooks)
Two-layer trust
This package is the orchestration half of Capxul's two-layer
architecture. Funds are custodied by audited, open-source Safe v1.4.1
contracts published in the Capxul GitHub repo.
The React glue here is proprietary — see LICENSE.
Quickstart
Wrap your app once with CapxulProvider. Pass a publishable key
(cap_pk_live_… / cap_pk_test_…). The provider mounts synchronously
— no Suspense gate, no async-mount stall — and lazily bootstraps the
runtime URLs from /v1/client/bootstrap on the first SDK call:
import { CapxulProvider } from "@capxul/sdk-react";
export default function RootLayout({ children }) {
return (
<CapxulProvider
config={{
mode: "publishable-key",
publishableKey: process.env.NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY!,
}}
>
{children}
</CapxulProvider>
);
}Inside the tree, read identity with useMe(). The hook collapses the
multi-step lifecycle (not-bootstrapped, bootstrapping, no-session,
no-data, live) into three states the consumer cares about:
import { useMe } from "@capxul/sdk-react";
function Header() {
const me = useMe();
if (me.isPending) return <span>Loading…</span>;
if (me.isError) return <span>Couldn't load profile.</span>;
return <span>Hi, {me.data.username}</span>;
}Drive an OTP sign-in flow with useAuthFlow() — a
useActor observer
over the canonical XState v5 machine in @capxul/sdk:
import { useAuthFlow } from "@capxul/sdk-react";
import { toEmail } from "@capxul/sdk";
function SignIn() {
const { snapshot, send } = useAuthFlow();
if (snapshot.matches("idle")) {
return (
<button
onClick={() =>
send({ type: "REQUEST_OTP", email: toEmail("[email protected]") })
}
>
Send code
</button>
);
}
// … handle "sending_otp", "otp_requested", "verifying", "authenticated"
}Lifecycle observability — useCapxulStatus()
Need a status indicator or a gate UI? Subscribe to the underlying transport state machine:
import { useCapxulStatus } from "@capxul/sdk-react";
function Banner() {
const status = useCapxulStatus();
switch (status.status) {
case "idle": // no network call yet
case "bootstrapping": // /v1/client/bootstrap in flight
return <Spinner />;
case "ready": // bootstrapped, no session
case "authenticated": // bootstrapped + session live
return null;
case "error":
return <ErrorBanner error={status.error} />;
}
}useCapxulStatus() is a thin
useSyncExternalStore
subscription against the transport singleton — only the components
that actually call it re-render when state changes. The provider
itself never re-renders.
Publishable-key proof status
The publishable-key path is runtime-proven where the repo can run it without secrets:
| Surface | Runtime proof | Expected safe signal |
|---|---|---|
| Provider + useMe() | corepack pnpm --filter @capxul/sdk-react check-types plus the headless proof tests under packages/sdk-react/ops/proof | provider reaches useCapxulStatus().status === "ready" after one bootstrap request |
| Reference CLI mock | corepack pnpm --filter @capxul/reference-cli build && node apps/reference-cli/dist/cli.js bootstrap probe --mock --json | ok:true, mode:"publishable-key", bootstrapRequests:1, authRequests:1, keyLengthClass:"provided" |
| Reference CLI live | same command without --mock, with CAPXUL_REF_PUBLISHABLE_KEY and optional CAPXUL_REF_BOOTSTRAP_URL set locally | success only when the key/origin/runtime are valid; otherwise sanitized SDK error JSON |
Do not paste or commit publishable keys, session tokens, Convex JWTs,
cookies, or provider payloads. Proof output reports only
keyLengthClass.
Hooks catalogue
| Surface | Hook |
|---|---|
| Imperative client | useCapxul() |
| Identity | useMe, useAccount |
| Organizations | useOrganization, useOrganizations, useMember, useMembers |
| Payments | usePayment, usePayments, useOrgPayments |
| Transfers | useTransfer, useTransfers, useOrgTransfers |
| Withdrawals | useWithdrawal, useWithdrawals, useOrgWithdrawals |
| Documents | useDocument, useDocuments, useOrgDocuments |
| Sub-accounts / virtual | useSubAccount, useSubAccounts, useVirtualAccount, useVirtualAccounts, useVirtualCard, useVirtualCards |
| Settings | useApiKey, useApiKeys, useWebhookEndpoint, useWebhookEndpoints, useWebhookEvent, useExternalAccount, useExternalAccounts, useBalanceLedgerEntry, useBalanceLedger |
| KYC / KYB | useKycProfile |
| Treasury | useTreasury, useSafe |
| Operations | useOperation (correlation join key per CANON.md §3.3) |
| Lifecycle | useCapxulStatus |
| Flows | useAuthFlow, useOnboardingFlow, useProvisioningFlow |
Most "not-yet-implemented" verticals return a QueryResult<T> in the
error state with code: "NOT_IMPLEMENTED" — they compile, render
without crashing, and surface a clear runtime signal.
Three-state read shape (QueryResult<T>)
Read hooks return a discriminated union:
type QueryResult<T> =
| { readonly status: "loading" }
| { readonly status: "data"; readonly data: T }
| { readonly status: "error"; readonly error: CapxulError };Hooks never throw for a "still loading" condition. Pattern-match
on status. (useMe() is the first hook migrated to TanStack Query
— it returns UseQueryResult<Account, CapxulError> for now; the
remaining hooks lift to that shape over time.)
Server / CLI consumers
@capxul/sdk-react is browser-only. For server, CLI, and harness
code, build a CapxulClient directly with createCapxulClient from
@capxul/sdk — no provider, no React. See that package's README.
License
Proprietary — see LICENSE. The orchestration code in
this package is governed by the Capxul Terms of Service. The
value-bearing custody contracts are open source.
Contact: [email protected]
