Capxul Docs
Getting started

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:

requirementWhat the user has after setupUse 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 settingUp and ready.
  • Hooks referenceuseCapxulAccountLifecycle and the account/money hooks it gates.
  • Capability status — which money capabilities are live in the published alpha.

On this page