Mental model
How to think about the Capxul SDK — a method-based client over a money substrate, a deliberately narrow public surface, and an explicit result boundary.
Capxul lets an application work with identity, accounts, organizations, and money movement without building on the machinery underneath — auth provider calls, backend functions, settlement execution, transaction evidence. Your app says what should happen; Capxul makes it happen and returns durable records your app can render.
The whole SDK follows from one sentence:
Your app owns screens, routing, product intent, and presentation state. Capxul owns identity, account readiness, organization authority, money movement, and the substrate that makes those things durable.
Application code
routes, forms, dashboards, product decisions
calls SDK hooks or client methods
│
Capxul SDK surface
session, profile, account lifecycle, actor, payment, request, org
stable product records and mutation results
│
Capxul control plane
auth, identity persistence, account setup, membership, policy checks
money ledger, payment documents, reconciliation
│
Substrate (hidden from app code)
settlement execution, account infrastructure, transaction evidenceIf a concept affects what the user experiences, it appears as a product object, a lifecycle status, or a hook result. If a concept only explains how Capxul implemented that experience, it stays out of app code.
A method-based client
The core of the SDK is CapxulClient, organized as method bundles:
client.auth.signIn({ email });
client.account.getLifecycle();
client.payments.pay({ to, amount });
client.paymentRequests.create(input);
client.org(orgId).members();Each bundle is a domain — auth, account, payments, requests, organizations —
and each method expresses product intent ("pay this person", "complete
onboarding") rather than substrate mechanics. The methods return product
records: a Payment with a status, an amount, and documents — not a
transaction receipt.
The client/hooks split
There are two entry points, and they are the same client underneath:
@capxul/sdk— the imperative client, for scripts, servers, CLIs, agents, and anything not naturally React-rendered. You create it withcreateCapxulClientand call methods directly.@capxul/sdk-react— React bindings.CapxulProviderowns client creation, bootstrap state, and the TanStack Query cache; the hooks are thin query/mutation bindings over the client methods.
The hooks do not add capability — they add React ergonomics. A query hook like
useCapxulAccountBalance hides the cache key, waits for bootstrap, unwraps
the result, and re-fetches when a mutation invalidates it. A mutation hook
like useCapxulPay surfaces isPending and error and invalidates the money
state it changed. The hooks own reactivity; you still own the UI.
The surface is intentionally narrow
Creating a client takes a publishable key — and almost nothing else:
const result = await createCapxulClient({
publishableKey: process.env.CAPXUL_PUBLISHABLE_KEY!,
requirement: "counterfactual",
});The publishable key identifies your registered application; from it the SDK
resolves everything else — endpoints, runtime detection, timeouts, caching
defaults. The only other public inputs are requirement (how much account
setup your app needs — see the account lane)
and an optional signer for transaction-capable accounts.
This narrowness is deliberate, and it is worth understanding why:
- The hidden layers stay changeable. Capxul has already restructured its internal setup steps without breaking apps, because no app could reach them.
- App code cannot drift into the substrate. If transports, endpoints, and adapters were public options, product code would slowly take dependencies on machinery that was never a contract.
- The vocabulary stays money-shaped. A narrow surface is how the SDK keeps custody concepts from leaking into your codebase.
The trade-off is real: you cannot tune transport behavior, swap endpoints, or intercept setup steps from app code. Internal seams exist for Capxul's own tests and hermetic harnesses, but they are not the app surface — if you feel you need one for a product screen, look for the public record that represents what you actually want to render.
Bootstrap is not login
CapxulProvider (or createCapxulClient) starts by bootstrapping the
application: validating the publishable key and resolving runtime
configuration. That says nothing about a user. Keep the four readiness states
separate:
| State | Read it with | Meaning |
|---|---|---|
| Bootstrap ready | useCapxul() | The SDK client is ready for this app. |
| Signed in | useCapxulSession() | There is an authenticated user session. |
| Profile available | useCapxulProfile() | Capxul has an identity record for the user. |
| Account ready | useCapxulAccountLifecycle() | The account requirement the app chose is satisfied. |
Route guards usually want the session. Money surfaces usually want the account lifecycle. Treating "signed in" as "ready to transact" is the most common integration mistake; the states exist separately because the work between them is real.
Results, not thrown surprises
Every public client method returns Promise<CapxulResult<T>>:
type CapxulResult<T> = { ok: true; value: T } | { ok: false; error: CapxulError };const paid = await client.payments.pay(input);
if (!paid.ok) {
report(paid.error.message);
return;
}
renderPayment(paid.value);The success/error boundary is explicit and typed — you never catch unknown
thrown values from product methods. React hooks unwrap this for you: failures
surface as a CapxulError on the hook's error field, in normal TanStack
Query fashion.
Recovery follows the same layering. Do not rebuild the client after an error;
use the retry point that matches where the failure lives — useCapxul().retry()
for bootstrap failures, the account lifecycle's retry() for setup failures,
and the mutation's own retry for form-level failures.
The rule of thumb
When you are unsure whether something belongs in your app state, ask which side
of the split it is on. Selected route, form input, table filters, a selected
orgId or paymentId — yours. Session, profile, lifecycle, balances,
payments, members — Capxul's, read through hooks or client methods, never
cached in local copies the SDK is already maintaining and invalidating.
The rest of the concepts section unpacks the two vocabularies this model rests on: money & accounts for what moves, and identity & organizations for who moves it.
Requirements & signers
The requirement prop declares how much account readiness your app needs at start-up — none, counterfactual, or deployed — and the signer rules that go with it.
Money & accounts
The domain vocabulary of the platform — Money, Accounts, Transfers, Payments and their kinds, Commitments, payment documents, and the instant-send guardrail.