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.
The quickstarts stop at sign-in. The moment your product shows a balance or
moves money, the signed-in user also needs a Capxul account — and the
requirement prop on CapxulProvider is how you declare, once at mount, how
much account readiness the app needs. createCapxulClient accepts the same
input with the same semantics.
<CapxulProvider publishableKey={publishableKey} requirement="deployed">
{children}
</CapxulProvider>The requirement ladder
Each rung asks the platform to do more work after sign-in, so pick the lowest rung the product actually needs:
requirement | What the user has after setup | Use when |
|---|---|---|
"none" (default) | A signed-in session only. No account is provisioned. | Auth-only apps: login, profile, org browsing. |
"counterfactual" | A stable account address, reserved before the account is fully activated. | The product needs a stable account reference — something to display or register elsewhere — but no money movement yet. |
"deployed" | A fully activated account that can transact. | Anything that shows a balance or moves money. Gate that UI on readiness (below). |
The requirement is an init-time target, not a blocking gate: sign-in returns as fast as ever, and the SDK works toward the target in the background.
What happens after sign-in
When requirement is not "none", the account lane runs automatically:
your app calls signIn and verifyOtp and nothing else — the SDK provisions
the account behind the scenes. How the lane works is covered in
Account lane; what your app sees is a single
readiness value, AccountLifecycle:
type AccountLifecycle =
| { status: "loading" }
| { status: "settingUp"; step: AccountSetupStep }
| { status: "ready"; accountId: string; canTransact: boolean }
| { status: "failed"; at: AccountSetupStep; error: CapxulError };Setup moves through four steps — connecting, confirmingIdentity,
registering, activating — and lands on ready or failed. Read it with
useCapxulAccountLifecycle, which polls automatically while
setup is in progress and exposes a retry mutation for the failed state:
"use client";
import { useCapxulAccountLifecycle } from "@capxul/sdk-react";
function MoneyGate({ children }: { children: React.ReactNode }) {
const { lifecycle, retry, isRetrying } = useCapxulAccountLifecycle();
if (lifecycle.status === "loading" || lifecycle.status === "settingUp") {
return <p>Setting up your account…</p>;
}
if (lifecycle.status === "failed") {
return (
<div>
<p>Account setup failed: {lifecycle.error.message}</p>
<button type="button" onClick={() => retry()} disabled={isRetrying}>
Try again
</button>
</div>
);
}
if (!lifecycle.canTransact) {
return <p>Your account is almost ready…</p>;
}
return children;
}Show balance and money-movement UI only behind status === "ready" with
canTransact true. Which money hooks are live in the published alpha is
tracked on Capability status.
Signers
Activating an account involves signing, so requirement: "deployed" raises
the question of who signs. The answer for browser apps is: nobody you have
to wire up.
Browser apps: omit signer
With requirement="deployed" and no signer prop, the SDK provisions an
embedded signer for the signed-in user automatically, configured from the same
bootstrap the publishable key already resolves. This is the production path —
the reference integration ships exactly publishableKey plus
requirement="deployed" and nothing else.
<CapxulProvider publishableKey={publishableKey} requirement="deployed">
{children}
</CapxulProvider>Local development and tests: devPrivateKeySigner
For local dogfooding and test harnesses where the embedded signer is unavailable or unwanted, the SDK exports a dev-mode signer:
import { devPrivateKeySigner } from "@capxul/sdk";
<CapxulProvider
publishableKey={publishableKey}
requirement="deployed"
signer={devPrivateKeySigner({ seed: import.meta.env.VITE_CAPXUL_DEV_SIGNER_SEED })}
>
{children}
</CapxulProvider>It derives a deterministic throwaway key from the seed plus the signed-in
user's email, so signing in again with the same email reproduces the same
signer and account setup stays coherent across sessions. The email is resolved
lazily from the cached session, so it is safe to construct the signer before
anyone has signed in; tests can pass an explicit email instead.
devPrivateKeySigner is for local development and tests only — never ship it
in a production app. The seed is a dev-only secret, and the accounts it
derives are throwaways: fund them with test funds
if you must, never with real money.
That's the whole surface
publishableKey, requirement, signer, and an optional queryClient are
the entire public provider input, and createCapxulClient takes the first
three. If a knob is not listed here, it is not part of the public SDK — there
is no supported way to point the SDK at a different backend or swap its
internals from app code.
Next
- Account lane — what provisioning actually
does between
settingUpandready. - Hooks reference —
useCapxulAccountLifecycleand the account/money hooks it gates. - Capability status — which money capabilities are live in the published alpha.
Test keys & funds
Create a test publishable key and send test funds with the @capxul/sandbox CLI — and know what must never ship to production.
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.