# Capability status (https://capxul-sdk-docs.pages.dev/docs/capability-status) This page is the honest map of the platform. Every capability below is graded against the **published** packages, not against ambition. If a page elsewhere in these docs disagrees with this one, this one wins — and the disagreement is a bug worth reporting. **Last verified:** 2026-07-08 against `@capxul/sdk@1.0.0-alpha.13` / `@capxul/sdk-react@1.0.0-alpha.13`. ## How to read the grades [#how-to-read-the-grades] * **Live** — published, exercised by a real consumer (the Capxul app or the reference app), safe to build on. * **SDK-ready** — the primitive is published and tested, but no product surface has proven it end-to-end yet. Expect rough edges; build against it with that expectation. * **Partial** — published, but with documented constraints that limit what you can ship on it today. The constraint is listed. * **Roadmap** — direction, not capability. Do not build against it; do not claim it. ## Status matrix [#status-matrix] | Capability | Grade | Hooks / API | Notes | | ------------------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Email OTP auth, session, sign-out | **Live** | `useCapxulSignIn`, `useCapxulVerifyOtp`, `useCapxulSession`, `useCapxulSignOut` | Proven in the Capxul app and reference app. | | Profile & current user | **Live** | `useCapxulProfile`, `useCapxulCurrentUser` | Profile is `null` for a fresh identity until onboarding completes. | | Account lifecycle / readiness | **Live** | `useCapxulAccountLifecycle`, `useCapxul` | The account lane runs automatically after sign-in when `requirement !== "none"`. | | Personal balance | **Live** | `useCapxulAccountBalance` | | | Organizations: list, detail, switching | **Live** | `useCapxulOrgs`, `useCapxulOrg`, `useCapxulSwitchActingEntity` | | | Org treasury, members, roles | **Live** | `useCapxulOrgTreasury`, `useCapxulOrgMembers`, `useCapxulOrgRoles` | Treasury returns the org's single treasury `Account`. | | Org audit log | **Roadmap** (typed contract published) | `useCapxulOrganizationAuditLog` | `org(orgId).auditLog()` currently returns `NOT_IMPLEMENTED` on every call. | | Onboarding completion | **Live** | `useCapxulCompletePersonalOnboarding`, `useCapxulCompleteOrganizationOnboarding` | | | Payments: pay, list, get | **Partial** | `useCapxulPay`, `useCapxulPayments`, `useCapxulPayment` | `pay` requires typed recipients (handle, email, payee, request) and personal actors today; bare addresses are rejected by design. Amounts must use `decimals: 6`. | | Cancel payment | **Roadmap** (typed contract published) | `useCapxulCancelPayment` | `payments.cancel` currently returns `NOT_IMPLEMENTED` on every call. | | Wallet withdraw | **SDK-ready** | `useCapxulWithdraw` | External wallet cash-out with a Withdrawal document. Not yet wired into a product surface. | | Payout to saved destination | **Partial** | `useCapxulPayout` | Backend settles **wallet** destinations only. Bank / mobile-money payouts are rejected. | | Destinations (bank / mobile-money / wallet) | **Partial** | `useCapxulDestinations`, `useCapxulAddDestination`, `useCapxulRemoveDestination` | Destination *metadata* can be stored for all three kinds; settlement exists only for wallet. | | Offramp quotes & status | **Roadmap** (typed contract published) | `useCapxulOfframpQuote`, `useCapxulOfframpStatus` | The hooks and types exist so integrations can be typed today, but every call currently returns `NOT_IMPLEMENTED`. No live rail yet. | | Payment requests & inbox | **SDK-ready** | `useCapxulIssueRequest`, `useCapxulInbox`, `useCapxulApproveInboxRequest`, … | Create / send / approve / decline primitives exist; a full invoice product workflow does not. | | Address book & insights | **SDK-ready** | `useCapxulAddressBook`, `useCapxulInsightsSummary`, … | | | Activity feed | **SDK-ready** | `useCapxulActivity` | Outbound payments feed. Published but not yet proven against a product screen. | | Payroll roster & runs | **SDK-ready** | `useCapxulPayrollRoster`, `useCapxulRunPayroll`, … | Primitives exist; scheduling / streaming product UX is not SDK-complete. | | Sub-accounts (envelopes) & transfers | **SDK-ready** | `useCapxulSubAccountsList`, `useCapxulTransfer`, … | | | Bank / mobile-money settlement | **Roadmap** | — | The next platform build direction. Destinations can be modeled today; cash-out cannot. | | Cards | **Roadmap** | — | Product vision. No SDK or backend surface exists. | | `@capxul/components` (headless UI) | **Roadmap** | — | Compound-component families are specified in the domain canon but not yet implemented. | ## Known gaps the platform team is tracking [#known-gaps-the-platform-team-is-tracking] These are read-model gaps the Capxul frontend team has already hit; they are tracked as SDK public-contract work. If you hit them too, you are not doing it wrong — the contract does not exist yet: * A composite **personal payments** read model (metrics, payroll cards, invoice rows). * **Multi-chain org treasury** token balances (today's treasury read is a single account). * **Org payment history** rows with export. * Personal **activity** aggregates (earnings summaries). ## The instant-send guardrail [#the-instant-send-guardrail] One rule explains many "why was this rejected?" moments: an instant, irreversible payment is only permitted to a **validated** recipient. Paying anyone not yet validated always goes through a recoverable **Commitment** (escrowed, cancellable, redirectable). The SDK enforces this; it is not a bug to work around. See [Money & accounts](https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts). # What is Capxul? (https://capxul-sdk-docs.pages.dev/docs) Capxul is a financial-operations platform. The **Capxul SDK** gives your app accounts, payments, payroll, payment requests, and organizations through a TypeScript client and a set of React hooks — without your code ever touching the crypto substrate underneath. ```tsx import { CapxulProvider, useCapxulAccountBalance } from "@capxul/sdk-react"; function Balance() { const balance = useCapxulAccountBalance(); if (balance.isLoading) return Loading…; return {balance.data?.balance.value} {balance.data?.balance.currency}; } ``` ## The surface is Stripe-shaped [#the-surface-is-stripe-shaped] Money is just money. A developer using the SDK never has to know the words "Safe", "UserOp", "wei", or a chain id to move money. Crypto is the substrate, never the vocabulary — any custody term appearing in the consumer-facing surface is treated as a defect (a **leak**). You work with: * **Money** — a currency, a decimal value, and a precision. Never wei. * **Accounts** — a person's or org's pot of money, identified by an opaque id. Never an address. * **Payments** — money sent to a validated recipient, a handle, an email, or a saved payee. Never a raw chain address. Read the [mental model](https://capxul-sdk-docs.pages.dev/docs/concepts/mental-model) and the [money & accounts glossary](https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts) for the full vocabulary. ## Two products, one platform [#two-products-one-platform] * **Capxul App** is the customer-facing financial-operations product. * **Capxul SDK** (these docs) is the platform layer it is built on — the same primitives are published for other product teams, external developers, and agents to build with. ## Agent-native by design [#agent-native-by-design] The SDK is built with the expectation that agents participate in both building and operating products. Every capability is a safe, structured primitive an agent can compose: the [MCP server](https://capxul-sdk-docs.pages.dev/docs/agents/mcp) exposes financial operations as tools, every docs page is [available as raw markdown](https://capxul-sdk-docs.pages.dev/docs/agents/machine-readable-docs), and [llms.txt](/llms.txt) indexes the whole site for machine readers. ## Packages [#packages] | Package | What it is | | --------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `@capxul/sdk` | The core TypeScript client — `createCapxulClient` and method bundles for auth, accounts, payments, orgs, payroll, and more. | | `@capxul/sdk-react` | React bindings — `CapxulProvider` plus \~60 hooks built on TanStack Query. | | `@capxul/sdk-testing` | Test utilities for SDK adapters and proof harnesses. | | `@capxul/mcp` | The Capxul MCP server — financial operations as agent tools. | | `@capxul/sandbox` | Sandbox CLI — create test publishable keys and send testnet faucet funds. | All packages publish to npm on the `1.0.0-alpha.x` train. Pin exact versions; see [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) for what is live today. ## Where to start [#where-to-start] * **Building a product UI?** Start with [Getting started](https://capxul-sdk-docs.pages.dev/docs/getting-started), then the [hooks reference](https://capxul-sdk-docs.pages.dev/docs/hooks). * **Wondering what's real today?** Read [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). * **Contributing to the platform?** Start at [Contributing](https://capxul-sdk-docs.pages.dev/docs/contributors/dev-onboarding). * **Building with agents?** See the [agent surface](https://capxul-sdk-docs.pages.dev/docs/agents/mcp). # Machine-readable docs (https://capxul-sdk-docs.pages.dev/docs/agents/machine-readable-docs) This site is built to be read by agents as much as by people. Every page has a raw-markdown form, and the whole site is indexed for machine readers. No scraping, no HTML parsing. ## Site-wide indexes [#site-wide-indexes] | Route | What it returns | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | [`/llms.txt`](/llms.txt) | An index of every docs page — titles and absolute URLs. Start here to discover what exists. | | [`/llms-full.txt`](/llms-full.txt) | The full content of every page, concatenated into one document. Use it to load the entire docs corpus in one fetch. | ## Per-page markdown [#per-page-markdown] Every docs page is also published as a static markdown file: take the page path and fetch it under `/llms.mdx/` with `/content.md` appended — `/docs/agents/mcp` becomes: ```bash curl https:///llms.mdx/docs/agents/mcp/content.md ``` The response is the processed page content, prefixed with the page title and its canonical URL — so a fetched page always identifies itself. (The site is statically hosted, so there is no `Accept`-header content negotiation; the markdown files are plain static assets.) ## Copy Markdown button [#copy-markdown-button] Every docs page has a **Copy Markdown** button at the top, next to a view options popover that links the same raw-markdown URL and the page's source on GitHub. Useful when a human is pasting a page into an agent's context by hand — and the easiest way to discover a page's markdown URL. ## Suggested reading order for agents [#suggested-reading-order-for-agents] 1. Fetch [`/llms.txt`](/llms.txt) to see the page index. 2. Fetch the specific pages you need via their `content.md` URLs — or [`/llms-full.txt`](/llms-full.txt) if you want everything. 3. Check [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) before acting on any capability claim — it is the honest map of what is live in the published alpha. # MCP server (https://capxul-sdk-docs.pages.dev/docs/agents/mcp) `@capxul/mcp` is a runnable [Model Context Protocol](https://modelcontextprotocol.io) server that exposes Capxul's financial operations as tools any MCP client (Claude, Cursor, other agents) can call. The package ships a CLI, not a library: the published artifact is the `capxul-mcp` executable. Like the SDK, the tool surface speaks money — tool results carry Money values, labels, and opaque ids, never custody internals. The MCP surface ships on the `1.0.0-alpha.x` train and is still moving. Tool names, inputs, and gating may change between alpha releases. Pin an exact version, and treat the startup banner — which prints the exact tool set the running server registered — as the authority for your launch. ## Install and run [#install-and-run] ```bash # one-off, via npx npx @capxul/mcp serve --mode live # or install globally npm i -g @capxul/mcp capxul-mcp serve --mode live ``` ``` Usage: capxul-mcp serve [--mode live|dev] [--port ] [--approval-url ] [--stdio] Options: --mode live|dev Launch mode. Defaults to live. There is no env-var mode selector. --port Bind port. Defaults to CAPXUL_MCP_PORT or 8788. (HTTP transport only.) --approval-url Approval app base URL. Defaults to CAPXUL_MCP_APPROVAL_URL or http://localhost:5998. --stdio Serve over stdio (local spawn) instead of HTTP. Defaults to off (HTTP). --help Show this help. ``` The server speaks MCP over **Streamable HTTP** at `POST /mcp` — a fresh, stateless server instance per request, with shared state (sessions, approvals) held in the process. Pass `--stdio` to serve over stdio instead, for clients that spawn the server locally. On startup the banner prints the bound URL, the mode, whether the faucet is enabled, and the full list of registered tools. That list is computed from the live registry at boot, so it never drifts from what the server actually exposes. ## Auth model [#auth-model] There are two independent layers: **Transport auth — OAuth bearer.** An in-memory PKCE OAuth provider gates `POST /mcp` when `CAPXUL_MCP_OAUTH_REQUIRED=1`. The server publishes the standard discovery documents (`/.well-known/oauth-protected-resource`, `/.well-known/oauth-authorization-server`) and the `/oauth/authorize` and `/oauth/token` endpoints. A proof-issuer secret (`CAPXUL_MCP_OAUTH_PROOF_ISSUER_SECRET`) can mint bearer tokens for automated probes — and is currently required whenever OAuth is required, because external-issuer validation is not implemented yet. Stdio mode skips OAuth entirely: a local spawn is trusted IO with no bearer-token surface. **Financial auth — email OTP sessions.** Signing in to Capxul happens through tools: `auth.requestOtp` emails a one-time code, `auth.verifyOtp` exchanges it for an opaque `sessionToken` that every session-scoped tool takes as an argument. The OTP travels only as a tool argument, and no other credential — no backend JWT, no key material — ever appears in a tool result. ### The dev-mode custody gate [#the-dev-mode-custody-gate] The `--mode` flag decides how money-moving operations get signed: * **`--mode live`** (default) uses the production signer path. Full account activation needs a one-time human approval in the browser: `account.ensureReady` with requirement `deployed` returns `{ status: "pending", approvalUrl, approvalId }` — open `approvalUrl` (the dedicated Approval app), complete the approval there, then poll `account.deploymentStatus` with the `approvalId`. The server itself never holds key material. * **`--mode dev`** runs against a deterministic dev signer seed (`CAPXUL_MCP_DEV_SIGNER_SEED`) and a dev backend deployment, so agent journeys can be exercised headlessly, without live custody. Dev mode is also the gate for the payment tools — see [tool gating](#which-tools-are-registered) below. ## Runtime configuration [#runtime-configuration] Environment is read lazily on the first authenticated tool call, so liveness (`system.ping`) needs no secrets. A missing variable surfaces as a structured `ENV_MISSING` tool error naming the exact variable. Notable variables: | Variable | Purpose | | -------------------------------------- | ------------------------------------------------------ | | `CAPXUL_MCP_PORT` | Bind port (default `8788`; `0` = ephemeral). | | `CAPXUL_FAUCET_ENABLED` | `1` or `true` to register the testnet faucet tool. | | `CAPXUL_MCP_DEV_SIGNER_SEED` | Deterministic dev signer seed (`--mode dev`). | | `CAPXUL_MCP_OAUTH_REQUIRED` | `1` to require an OAuth bearer token on `POST /mcp`. | | `CAPXUL_MCP_OAUTH_PROOF_ISSUER_SECRET` | Secret to mint proof bearer tokens. | | `CAPXUL_MCP_APPROVAL_URL` | Base URL of the Approval app for activation approvals. | ## Which tools are registered [#which-tools-are-registered] Three tiers, decided at launch: 1. **Always registered** — system, auth, account, identity, and payee tools. 2. **Faucet** — `faucet.fundTestnet` registers only when `CAPXUL_FAUCET_ENABLED` is set. Otherwise it is absent from `tools/list`. 3. **Payments and the wider financial-ops set** — payments, payment documents, orgs, payment requests, the draft workbench, and the account/org projection tools register only when the payments gate is on: `--mode dev` **and** the faucet enabled. In `--mode live` these tools are absent today. A typical first session, in order: `system.ping` → `auth.requestOtp` → `auth.verifyOtp` (returns `sessionToken`) → `account.ensureReady` → `account.balance`. ## Tool reference [#tool-reference] Inputs marked `?` are optional. `sessionToken` is always the opaque token returned by `auth.verifyOtp`. ### System and auth [#system-and-auth] | Tool | Input | What it does | | ----------------- | --------------- | ------------------------------------------------------------------------------- | | `system.ping` | `echo?` | Liveness probe; echoes back the optional value. Needs no auth and no secrets. | | `auth.requestOtp` | `email` | Email a one-time sign-in code. Works for new and returning users alike. | | `auth.verifyOtp` | `email`, `otp` | Verify the emailed code and start a session. Returns the opaque `sessionToken`. | | `auth.status` | `sessionToken?` | Report whether the session is authenticated, and for whom. | | `auth.signOut` | `sessionToken` | End the session and revoke the token. | ### Account readiness and balance [#account-readiness-and-balance] | Tool | Input | What it does | | -------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | `account.status` | `sessionToken` | Readiness ladder: `loading`, `settingUp` (with the current step), `ready` (with `accountId` and `canTransact`), or `failed`. | | `account.ensureReady` | `sessionToken`, `requirement?` (`provisioned` or `deployed`) | Run account setup to completion; idempotent. With `deployed`, may return `pending` plus an `approvalUrl` for the one-time browser approval. | | `account.deploymentStatus` | `approvalId` | Poll the activation approval started by `account.ensureReady`. `pending` until it completes, then `ready` with `canTransact`. | | `account.balance` | `sessionToken` | The account's balance and available balance, as Money. | ### Identity and payees [#identity-and-payees] | Tool | Input | What it does | | ------------------------ | ----------------------------------------------- | ------------------------------------------------------------------------------- | | `me.get` | `sessionToken` | The authenticated identity: `authUserId`, `email`, `displayName`. | | `me.depositInstructions` | `sessionToken` | Instructions for depositing money into the account. | | `handles.resolve` | `sessionToken`, `handle` | Resolve a Capxul handle or org handle to its current payment destination. | | `payees.create` | `sessionToken`, `label`, `recipient`, `handle?` | Save a recipient as a Payee. The recipient must resolve by email or org handle. | | `payees.get` | `sessionToken`, `payeeId` | Load a saved Payee by id. | | `payees.resolve` | `sessionToken`, `recipient` | Resolve an email, handle, org handle, or Payee id to a payment destination. | ### Faucet [#faucet] Registered only when `CAPXUL_FAUCET_ENABLED` is set. Test environments only — the same guardrails as the [Sandbox CLI](https://capxul-sdk-docs.pages.dev/docs/agents/sandbox) apply: never wire the faucet into production UX. | Tool | Input | What it does | | -------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `faucet.fundTestnet` | `sessionToken`, `amount { value, currency?, decimals? }` | Mint test money into the session's account (testnet only). `value` is a decimal string, e.g. `"10"` for $10. Returns `{ status: "funded" }` — no settlement internals. | ### Payments [#payments] Registered only when the payments gate is on (`--mode dev` + faucet enabled). Money-moving tools require the account to be `ready` with `canTransact: true`, or they fail with a wrong-state error. Amounts are Money strings such as `"2 USDX"`. Recipients (`to`) are always validated — a handle, email, org handle, or Payee id, never a raw address — except `payments.withdraw`, whose entire purpose is an external destination. | Tool | Input | What it does | | ------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `payments.pay` | `sessionToken`, `to`, `amount`, `document?`, `release?`, `lineItems?` | Pay a validated recipient. An optional `release` clause (`scheduled` or `stream`) turns the payment into a recoverable Commitment. Returns a Payment record. | | `payments.withdraw` | `sessionToken`, `to`, `amount`, `document` | Cash out to an external wallet. Requires a Withdrawal payment document; the result redacts the destination to a short label. | | `payments.list` | `sessionToken` | List your payments, most recent first, each with its live vesting read-model (`released`, `availableToClaim`). | | `payments.get` | `sessionToken`, `paymentId` | One payment by id with its vesting read-model. | | `payments.claim` | `sessionToken`, `paymentId` | Claim the vested-and-unclaimed amount of a Commitment you are the recipient of. | | `payments.cancel` | `sessionToken`, `paymentId`, `document?` | Cancel a Commitment you created. Reclaims only the unvested remainder. | | `payments.redirect` | `sessionToken`, `paymentId`, `to`, `document?` | Redirect a Commitment to a new validated recipient before any claim. | | `paymentDocuments.verify` | `sessionToken`, `documentHash` | Re-derive a stored payment document's hash and confirm it matches the committed `documentHash`. | | `paymentDocuments.render` | `sessionToken`, `documentHash` | Render a stored document to HTML for the **user**; the model receives metadata only. | ### The wider dev-mode surface [#the-wider-dev-mode-surface] The same payments gate also registers these tool families. They mirror the SDK's org, receivables, and projection surfaces; expect them to evolve the fastest of anything on this page. | Family | Tools | What it covers | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | Draft workbench | `payments.draft`, `payments.simulate`, `approvals.request`, `approvals.status`, `approvals.cancel`, `payments.execute` | The decomposed payment flow: the agent drafts and simulates, a human mints a one-time approval in the browser, execution consumes it. | | Organizations | `org.create`, `org.list`, `org.members`, `org.roles`, `org.invite`, `org.assignRole`, `org.removeMember`, `org.spendViaPayments`, `org.batchPayroll` | Create and administer orgs, and spend from an org treasury under role caps. | | Payment requests | `paymentRequests.create`, `.markSent`, `.markViewed`, `.cancel`, `.collect`, `.list`, `.get`, `.reconcile` | The receivables lifecycle: request, share a link, collect, reconcile. | | Address book | `account.addressBook.*` and `org.addressBook.*` (`list`, `get`, `add`, `hide`, `unhide`, `label`) | Relationship entries per actor scope, refs and labels only. | | Requests and inbox | `account.requests.*`, `org.requests.*` (`issue`, `list`, `get`, `cancel`, `reconcile`); `account.inbox.*`, `org.inbox.*` (`list`, `approve`, `decline`) | Issue requests to payers; list, approve, or decline payable items. | | Insights | `account.insights.summary`, `account.insights.history`, and org equivalents | Read-only finance summary and payment-history projections. | | Destinations and payouts | `destinations.add`, `destinations.list`, `destinations.remove`, `payments.payout`, `subAccounts.transfer` | Saved payout destinations, payouts to them, and transfers between sub-accounts. | | Payroll | `org.payroll.roster.add`, `.list`, `.update`, `.remove`, `org.payroll.run` | Roster management and payroll runs. | The capability constraints on the equivalent SDK surfaces apply here too — check [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) before building a product flow on any of them. ## What never crosses the wire [#what-never-crosses-the-wire] Tool results are leak-safe by design: they carry Money values, validated recipient labels, opaque ids, and committed document hashes. Settlement internals — transaction hashes, account addresses, raw minor units, approval token values — stay server-side. If you find one in a tool result, that is a defect worth reporting, not a feature to depend on. # Sandbox CLI (https://capxul-sdk-docs.pages.dev/docs/agents/sandbox) `@capxul/sandbox` is the CLI for standing up a Capxul **test** environment. It has exactly two commands: `key create` mints a test publishable key, and `faucet send` mints test money. Both run against Capxul's already-deployed canonical quickstart backend — the CLI deploys nothing and pushes no code. ```bash npx @capxul/sandbox # interactive picker npx @capxul/sandbox key create npx @capxul/sandbox faucet send npx @capxul/sandbox --help ``` Run with no arguments to pick a command interactively. A global install exposes the `capxul-sandbox` (and `sandbox`) executables. Everything this CLI produces is test-grade: test keys, test money, testnet settlement. Never wire the faucet into production UX or a customer-facing flow, and never treat faucet funds as balance a product can rely on. ## key create [#key-create] Creates a developer application and a **test** publishable key on the canonical quickstart deployment, then writes the customer values to a local handoff file. ```bash npx @capxul/sandbox key create ``` The CLI prompts for: * an **application name**, * the **allowed local origin** — usually `http://localhost:3000`; the key only accepts requests from origins it allows, * a **handoff file path** — default `~/.config/capxul/quickstart-handoffs/customer-nextjs-quickstart.md`. After a confirmation it mints the key and reports the handoff path, the application id, the key id, and the allowed origin. Two properties matter for how you script around it: * **The raw key is never printed.** The terminal shows ids and metadata only; the publishable key itself lands in the handoff file. Copy it from there into your app's `.env.local` (see the [getting started guide](https://capxul-sdk-docs.pages.dev/docs/getting-started)). * **It is not a deploy.** The command calls the already-deployed canonical backend; your app and its config are untouched. ## faucet send [#faucet-send] Mints test USDX to an address on Base Sepolia, via the canonical backend's dev faucet. It cannot select a live or mainnet target. ```bash npx @capxul/sandbox faucet send ``` The CLI prompts for: * a **recipient EVM address** — the current release sends funds directly to an address, * a **USDX amount** — a decimal string, default `10` (test USDX, 6 decimals). After a confirmation it mints the funds and reports the recipient, amount, and transaction hash. The address prompt is a temporary developer-tool detail, not a pattern to copy: a future release is planned to be account-based (pick or create a Capxul account, then fund it). Do not design customer onboarding around collecting wallet addresses. If your agent is already signed in through the [MCP server](https://capxul-sdk-docs.pages.dev/docs/agents/mcp), prefer its `faucet.fundTestnet` tool instead — it funds the session's account directly, no address required. ## When you need it [#when-you-need-it] * **Proving email OTP login** needs only `key create` — no test money at all. * **Account and money flows** (balances, payments) additionally need the faucet, and only in test environments. For the full local setup walkthrough, start at [Getting started](https://capxul-sdk-docs.pages.dev/docs/getting-started). # CapxulClient (https://capxul-sdk-docs.pages.dev/docs/client) `@capxul/sdk` exposes one factory: `createCapxulClient`. It resolves the platform bootstrap for your publishable key and returns a **`CapxulClient`** — imperative method bundles covering auth, accounts, payments, receivables, organizations, payroll, and onboarding. Every [React hook](https://capxul-sdk-docs.pages.dev/docs/hooks) wraps one of these methods; in a React app, [`CapxulProvider`](https://capxul-sdk-docs.pages.dev/docs/hooks/capxul-provider) creates and owns the client for you. Use the client directly when there is no React tree — a script, a server, an agent backend. See [Node & servers](https://capxul-sdk-docs.pages.dev/docs/client/node-and-servers). ## Create a client [#create-a-client] ```ts import { createCapxulClient } from "@capxul/sdk"; const result = await createCapxulClient({ publishableKey: process.env.CAPXUL_PUBLISHABLE_KEY!, requirement: "counterfactual", }); if (!result.ok) { throw result.error; } const client = result.value; await client.auth.signIn({ email: "user@example.com" }); ``` `createCapxulClient` returns `Promise>` — a failed bootstrap comes back as an `error` value, not an exception. ### Parameters [#parameters] The factory accepts a single `CapxulClientInput` object with exactly three fields. The SDK resolves everything else — the bootstrap host, the runtime, request timeouts, and session caching — internally. #### publishableKey [#publishablekey] `string` — required. The key that identifies your app to the platform. Create sandbox keys with the [`@capxul/sandbox` CLI](https://capxul-sdk-docs.pages.dev/docs/getting-started). #### requirement [#requirement] `"none" | "counterfactual" | "deployed"` — optional, defaults to `"none"`. The account-readiness target the SDK works toward after sign-in. With `"none"` no account setup runs at all; the other two values start the account lane automatically once a user signs in. Full semantics are on [Node & servers](https://capxul-sdk-docs.pages.dev/docs/client/node-and-servers#requirement-semantics). #### signer [#signer] `CapxulSigner` — optional. The consumer-held signing key for `requirement: "deployed"`. Required outside the browser; browser apps omit it and the SDK provisions one automatically. These three fields are the entire public input. Overrides such as `origin`, `bootstrapBaseUrl`, `runtime`, `fetch`, `invokeTimeoutMs`, `authCache`, and `telemetry` are internal production and test seams — they are not consumer API and are not accepted here. ## Results, not exceptions [#results-not-exceptions] Every method on every bundle returns `Promise>`: ```ts import type { CapxulResult } from "@capxul/sdk"; type CapxulResult = | { readonly ok: true; readonly value: T } | { readonly ok: false; readonly error: CapxulError }; ``` Check `ok` to narrow the result; on failure, `error` is always a structured `CapxulError`. The React hooks unwrap this shape into TanStack Query `data` / `error` fields for you — explicit narrowing is only needed when you call the client directly: ```ts const account = await client.accounts.read(); if (!account.ok) { console.error(account.error.code, account.error.message); return; } console.log(`${account.value.balance.value} ${account.value.balance.currency}`); ``` See [Error handling](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) for the error catalog and recovery patterns. ## Method bundles [#method-bundles] The client groups its methods into domain bundles. Each row links the hook family that wraps it. Bundles marked *client-only* have no hooks yet — in a React app, reach them through [`useCapxulClientOrNull`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul-client-or-null). | Bundle | What it does | React hooks | | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `client.auth` | Email OTP auth: `canSendOtp`, `signIn`, `verifyOtp`, `getSession`, `signOut`. | [Auth](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-in) | | `client.account` | Post-sign-in setup lifecycle (`getLifecycle`, `retrySetup`) plus the personal [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) — address book, requests, inbox, insights. | [Account](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle) | | `client.accounts` | Read the personal **Account** — `balance` and `available`, both `Money`. | [Account](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle) | | `client.currentUser`, `client.me` | Who is signed in: profile, payment link, and org memberships (`currentUser.get`); profile plus deposit instructions (`me`). | [Auth](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-in) | | `client.identity` | Load the signed-in user's stored profile. | [Auth](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-in) | | `client.handles` | Resolve a handle to a payable recipient. | Client-only — `pay` accepts handles directly ([Payments](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-pay)) | | `client.payees` | Create, fetch, and resolve saved payees. | Client-only | | `client.targets` | Resolve any target reference — handle, email, org, payee, or destination — with its pay / request / payout capabilities. | Client-only | | `client.destinations` | Save, list, and remove external payout destinations. | [Destinations](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-destinations) | | `client.payments` | Move money: `pay`, `payout`, `withdraw`, plus `list`, `get`, and `cancel`. | [Payments](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-pay) | | `client.activity` | The paginated activity feed — payments, payouts, withdrawals, deposits, claims. | [Activity](https://capxul-sdk-docs.pages.dev/docs/hooks/activity/use-capxul-activity) | | `client.offramp` | Quote and track a cash-out to a saved destination. | [Offramp](https://capxul-sdk-docs.pages.dev/docs/hooks/offramp/use-capxul-offramp-quote) | | `client.paymentDocuments` | Verify a payment document's integrity and render it to HTML by document hash. | Client-only | | `client.paymentRequests` | Receivables: fixed-amount payment links with a draft → sent → viewed → paid lifecycle, plus `collect` and `reconcile`. | Client-only — the [Requests](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests) hooks cover the actor-scoped request surface, a different bundle | | `client.workbench` | The agent payment workbench: `draft` → `simulate` → `requestApproval` → `execute`, with a human approval step in between. | Built for agents — see [MCP](https://capxul-sdk-docs.pages.dev/docs/agents/mcp) | | `client.subAccounts` | Envelopes under an Account: `create`, `list`, `rename`, `delete`, `transfer`. | [Sub-accounts](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-accounts-list) | | `client.createOrg`, `client.orgs`, `client.org(orgId)` | Create organizations, list memberships, and resolve the entity-scoped bundle — treasury, members, roles, invites, spend, payroll. | [Organizations](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs), [Payroll](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-payroll-roster) | | `client.onboarding` | Complete personal or organization onboarding after the first sign-in: `completePersonal`, `completeOrganization`. | [Onboarding](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-personal-onboarding) | Org scoping is explicit: `client.org(orgId)` resolves a bundle for that one organization on every call — there is no stateful "active org" on the client. Not every bundle is equally proven in a product surface yet. Check [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) before you build on one. ## Off the surface [#off-the-surface] The client also carries a `_internal` namespace — an observation and testing seam. Nothing under `_internal` is consumer API; do not build on it. Likewise, factory inputs beyond the three documented above (self-hosted origins, fetch and runtime overrides, timeout and cache tuning) live on internal entrypoints for hermetic tests and platform tooling — never on `createCapxulClient`. # Node & servers (https://capxul-sdk-docs.pages.dev/docs/client/node-and-servers) The React hooks are one consumer of the core client. Anywhere without a React tree — a Node script, a server route, a CLI, an agent backend — you create the client yourself with [`createCapxulClient`](https://capxul-sdk-docs.pages.dev/docs/client) and call its method bundles directly. ## Create the client in a script [#create-the-client-in-a-script] ```ts import { createCapxulClient } from "@capxul/sdk"; const result = await createCapxulClient({ publishableKey: process.env.CAPXUL_PUBLISHABLE_KEY!, requirement: "counterfactual", }); if (!result.ok) { throw result.error; } const client = result.value; // Email OTP works the same as in the browser: request a code, then verify it. await client.auth.signIn({ email: "ops@example.com" }); const otpCode = process.argv[2]!; // the 6-digit code from the sign-in email const session = await client.auth.verifyOtp({ email: "ops@example.com", code: otpCode }); if (!session.ok) { throw session.error; } const account = await client.accounts.read(); if (account.ok) { console.log(`${account.value.balance.value} ${account.value.balance.currency}`); } ``` Two things differ from the browser: * **No exceptions for domain failures.** Every method returns `Promise>` — narrow on `ok` instead of wrapping calls in `try`/`catch`. See [the result contract](https://capxul-sdk-docs.pages.dev/docs/client#results-not-exceptions). * **Sessions live in memory.** In Node, the default session cache lasts for the lifetime of the process. Sign in at startup; a new process starts signed out. ## Requirement semantics [#requirement-semantics] `requirement` declares, at creation time, how ready the signed-in user's **Account** must become. The client works toward that target automatically after sign-in — no per-call readiness arguments anywhere else. ### `"none"` (default) [#none-default] No account setup runs. Use it for tooling that only needs auth or profile reads and never touches money. ### `"counterfactual"` [#counterfactual] After sign-in, the account lane prepares the user's Account so it can receive money immediately; full activation is deferred. This is the usual choice for scripts and servers that read balances or move money. ### `"deployed"` [#deployed] The Account is fully activated during setup. Outside the browser this requires a `signer` — `createCapxulClient` rejects `requirement: "deployed"` without one. Build a Node signer from a private key: ```ts import { createCapxulClient } from "@capxul/sdk"; import { localPrivateKeySigner } from "@capxul/sdk/node"; const result = await createCapxulClient({ publishableKey: process.env.CAPXUL_PUBLISHABLE_KEY!, requirement: "deployed", signer: localPrivateKeySigner({ privateKey: process.env.SIGNER_PRIVATE_KEY! as `0x${string}` }), }); ``` Browser apps omit `signer` for `"deployed"` — the SDK provisions one automatically. Track setup progress with `client.account.getLifecycle()` (the same lifecycle [`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle) polls in React). ## Server-bootstrapped React: inject the client [#server-bootstrapped-react-inject-the-client] `CapxulProvider` accepts **exactly one** of `publishableKey` or `client`. When your server (or test harness) bootstraps the client before React renders, pass the pre-built client instead of a key: ```tsx import { CapxulProvider } from "@capxul/sdk-react"; import type { CapxulClient } from "@capxul/sdk"; export function App({ client }: { client: CapxulClient }) { return ( ); } ``` Injected-client mode changes the provider contract: * **Ready immediately.** There is no bootstrapping phase; [`useCapxul()`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul) reports `ready` from the first render, and data hooks run right away. * **Lifecycle stays with you.** The provider never closes an injected client — you created it, you own it. * **No `requirement` or `signer` props.** Those are inputs to `createCapxulClient`; you already made those choices when you built the client. Passing them alongside `client` is a type error. * **Swapping the `client` prop resets the cache.** The provider clears Capxul-scoped queries when the client identity changes, so data from one client never bleeds into another. Passing both `publishableKey` and `client` — or neither — throws at render. ## Building for agents [#building-for-agents] An agent backend is just a Node consumer: create the client, call the bundles. For agent-driven payments specifically, use the `client.workbench` flow — `draft` → `simulate` → `requestApproval` → `execute` — which keeps a human in the approval loop, or skip the client entirely and connect to the [Capxul MCP server](https://capxul-sdk-docs.pages.dev/docs/agents/mcp), which exposes these operations as tools. # The account lane (https://capxul-sdk-docs.pages.dev/docs/concepts/account-lane) Signing in answers "who is this?". It does not answer "can this identity hold and move money yet?". Between the two sits real setup work: account infrastructure has to be connected, an identity record confirmed, an Account reserved, and — when the app needs to transact — the Account activated. Capxul's answer to that gap is the **account lane**: the SDK runs the whole ladder itself, automatically, after sign-in. Apps never chain setup calls; they render a single readiness value. ## The ladder [#the-ladder] When the lane runs, it climbs a short ladder of steps, each one idempotent — already-satisfied rungs are skipped: 1. **Account infrastructure** — connect the account infrastructure for the signed-in user. 2. **Identity** — ensure the Capxul identity record exists for the session. 3. **Provision** — reserve the Account itself. The Account's identity is derived deterministically from the verified email alone, so at this point it already exists as a stable identity and deposit target — before it can transact. 4. **Activate** — make the Account transaction-capable. This rung only runs when the app asked for it (see below) and the client holds a signer that can complete activation. …and then **ready**. A failure at any rung parks the lane in a failed state that names the step and carries a `CapxulError`; nothing is left half-driven, and retrying resumes from where it stopped rather than starting over. ## `requirement` decides how far the lane runs [#requirement-decides-how-far-the-lane-runs] The `requirement` option you pass at client creation is the account target — it tells the lane where "ready" is for your app: | Requirement | The lane runs | Use when | | ------------------ | ----------------------- | ------------------------------------------------------------------------------------ | | `"none"` | Not at all. | The app only needs auth, session, and profile. | | `"counterfactual"` | Through **provision**. | The app needs a stable account identity or deposit target, but not transactions yet. | | `"deployed"` | Through **activation**. | The app must show or use a transaction-capable money surface. | This is why `requirement` is chosen once, at bootstrap, rather than per call: it is not a request parameter, it is a statement about what your product's screens assume. A dashboard that renders balances can live on `"counterfactual"`; a screen with a Pay button needs `"deployed"`. One nuance for `"deployed"`: activation needs a signer. A client with a `"deployed"` requirement but no signer runs the lane to the provisioned (counterfactual) state and parks there — a dedicated approval surface owns the activation instead. The lifecycle's `canTransact` flag tells you which side of that line you are on. ## The lane starts itself [#the-lane-starts-itself] There is no `startProvisioning()` call in app code. After a successful `verifyOtp`, whenever a session exists, the requirement is not `"none"`, and the target is not yet met, the lane kicks off automatically. Reading the lifecycle — via `client.account.getLifecycle()` or the React hook — also idempotently starts or resumes it. The lane runs at most once at a time; reads never trigger duplicate climbs. ## Apps read one value: `AccountLifecycle` [#apps-read-one-value-accountlifecycle] The public contract is a single union: ```ts type AccountLifecycle = | { status: "loading" } | { status: "settingUp"; step: AccountSetupStep } | { status: "ready"; accountId: string; canTransact: boolean } | { status: "failed"; at: AccountSetupStep; error: CapxulError }; ``` The `settingUp` steps are deliberately plain UI words — `"connecting"`, `"confirmingIdentity"`, `"registering"`, `"activating"` — mapped from the internal ladder. They are labels to render, not instructions to call anything. (`"confirmingIdentity"` stays in the vocabulary for contract stability even though the current ladder no longer produces it.) In React, `useCapxulAccountLifecycle()` is the whole integration: it polls every couple of seconds while the lane is loading or setting up, stops when it settles, and exposes a `retry` for the failed state: ```tsx const { lifecycle, retry } = useCapxulAccountLifecycle(); switch (lifecycle.status) { case "loading": return ; case "settingUp": return ; case "failed": return retry()} />; case "ready": return ; } ``` ## Why apps never chain provisioning calls [#why-apps-never-chain-provisioning-calls] It would be possible to expose the rungs as public methods and let apps drive them. The SDK refuses, for reasons that have already paid for themselves: * **The ladder changes; the contract doesn't.** The lane has already been restructured — steps have been merged away as the platform simplified — without breaking a single app, because apps only ever rendered `AccountLifecycle`. * **Choreography is a correctness problem.** Ordering, idempotency, resume-after-failure, and not-running-twice are easy to get subtly wrong in app code and are handled once, centrally, in the lane. * **It keeps the surface money-shaped.** The rungs touch infrastructure that the consumer vocabulary deliberately does not name. A public step-by-step API would drag that vocabulary into product code. The trade-off is control: you cannot reorder rungs, insert your own steps, or intercept the ladder mid-climb. If a product genuinely needs to react to setup progress, the granularity you get is the `step` on `settingUp` — which has so far been exactly enough to build setup screens. ## The bigger picture [#the-bigger-picture] Think of the platform as having two gates between a visitor and money: authentication ("who is this?") and the account lane ("is their money surface ready?"). They are separate states read from separate hooks, and the second one is a process, not a flag — which is why it has steps, failure, and retry. Once `lifecycle.status` is `"ready"`, everything in [money & accounts](https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts) is on the table. # Agent-native (https://capxul-sdk-docs.pages.dev/docs/concepts/agent-native) Capxul is built on an assumption about where software is going: over time, more application-building and more workflow execution is handled by agents. A financial platform has to decide what that means for it. Capxul's answer: > Agent-native means an agent can directly do what a user or a developer can > do — in a simple, controlled way — through SDKs, tools, and delegated > permissions. Not a chatbot bolted onto a product. Not one blessed "agent UX". The platform exposes safe, structured primitives, and different products, agents, and interfaces compose them. ## The same properties serve humans and agents [#the-same-properties-serve-humans-and-agents] Nothing about the SDK was forked to make it agent-usable — the design choices that make it safe for a developer are the ones that make it legible to an agent: * **Method-based intent.** `client.payments.pay(...)`, `client.org(orgId).invite(...)` — an agent expresses what should happen in domain verbs, not by assembling low-level operations. * **An explicit result boundary.** Every method returns `Promise>` — `ok` or a typed `CapxulError`, never an unknown thrown value. An agent can branch on failure as reliably as a program. * **Typed recipients, opaque ids.** There is no API shape where an agent can fat-finger a raw address; recipients are typed references and accounts are opaque ids. * **Guardrails in the platform, not the prompt.** The [instant-send guardrail](https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts#recipients-validation-and-the-instant-send-guardrail) means an agent *cannot* send irreversibly to an unvalidated recipient — the worst case of a confused agent is a recoverable, cancellable Commitment. Safety rules enforced below the tool layer do not depend on the agent behaving. This is the trade-off agent-native implies: controlled primitives are narrower than raw access, for agents exactly as for developers. That narrowness is the feature. ## Three levels of agent participation [#three-levels-of-agent-participation] Agent-native applies at more than one altitude: 1. **Agents building.** A developer hands the SDK and these docs to a coding agent and has it build Capxul-powered features. The docs are written to be sufficient grounding for that (see below), and the published packages are the same ones humans use. 2. **Users delegating.** A Capxul user grants an agent specific, scoped permission to act — pay people, run payroll, send payment requests, pull up payment state — without handing over their identity wholesale. Today the concrete shape of this is the MCP server's session model; richer delegation is platform direction, not yet a published contract (see [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status)). 3. **Agent apps embedding.** An agent application embeds Capxul's financial primitives inside its own product — organizations, payments, payroll as capabilities of the agent's product, the way apps embed auth or payments widgets today. ## The MCP server [#the-mcp-server] For agents that speak tools rather than TypeScript, `@capxul/mcp` is a runnable [Model Context Protocol](https://modelcontextprotocol.io) server — `npx @capxul/mcp serve` — exposing financial operations as MCP tools over Streamable HTTP. An agent authenticates a user through an email OTP tool pair and receives an opaque session token bound to that MCP session; from there, tools cover account readiness, Money balances, and sending a sponsored payment, with a liveness probe that needs no secrets and OAuth bearer gating for deployments that require it. The MCP tool set is deliberately narrower than the SDK today — a starting surface, not a mirror of every client method. Details and setup live in the [agent surface docs](https://capxul-sdk-docs.pages.dev/docs/agents/mcp). ## These docs are part of the surface [#these-docs-are-part-of-the-surface] If agents build from docs, docs are infrastructure. This site treats machine readers as first-class: * **[llms.txt](/llms.txt)** — an index of the whole site for machine readers. * **[llms-full.txt](/llms-full.txt)** — the entire docs content in one file, for tools that want everything in context at once. * **Per-page markdown** — every page is also published as a static markdown file (this one: `/llms.mdx/docs/concepts/agent-native/content.md`); see [machine-readable docs](https://capxul-sdk-docs.pages.dev/docs/agents/machine-readable-docs). * **Copy Markdown** — every page has a copy-as-markdown button for pasting into an agent's context. The writing conventions here — grounded facts, one honest [capability map](https://capxul-sdk-docs.pages.dev/docs/capability-status), stable vocabulary — exist for the same reason. An agent cannot ask a hallway question when a page oversells; docs that are safe to build from unsupervised are the bar. ## The bigger picture [#the-bigger-picture] The through-line of this whole concepts section is that Capxul's surface is made of stable, structured, guardrailed primitives instead of ambient access. Agent-native is what that design buys at the platform level: the same primitive is a hook for your frontend, a method for your server, and a tool for your agent — and the platform's safety rules hold across all three, because they live below the surface rather than in any one consumer. # Identity & organizations (https://capxul-sdk-docs.pages.dev/docs/concepts/identity-and-orgs) [Money & accounts](https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts) covers what moves. This page covers who: the identities that can hold money, the organizations layered on top of them, and how the SDK keeps "who is acting" explicit. ## The Capxul user is the atom [#the-capxul-user-is-the-atom] A **Capxul user** is a person with their own personal Account, identified by a Capxul user ID. It is the atomic money-holding identity: *everyone* on the platform is a Capxul user first, and every other structure is layered on top. There is no org-only identity — an organization's founder, members, and payees are all Capxul users (or people invited to become one). ## Organizations are layered on top [#organizations-are-layered-on-top] An **Organization** is a shared entity with its own **treasury Account** and a globally-unique, claimable **Organization ID** — today, the org handle. The treasury is an ordinary Account in the domain sense (opaque id, balance, available balance); what makes it organizational is who may spend from it and under what authority. Org money movement is authorized under **roles**: an org payment is made by a member acting within their role and its spending limits, not by whoever happens to hold a credential. The SDK exposes members, roles, treasury, and payroll as the org surface; the machinery that enforces authority stays underneath. ## Member means authority, not payment history [#member-means-authority-not-payment-history] A **Member** is a Capxul user who holds a role in an organization. Membership is about *authority inside the org* — nothing else. In particular: > Being *paid by* an organization does **not** make you a member. The domain keeps two invitations strictly apart. Inviting someone *into the org with a role* makes them a member. *Paying* someone merely invites them to become an individual Capxul user so they can claim the money. A payroll roster can be full of people with no standing in the org at all — which is exactly right: your salary recipients should not accidentally acquire treasury authority. ## Handles are claimable IDs [#handles-are-claimable-ids] A **claimable ID** is a public, globally-unique identifier that a recipient can be paid at and later claim. Organizations have one (the org handle), and a personal Capxul user has one too: the **Handle** chosen at onboarding — the username, with uniqueness and reservation rules. Handles are what make "pay @fatima" possible without either side ever seeing an address. ## The acting entity is explicit [#the-acting-entity-is-explicit] Most surfaces exist twice: your personal Account has an inbox, an address book, payment requests — and so does each organization. The **acting entity** is which of those you are operating as. The SDK deliberately has **no hidden global "active org"**. Scope is stated at the call site: ```ts client.account.requests.list(); // acting personally client.org(orgId).requests.list(); // acting as the org ``` React follows the same model with explicit actor scopes passed to hooks: ```tsx const personalInbox = useCapxulInbox(capxulAccountScope); const orgInbox = useCapxulInbox(capxulOrgScope(orgId)); ``` If your product has an entity-switcher menu, the selected entity is *your* app state — you hold the chosen `orgId` and pass the matching scope to the hooks on each screen. (`useCapxulSwitchActingEntity` exists as a stable mutation seam for switcher UIs, but it carries no SDK side effect; the switch itself is the consumer's local state.) The trade-off is honest: explicit scoping means passing a parameter where a global would have been terser. What it buys is that no part of the app can silently change which entity another part is operating as — a property worth a great deal when the operations are payments. ## Recipients are typed references [#recipients-are-typed-references] When you name a counterparty, you use a typed **Ref**, never a raw address: ```ts { kind: "email", email: "recipient@example.com" } { kind: "handle", handle: "fatima" } { kind: "orgHandle", orgHandle: "acme" } { kind: "payeeId", payeeId: "payee_123" } ``` Each kind maps onto the identity model: a Capxul user (by handle or email), an organization (by org handle), an email that is not yet a Capxul user (which produces a claimable payment), or a saved **Payee** — an external payout counterparty whose settlement details live inside the Payee record, referenced by an opaque id. The app names *who*; Capxul resolves *where that intent settles*. ## The bigger picture [#the-bigger-picture] The identity model mirrors the money model: opaque ids instead of addresses, typed references instead of raw destinations, and authority made explicit instead of ambient. One consequence is worth internalizing early — questions like "is this person in the org?" and "has this person been paid by the org?" have different answers on purpose. When you can explain why a payroll recipient is not a member, you have this page. Whether a given identity is *ready* to hold and move money is a separate, time-ordered question — that is [the account lane](https://capxul-sdk-docs.pages.dev/docs/concepts/account-lane). # Mental model (https://capxul-sdk-docs.pages.dev/docs/concepts/mental-model) 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. ```text 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 evidence ``` If 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 [#a-method-based-client] The core of the SDK is `CapxulClient`, organized as method bundles: ```ts 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 [#the-clienthooks-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 with `createCapxulClient` and call methods directly. * **`@capxul/sdk-react`** — React bindings. `CapxulProvider` owns 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 [#the-surface-is-intentionally-narrow] Creating a client takes a **publishable key** — and almost nothing else: ```ts 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](https://capxul-sdk-docs.pages.dev/docs/concepts/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 [#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 [#results-not-thrown-surprises] Every public client method returns `Promise>`: ```ts type CapxulResult = { ok: true; value: T } | { ok: false; error: CapxulError }; ``` ```ts 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 [#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](https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts) for what moves, and [identity & organizations](https://capxul-sdk-docs.pages.dev/docs/concepts/identity-and-orgs) for who moves it. # Money & accounts (https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts) The Capxul surface is Stripe-shaped: money is just money. You will never pass a raw token amount, an address, or a chain identifier to move it. This page is the vocabulary that replaces those things — every hook and client method speaks it, so it is worth learning precisely. ## Money [#money] **Money** is a consumer-facing amount: a currency, a decimal value, and a precision. ```ts { currency: "USD", value: "25.00", decimals: 2 } ``` It is the only monetary value any caller sees — never wei, never a raw token integer. The precision travels with the value so amounts round-trip exactly. ## Account [#account] An **Account** is a person's or organization's logical pot of money, identified by an opaque `account_`-shaped id — **not** an address. An Account has a balance and an available balance. Every Capxul user has a personal Account; every organization has a treasury Account. ## Transfer vs. Payment [#transfer-vs-payment] The first distinction the domain draws is whether money leaves the Account. * A **Transfer** moves money *within a single Account* — between the main balance and a named envelope (sub-account), or between envelopes. A Transfer never reaches an external party. **A Transfer is not a Payment.** * A **Payment** sends money from an Account to an *external recipient* — someone outside your own Account. "Payment" is the domain noun; "pay" is the verb. ## The kinds of Payment [#the-kinds-of-payment] Payment is the genus. Its two species differ only in **who authorizes the outflow**: * A **direct payment** is paid from a *personal* Account; the person authorizes it themselves. * An **org payment** is paid from an *organization's* Account, authorized under the payer's **role** and its spending limits. Two familiar product shapes are kinds of Payment, not separate mechanisms: * **Payroll** — paying many people at once from an organization (salaries). * A **batch payment** — one Payment instruction that pays several recipients together as a single settled act. This is a deliberately small taxonomy. New product surfaces do not get new money-movement primitives; they compose Payments. ## Time-based payments are Commitments [#time-based-payments-are-commitments] Two more Payment shapes release money by the clock instead of instantly: * A **scheduled payment** releases *in full at a chosen future time* — nothing before, everything after. * A **stream** releases *continuously over a window* (a salary over a month), optionally with a **cliff** — a point before which nothing has released. Both are backed by the same object: a **Commitment**. When a Commitment is created, the payer's money is escrowed in the settlement layer. It then **vests** by the clock (Capxul uses straight-line release between start and end; a scheduled payment is simply a Commitment whose cliff and end are the same instant). The recipient takes possession by **claiming** the vested funds. The payer keeps two safety valves until the money is claimed: * **Cancel** — reclaim the *unvested* remainder. Vested-but-unclaimed money stays the recipient's. * **Redirect** — point the not-yet-claimed Payment at a different recipient (paid the wrong person, or the original never claimed) without cancelling and re-creating it. Recoverability is the trade-off that makes Commitments central: escrow costs the payer liquidity up front, but it means a time-based or not-yet-claimable payment is never irreversibly gone. ## Payment documents [#payment-documents] A **payment document** is the human-meaningful record of *why* a Payment happened — one of four kinds: **invoice**, **payslip**, **receipt**, or **memo** (free-text, the minimal document). The full document lives off the settlement layer; the Payment itself carries only a tamper-proof fingerprint of it (the **document hash**), so the reason for a payment stays verifiable long after the fact. ## Recipients, validation, and the instant-send guardrail [#recipients-validation-and-the-instant-send-guardrail] A **recipient** is always a typed reference — a Capxul user (by id or email), an organization, an email that is not yet a Capxul user, or a saved **Payee** — never a raw address in the caller's hands. A recipient is **validated** once they have activated their own Capxul account, so money sent to them lands somewhere they already control. That distinction drives the single most important settlement rule: > An instant, irreversible Payment is only permitted to a **validated** > recipient. Paying anyone not yet validated always goes through a > recoverable **Commitment**. The SDK enforces this — a bare irreversible send to an unvalidated recipient is never offered. If a payment you expected to be instant comes back as escrowed, this guardrail is why; it is not a bug to work around. ### Claimable payments [#claimable-payments] The guardrail's most visible product shape is the **claimable payment** (payment link): a Payment to someone who is not yet a validated Capxul user, named by email. It is simply a Commitment addressed to the account reserved for that email — the account the recipient takes control of once they activate. The money escrows now; the recipient validates and claims. "You've got money on Capxul" is a real escrowed Commitment, not a notification about a promise — and if the recipient never shows up, the payer cancels or redirects it. There is no separate held-money machinery. ## Where the model meets today's alpha [#where-the-model-meets-todays-alpha] The vocabulary above is the platform's domain model; not every corner of it has a fully proven product surface in the published alpha. Payments to typed recipients are live with the constraints listed on [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status); payroll, requests, and sub-account primitives are published but at varying grades. When this page and that one disagree about what you can ship today, that one wins. ## The bigger picture [#the-bigger-picture] Notice what the whole vocabulary refuses to mention: addresses, tokens, chains. Accounts are opaque ids; recipients are typed references; amounts are decimal Money. If you find yourself needing a raw address or a token integer to express something, you have left the intended surface — go back and find the domain word for what you actually mean. Who holds these Accounts and who may move money from them is the subject of [Identity & organizations](https://capxul-sdk-docs.pages.dev/docs/concepts/identity-and-orgs). # Architecture (https://capxul-sdk-docs.pages.dev/docs/contributors/architecture) The platform is a set of published npm packages over a private substrate, separated by a ports-and-adapters seam. The authoritative contract is [`canon/architecture.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/architecture.md); this page is the summary. ## Public packages vs the private substrate [#public-packages-vs-the-private-substrate] Five packages publish to npm. Everything else is `"private": true` and never publishes — private packages never appear in a consumer's `node_modules`; published artifacts inline the internal code they need at pack time. | Public (published) | Private (never published) | | --------------------- | ------------------------- | | `@capxul/sdk` | `@capxul/backend` | | `@capxul/sdk-react` | `@capxul/wire` | | `@capxul/sdk-testing` | `@capxul/types` | | `@capxul/mcp` | `@capxul/errors` | | `@capxul/sandbox` | `@capxul/config` | | | `@capxul/observability` | | | `@capxul/contracts` | ## The layer stack [#the-layer-stack] Consumers call method bundles and receive `Promise>`. Internally, ports are Effect `Context.Tag`s, adapters are Effect `Layer`s, and domain code is an Effect program that depends only on ports: | Layer | Owns | | ------------------ | ----------------------------------------------------------------------------- | | Public SDK surface | `createCapxulClient`, method bundles, `CapxulResult`, `CapxulError` | | Domain core | Effect programs for auth, provisioning, identity, credentials, smart accounts | | Ports | `Context.Tag` declarations and service interfaces | | Adapters | `Layer` implementations of ports — external libraries and platform IO | | Backend | Convex functions, BetterAuth, Resend, AgentMail, tenancy guards | The load-bearing constraint: the domain core does not import Convex, BetterAuth, Resend, AgentMail, Openfort, browser storage, the Node filesystem, or network clients directly. All platform IO enters through an adapter behind a port. ## The ports-and-adapters seam [#the-ports-and-adapters-seam] A **port** is a `Context.Tag` plus a service interface; an **adapter** is a `Layer` that provides it. Each port has a spec in [`canon/ports/`](https://github.com/Xelmar-tech/infrastructure/tree/master/canon/ports) and the full list lives in the [port catalog](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/vocabulary/port-catalog.md). The catalog groups them into three families: * **Domain ports** — `IdentityPort`, `SmartAccountPort`, `AccountReadPort`, `SubAccountPort`, `SafeDeploymentPort`, `CredentialsPort`, `BootstrapPort`. * **Platform ports** — `AuthClientPort`, `AuthCachePort`, `EvmSignerPort`, `ClockPort`, `EnvPort`, `TelemetryPort`. * **Primitive ports** — `ConvexCallPort`, `TransportPort`. Every port is proven by a single conformance matrix that runs the same tests against its hermetic and real adapters — see the [test contract](https://capxul-sdk-docs.pages.dev/docs/contributors/conventions#test-contract--red-test-contract). ## Where the backend runs [#where-the-backend-runs] The backend is serverless **Convex** — there is no Docker image and no local database. `@capxul/backend` owns the Convex functions, BetterAuth (auth and OTP), Resend (email delivery), AgentMail (test inboxes), and the tenancy guards. Every public query or mutation that returns user data must call `requireConsentedUser(userId, appId)` before reading or returning it. The SDK reaches Convex only through the `ConvexCallPort` / `TransportPort` adapters — never by importing the generated Convex API. ## The public-surface rule [#the-public-surface-rule] Consumers import `@capxul/sdk` and `@capxul/sdk-react` — nothing else. The backend, `@capxul/wire`, and the Convex function APIs are private surface, and internal SDK subpaths (`@capxul/sdk/production`, `_internal`, adapter seams) are not part of the published contract. Public SDK methods return `Promise>`: ```ts export type CapxulResult = | { readonly ok: true; readonly value: T } | { readonly ok: false; readonly error: CapxulError }; ``` They do not return raw values, thrown exceptions, actors, state machines, or Effect programs. Internal Effect error variants are mapped to `CapxulError` before any value crosses the boundary. The rule itself is [`canon/rules/public-surface.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/rules/public-surface.md); the error shape is covered in [Conventions](https://capxul-sdk-docs.pages.dev/docs/contributors/conventions#error-handling-and-the-error-catalog). # Conventions (https://capxul-sdk-docs.pages.dev/docs/contributors/conventions) These are the conventions that PR review actually enforces. Each section is a summary; the linked canon file is the authority. ## Vocabulary discipline [#vocabulary-discipline] Two distinct rules share this name: **The leak rule.** The consumer surface is Stripe-shaped: money is just money, and crypto is the substrate, never the vocabulary. A custody term ("Safe", "address", "Zodiac", "UserOp", "wei", "chain") appearing in the consumer-facing surface is a **leak**, and leaks are defects. [`CONTEXT.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/CONTEXT.md) at the repo root is the vocabulary authority — check it before naming anything a consumer will see. **Identifier discipline.** Build-cycle metadata never appears in production identifiers or test helper type aliases: `Stage1`, `Phase2`, `M1_1`-style names fail review. Stage and phase words are fine in markdown narrative; code identifiers must name durable architecture concepts. Canon: [`canon/conventions/vocabulary-discipline.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/conventions/vocabulary-discipline.md) and [`canon/rules/vocabulary-discipline.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/rules/vocabulary-discipline.md). ## Branded types [#branded-types] Every domain primitive at a port surface is branded in `@capxul/types` (`AccountId`, `Email`, `PublishableKey`, `EpochMs`, …). Constructors like `toAccountId` are the **only** way to create branded values — manual casts (`raw as Address`) are banned outside the constructor implementations. Non-wire seams (DB hydration, env reads, fixtures) use the `to*` constructors; wire seams (HTTP/JSON crossing into the SDK) use schemas from `@capxul/wire`, which must validate exactly as strictly as their `to*` counterparts — shape-only brand schemas are not permitted. Canon: [`canon/conventions/branded-types.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/conventions/branded-types.md) and [`canon/rules/branded-types.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/rules/branded-types.md). ## Error handling and the error catalog [#error-handling-and-the-error-catalog] `CapxulError` is the public error type. Internal code may model errors as Effect channel variants, but every public SDK method maps them to `CapxulError` and returns `CapxulResult` — internal variants never cross the boundary, and adapters translate external failures at the edge. The canonical public catalog contains **21 codes** (`NOT_AUTHENTICATED`, `INVALID_INPUT`, `INSUFFICIENT_BALANCE`, `WRONG_STATE`, …); every `Errors.*` factory must return one of them. Raw provider cause payloads may live on the native `Error.cause` chain but do not belong in public `message` or `details` fields. Canon: [`canon/conventions/error-handling.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/conventions/error-handling.md) and [`canon/conventions/error-catalog.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/conventions/error-catalog.md). ## Test contract & RED test contract [#test-contract--red-test-contract] Every slice owns tests at the layer it changes: **L1** (pure functions, types, schemas), **L2** (port and adapter behavior — exactly one `.conformance.test.ts` matrix file per port), **L3** (package integration without external services), **L4** (the real reachable runtime/service subset for the slice). Work starts RED: a test that fails because required production behavior is absent or wrong — not because a file doesn't exist. GREEN means that same test passes without being skipped, stubbed, weakened, or rewritten. `.skip` and `.todo` after the RED phase, existence checks, and mocking the unit under test are all rejected; mock only at system boundaries. Every child issue declares its RED test up front (path, what it proves, mock boundary, why it fails). Canon: [`canon/conventions/test-contract.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/conventions/test-contract.md), [`canon/conventions/red-test-contract.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/conventions/red-test-contract.md), and [`canon/conventions/red-green-classifier.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/conventions/red-green-classifier.md). ## Public-surface rule [#public-surface-rule] Public SDK methods return `Promise>` — never raw values, thrown exceptions, actors, state machines, or Effect programs. Consumers import `@capxul/sdk` and `@capxul/sdk-react` only; the backend, `@capxul/wire`, and Convex APIs are private. See [Architecture](https://capxul-sdk-docs.pages.dev/docs/contributors/architecture#the-public-surface-rule) for the layer picture. Canon: [`canon/rules/public-surface.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/rules/public-surface.md). ## Commit convention [#commit-convention] Commit messages follow `type(scope): summary` — e.g. `feat(sandbox): add canonical operator CLI`, `fix(sdk): keep private packages out of runtime manifests`, `chore(release): version packages`. This is repo practice throughout the history and the release tooling (the automated version PR commits as `chore(release): version packages`); there is no separate canon file for it. PRs that change a publishable package's consumer-facing behavior must also include a changeset — see [Release flow](https://capxul-sdk-docs.pages.dev/docs/contributors/release-flow). # Dev onboarding (https://capxul-sdk-docs.pages.dev/docs/contributors/dev-onboarding) This section is for developers joining the platform repository — [`Xelmar-tech/infrastructure`](https://github.com/Xelmar-tech/infrastructure) (local checkouts are often named `capxul-platform`) — as contributors. These pages summarize the repo's internal canon and link into it; when a page here disagrees with a canon file in the repo, the canon file wins. Start with this page, then read [Architecture](https://capxul-sdk-docs.pages.dev/docs/contributors/architecture), [Conventions](https://capxul-sdk-docs.pages.dev/docs/contributors/conventions), and [Release flow](https://capxul-sdk-docs.pages.dev/docs/contributors/release-flow). ## Prerequisites [#prerequisites] * **Node ≥ 22.12** — enforced by the root `package.json` `engines` field. The `vp` and `convex` CLIs break on older Node. * **pnpm 9.15.9** — pinned via the root `packageManager` field. ```bash git clone git@github.com:Xelmar-tech/infrastructure.git cd infrastructure pnpm install # also installs the lefthook git hooks ``` ## The vp toolchain [#the-vp-toolchain] The repo runs on [vite-plus](https://npmjs.com/package/vite-plus) (`vp`). Every gate-shaped command goes through a `vp` verb — raw `pnpm` and bare `bash scripts/*.sh` invocations are banned by the repo's operating rules (`AGENTS.md` Rule 3). The task graph that the aggregate gates traverse is defined in the root `vite.config.ts` under `run.tasks`. | Command | What it does | | ------------------------- | ------------------------------------------------------------------------ | | `vp test` | Hermetic unit tests (Vitest) — no external services, no secrets file | | `vp lint --deny-warnings` | Lint (oxlint, not ESLint) | | `vp run lint:fix` | Lint with auto-fix | | `vp run format` | Format (oxfmt, not Prettier) | | `vp run check-types` | `tsc --noEmit` against the root project | | `vp run check:fast` | The pre-push gate (lefthook runs it for you) | | `vp run check:full` | The full gate — runs on pushes to `master` in CI; includes `vp pack` | | `vp run test:e2e` | Live SDK e2e against a real deployment (sources operator secrets itself) | Fan-out and targeting: `vp run -r ` runs a task across packages, `vp run --filter ` targets one. Libraries build with `vp pack`, apps with `vp build`. ## Workspace layout [#workspace-layout] A pnpm monorepo with two workspace groups. `packages/*`: | Package | One line | | ----------------------- | ---------------------------------------------------------------------------- | | `@capxul/sdk` | Core TypeScript client — `createCapxulClient` and method bundles (published) | | `@capxul/sdk-react` | React hooks over the client (published) | | `@capxul/sdk-testing` | Test utilities for SDK adapters and proof harnesses (published) | | `@capxul/backend` | Convex backend — schema, auth, credentials, tenancy (private) | | `@capxul/wire` | Wire schemas shared by backend and SDK adapters (private) | | `@capxul/types` | Branded domain types and their `to*` constructors (private) | | `@capxul/errors` | `CapxulError` factories and Convex error decoding (private) | | `@capxul/config` | Chain configs and constants (private) | | `@capxul/observability` | Telemetry utilities (private) | | `@capxul/contracts` | Solidity settlement contracts, built with Foundry (private) | `apps/*`: | App | One line | | ---------------- | ------------------------------------------------------------------------ | | `apps/mcp` | The Capxul MCP server CLI — agent-facing financial-ops tools (published) | | `apps/sandbox` | Sandbox operator CLI — publishable keys and testnet faucet (published) | | `apps/docs` | This documentation site (Fumadocs) | | `apps/reference` | Canonical SDK consumer exemplar app | | `apps/exemplar` | Greenfield SDK integration DX demo | | `apps/approve` | One-page approval app that MCP approval links deep-link into | | `apps/skybridge` | React views over the SDK, rendered inside ChatGPT/Claude | Which packages publish to npm and which never do is covered in [Release flow](https://capxul-sdk-docs.pages.dev/docs/contributors/release-flow). ## Where the canon lives [#where-the-canon-lives] The repo's standing rules live in [`canon/`](https://github.com/Xelmar-tech/infrastructure/tree/master/canon) at the repo root: * `canon/conventions/` — load-bearing conventions: branded types, error handling, the test contract, and more (summarized in [Conventions](https://capxul-sdk-docs.pages.dev/docs/contributors/conventions)). * `canon/rules/` — short, enforceable rules (public surface, branded types, vocabulary discipline). * `canon/ports/` — one spec per port in the ports-and-adapters seam. * `canon/vocabulary/` — the port, brand, and error catalogs. * `canon/architecture.md`, `canon/release-flow.md`, `canon/dev-onboarding.md` — the architecture contract, the publish runbook, and the operational onboarding guide. [`CONTEXT.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/CONTEXT.md) at the repo root is the domain glossary and the vocabulary authority. When a term there disagrees with the code, the disagreement is a bug in one of them — say so. ## Backend, keys, and secrets [#backend-keys-and-secrets] Day-to-day development runs against a Convex deployment. The recommended path is an isolated per-worktree deployment: `bash scripts/provision-agent-convex.sh` creates one, sets its server env, pushes the backend, and mints a publishable key. Operator secrets live in **one file per machine** — `~/.config/capxul/secrets.env` — managed with `vp run secrets:init` and `vp run secrets:verify` (never commit or print values). The full operational guide — origin allowlists, the dev OTP peek, and the gotchas that cost real time — is [`canon/dev-onboarding.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/dev-onboarding.md). ## Agents work here too [#agents-work-here-too] The repo is operated by humans and autonomous agents under the same rules. [`AGENTS.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/AGENTS.md) carries the closed-loop operating rules that apply to every agent in every runtime: decision prompts must carry a full decision pack (ground-truth code or diagram, rubric, graded options, recommendation); every PR must pass a clean run of the local `/code-review` gate before it is opened; and every gate-shaped invocation goes through `vp`, never raw `pnpm` or shell scripts. Read it before dispatching or acting as an agent in this repo. # Release flow (https://capxul-sdk-docs.pages.dev/docs/contributors/release-flow) Packages ship from the monorepo with [Changesets](https://github.com/changesets/changesets). The trunk branch is `master`, and the full runbook is [`canon/release-flow.md`](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/release-flow.md); this page is the contributor summary. ## Pre-release mode on the alpha tag [#pre-release-mode-on-the-alpha-tag] The repo is in Changesets **pre-release mode** (`.changeset/pre.json`: `"mode": "pre"`, `"tag": "alpha"`). Every publish lands a prerelease version on the `alpha` npm dist-tag — `latest` only moves when a stable release ships intentionally. `@capxul/sdk`, `@capxul/sdk-react`, `@capxul/sdk-testing`, and `@capxul/mcp` are a **fixed group** in `.changeset/config.json`: they version in lock-step, so their published versions always match. ## The publishable set [#the-publishable-set] | Package | Role | | --------------------- | ------------------------------------------------------ | | `@capxul/sdk` | Core SDK | | `@capxul/sdk-react` | React hooks surface | | `@capxul/sdk-testing` | Testing utilities for SDK adapters and proof harnesses | | `@capxul/mcp` | Runnable `capxul-mcp` MCP server CLI (`apps/mcp`) | | `@capxul/sandbox` | Runnable sandbox operator CLI (`apps/sandbox`) | All other workspace packages (`backend`, `wire`, `types`, `errors`, `config`, `observability`, `contracts`, and the non-published apps) are `"private": true` and **never publish** — `.changeset/config.json` sets `privatePackages.tag: false`. `@capxul/mcp` and `@capxul/sandbox` are published **apps**: they ship a runnable CLI (a single bundled `main.mjs` with a bin entry) rather than a library, and every internal `@capxul/*` package they reach is inlined into the bundle. ## Day-to-day: add a changeset [#day-to-day-add-a-changeset] When a PR changes a publishable package's consumer-facing behavior, add a changeset before merge — CI enforces this via the `changeset:required` check: ```bash vp run changeset ``` Pick the affected packages, the semver bump, and write a short summary. Do **not** bump `package.json` versions in feature PRs — only the changeset file lands. ## The Release workflow [#the-release-workflow] [`.github/workflows/release.yml`](https://github.com/Xelmar-tech/infrastructure/blob/master/.github/workflows/release.yml) runs on every push to `master`: 1. **Version PR** — when pending changesets exist, `changesets/action` opens a PR titled `chore(release): version packages` that bumps versions, updates `CHANGELOG.md` files, and consumes the changeset files. Review it like any other PR: versions, changelog wording, which packages ship. 2. **Publish on merge** — merging the version PR triggers the same workflow to publish to npm (auth via a granular `NPM_TOKEN` under the protected `npm` environment), create `@capxul/@x.y.z` git tags, and open GitHub Releases. A final step re-aligns the `alpha` dist-tag on the published versions. If the workflow is blocked, there is a break-glass local path (`vp run release:local`) — see the [manual release runbook](https://github.com/Xelmar-tech/infrastructure/blob/master/canon/release-flow.md#manual-release-workflow-blocked). ## Pin exact versions [#pin-exact-versions] Consumers install from the `alpha` dist-tag and should **pin exact versions**: ```bash npm i @capxul/sdk@alpha @capxul/sdk-react@alpha ``` The alpha train can change surface between prereleases, so treat every bump as deliberate: pin the exact `1.0.0-alpha.x` version in `package.json` (no `^`/`~` range), upgrade `@capxul/sdk` and `@capxul/sdk-react` together (they version in lock-step), and check [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) for what is live in the version you are pinning. npm versions are immutable — a bad publish is fixed by a new version, never a republish. # Quickstart (https://capxul-sdk-docs.pages.dev/docs/getting-started) This is the shortest path to a working Capxul sign-in. By the end, your local app can request a one-time code by email, verify it, and show the signed-in session. You do not need to understand Capxul's backend to complete it. The steps below use [Vite](https://vite.dev) because it is the fastest way to a running React app. Building on Next.js? Follow the [Next.js guide](https://capxul-sdk-docs.pages.dev/docs/getting-started/nextjs) instead — same flow, framework-specific wiring. ### Create an app and install the SDK [#create-an-app-and-install-the-sdk] ```bash npm create vite@latest capxul-quickstart -- --template react-ts cd capxul-quickstart npm install npm install @capxul/sdk@1.0.0-alpha.13 @capxul/sdk-react@1.0.0-alpha.13 @tanstack/react-query ``` Pin the exact versions shown. The packages publish on the `1.0.0-alpha.x` train, and these docs are verified against `1.0.0-alpha.13` — see [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) for what that version can do. `@tanstack/react-query` v5 is a peer dependency of `@capxul/sdk-react`. ### Get a test key [#get-a-test-key] ```bash npx @capxul/sandbox key create ``` When the CLI asks for the **allowed local origin**, enter `http://localhost:5173` — Vite's dev origin. The CLI creates a test publishable key and writes it to a local handoff file (it never prints the raw key to the terminal). Open the handoff file and keep two values nearby: the **publishable key** and the **Capxul site URL**. Full walkthrough of the CLI, the handoff file, and test funds: [Test keys & funds](https://capxul-sdk-docs.pages.dev/docs/getting-started/test-keys). ### Add environment variables [#add-environment-variables] Create `.env.local` in the project root: ```bash VITE_CAPXUL_PUBLISHABLE_KEY= CAPXUL_SITE_URL= ``` Only the publishable key belongs in the browser, so only it gets the `VITE_` prefix. `CAPXUL_SITE_URL` is dev-server configuration — the next step reads it in `vite.config.ts` — and without the prefix Vite keeps it out of the browser bundle. ### Proxy the two Capxul routes [#proxy-the-two-capxul-routes] Replace `vite.config.ts`: ```ts import react from "@vitejs/plugin-react"; import { defineConfig, loadEnv } from "vite"; export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), ""); const capxulSiteUrl = env.CAPXUL_SITE_URL; if (!capxulSiteUrl) { throw new Error("CAPXUL_SITE_URL is required"); } return { plugins: [react()], server: { proxy: { "/v1/client/bootstrap": { target: capxulSiteUrl, changeOrigin: true, secure: true, }, "/api/auth": { target: capxulSiteUrl, changeOrigin: true, secure: true, }, }, }, }; }); ``` Why this exists: the SDK boots by calling `/v1/client/bootstrap` **on your app's own origin**, and sign-in stores its session cookies under same-origin `/api/auth/*`. The proxy forwards both paths to Capxul while the browser keeps treating them as your origin — which is what makes the cookies work. ### Mount `CapxulProvider` [#mount-capxulprovider] Replace `src/main.tsx`: ```tsx import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { CapxulProvider } from "@capxul/sdk-react"; import App from "./App"; const publishableKey = import.meta.env.VITE_CAPXUL_PUBLISHABLE_KEY; if (!publishableKey) { throw new Error("VITE_CAPXUL_PUBLISHABLE_KEY is required"); } createRoot(document.getElementById("root")!).render( , ); ``` The provider owns the whole client lifecycle: it bootstraps from the publishable key, creates the TanStack Query cache, and tears everything down on unmount. You do not mount a `QueryClientProvider` or call any client factory yourself. ### Build the sign-in form [#build-the-sign-in-form] Replace `src/App.tsx`: ```tsx import { type FormEvent, useState } from "react"; import { useCapxul, useCapxulSession, useCapxulSignIn, useCapxulSignOut, useCapxulVerifyOtp, } from "@capxul/sdk-react"; export default function App() { const bootstrap = useCapxul(); const session = useCapxulSession(); const signIn = useCapxulSignIn(); const verifyOtp = useCapxulVerifyOtp(); const signOut = useCapxulSignOut(); const [email, setEmail] = useState(""); const [code, setCode] = useState(""); const [otpSent, setOtpSent] = useState(false); async function sendOtp(event: FormEvent) { event.preventDefault(); await signIn.mutateAsync({ email }); setOtpSent(true); } async function verify(event: FormEvent) { event.preventDefault(); await verifyOtp.mutateAsync({ email, code }); } if (bootstrap.status === "bootstrapping" || session.isLoading) { return
Loading Capxul…
; } if (bootstrap.status === "error") { return (

Capxul failed to start: {bootstrap.error?.message ?? "Unknown error"}

); } if (session.data) { return (

Signed in

Email: {session.data.email}

User id: {session.data.authUserId}

); } return (

Sign in

{!otpSent ? (
setEmail(event.target.value)} />
) : (
setCode(event.target.value)} />
)} {signIn.error ?

{signIn.error.message}

: null} {verifyOtp.error ?

{verifyOtp.error.message}

: null}
); } ``` Three calls do the work: * `signIn.mutateAsync({ email })` sends the one-time code to the email. * `verifyOtp.mutateAsync({ email, code })` verifies it. If the email has never signed in before, Capxul creates the user identity here — there is no separate "create user" call. * `useCapxulSession()` reads the signed-in state; it flips from `null` to a session the moment verification succeeds, because the mutation invalidates the auth queries for you.
### Run it [#run-it] ```bash npm run dev ``` Open [http://localhost:5173](http://localhost:5173), enter an email you can read, submit the code from the inbox, and the page should show **Signed in** with the email and user id. If the app is stuck on a bootstrap error instead, the usual cause is an origin mismatch: the test key must allow `http://localhost:5173` exactly. Re-run `npx @capxul/sandbox key create` with the right origin if needed.
## Where to go next [#where-to-go-next] * [Next.js](https://capxul-sdk-docs.pages.dev/docs/getting-started/nextjs) — the same flow wired into a Next.js App Router app. * [Requirements & signers](https://capxul-sdk-docs.pages.dev/docs/getting-started/requirements-and-signers) — what to add when your app needs an account and money UI, not just sign-in. * [Test keys & funds](https://capxul-sdk-docs.pages.dev/docs/getting-started/test-keys) — the sandbox CLI in full, including test money. * [Hooks reference](https://capxul-sdk-docs.pages.dev/docs/hooks) — the full hook catalog behind these five imports. * [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) — what is live in the published alpha before you build further. # Next.js (https://capxul-sdk-docs.pages.dev/docs/getting-started/nextjs) This guide takes a fresh Next.js App Router app to a working Capxul email OTP login. It is the Next.js version of the [Quickstart](https://capxul-sdk-docs.pages.dev/docs/getting-started) and has been validated end-to-end against a real integration. You need two values before starting: a **publishable key** and a **Capxul site URL**. Get both from the sandbox CLI — [Test keys & funds](https://capxul-sdk-docs.pages.dev/docs/getting-started/test-keys) — or from a secure handoff Capxul gave you. Only the publishable key ever belongs in a browser env var. Never put backend secrets, private keys, or operator credentials in a Next.js app. ### Install packages [#install-packages] From a fresh Next.js app: ```bash npm install @capxul/sdk@1.0.0-alpha.13 @capxul/sdk-react@1.0.0-alpha.13 @tanstack/react-query ``` Pin exact versions. The packages publish on the `1.0.0-alpha.x` train; do not assume `latest` or the `alpha` tag points at the build your handoff was validated against. ### Add environment variables [#add-environment-variables] Create `.env.local`: ```bash NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY= CAPXUL_SITE_URL= ``` For local development the key must allow `http://localhost:3000` — that is the origin you gave the CLI when creating it. `CAPXUL_SITE_URL` is not a secret, but it is server-side configuration — it is read by `next.config.ts` in the next step, and nothing in the browser needs it. Do **not** prefix it with `NEXT_PUBLIC_`; keeping it server-side means it never ships in the client bundle. ### Proxy Capxul routes [#proxy-capxul-routes] Create or update `next.config.ts`: ```ts import type { NextConfig } from "next"; import { loadEnvConfig } from "@next/env"; loadEnvConfig(process.cwd()); const capxulSiteUrl = process.env.CAPXUL_SITE_URL; if (!capxulSiteUrl) { throw new Error("CAPXUL_SITE_URL is required"); } const nextConfig: NextConfig = { async rewrites() { return [ { source: "/v1/client/bootstrap", destination: `${capxulSiteUrl}/v1/client/bootstrap`, }, { source: "/api/auth/:path*", destination: `${capxulSiteUrl}/api/auth/:path*`, }, ]; }, }; export default nextConfig; ``` Two things here are load-bearing: * **Why the rewrites exist.** The browser SDK intentionally boots by calling `/v1/client/bootstrap` on your app's own origin, and sign-in keeps its session cookies under same-origin `/api/auth/*`. The rewrites forward both paths to the Capxul backend while the browser keeps the cookies on your app's origin — which is what makes the session work. * **Why `loadEnvConfig` is there.** `next.config.ts` runs before the app runtime, which loads `.env.local` for you — the config file does not get that for free. Without the explicit `@next/env` load, `CAPXUL_SITE_URL` can be missing during local builds even though the app itself sees `NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY` just fine. ### Mount `CapxulProvider` [#mount-capxulprovider] Create `app/capxul-provider.tsx`: ```tsx "use client"; import { CapxulProvider } from "@capxul/sdk-react"; export function Providers({ children }: { children: React.ReactNode }) { const publishableKey = process.env.NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY; if (!publishableKey) { throw new Error("NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY is required"); } return ( {children} ); } ``` Wrap the app in `app/layout.tsx`: ```tsx import type { Metadata } from "next"; import { Providers } from "./capxul-provider"; import "./globals.css"; export const metadata: Metadata = { title: "Capxul Next.js Quickstart", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( {children} ); } ``` The provider owns bootstrap, the TanStack Query cache, and client teardown. You do not mount `QueryClientProvider` or call a client factory yourself. ### Build the login page [#build-the-login-page] Create `app/page.tsx`: ```tsx "use client"; import { type FormEvent, useState } from "react"; import { useCapxul, useCapxulSession, useCapxulSignIn, useCapxulSignOut, useCapxulVerifyOtp, } from "@capxul/sdk-react"; export default function Home() { const bootstrap = useCapxul(); const session = useCapxulSession(); const signIn = useCapxulSignIn(); const verifyOtp = useCapxulVerifyOtp(); const signOut = useCapxulSignOut(); const [email, setEmail] = useState(""); const [code, setCode] = useState(""); const [otpSent, setOtpSent] = useState(false); const [message, setMessage] = useState(null); async function sendOtp(event: FormEvent) { event.preventDefault(); setMessage(null); await signIn.mutateAsync({ email }); setOtpSent(true); setMessage("Check your email for the one-time code."); } async function verify(event: FormEvent) { event.preventDefault(); setMessage(null); await verifyOtp.mutateAsync({ email, code }); setMessage("Logged in."); } if (bootstrap.status === "bootstrapping" || session.isLoading) { return
Loading Capxul...
; } if (bootstrap.status === "error") { return (

Capxul bootstrap failed: {bootstrap.error?.message ?? "Unknown error"}

); } if (session.data) { return (

Signed in

Email: {session.data.email}

User id: {session.data.authUserId}

); } return (

Capxul login

{!otpSent ? (
setEmail(event.target.value)} />
) : (
setCode(event.target.value)} />
)} {message ?

{message}

: null} {signIn.error ?

{signIn.error.message}

: null} {verifyOtp.error ?

{verifyOtp.error.message}

: null}
); } ``` The `verifyOtp` call is both "create user" and "log in": if the email has never signed in before, Capxul creates the user identity during verification. Your app does not call a separate create-user API. Import only from `@capxul/sdk` and `@capxul/sdk-react` package roots. Deep imports into package internals are not public API and will break between alpha releases.
### Validate [#validate] Run through this checklist before calling the integration done: 1. The key allows `http://localhost:3000` (the origin you gave the CLI). 2. `CAPXUL_SITE_URL` points at the Capxul backend from your handoff. 3. `npm run build` passes — this proves `next.config.ts` sees `CAPXUL_SITE_URL` (the `@next/env` load above). 4. Start `npm run dev`, send a code to a test email, verify it, and confirm the page shows the session email and user id. A profile can be `null` for a newly created identity — the session is the proof point here, not the profile. 5. Before deploying, get your production origin added to **both** origin gates: the publishable key's origin allowlist covers only bootstrap; the Capxul backend keeps separate trusted-origin and CORS allowlists (`CAPXUL_TRUSTED_ORIGINS`, `CORS_ALLOWED_ORIGINS`) that must also include your origin. If bootstrap succeeds but auth calls fail, this split is the usual suspect.
## Beyond login [#beyond-login] Stop here if the product only needs sign-in. If it also needs an account, balance, or money movement, the next decision is the `requirement` prop on `CapxulProvider` — read [Requirements & signers](https://capxul-sdk-docs.pages.dev/docs/getting-started/requirements-and-signers), then the [hooks reference](https://capxul-sdk-docs.pages.dev/docs/hooks) for the money hooks and [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) for what is live in the current alpha. # Requirements & signers (https://capxul-sdk-docs.pages.dev/docs/getting-started/requirements-and-signers) 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. ```tsx {children} ``` ## The requirement ladder [#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 [#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](https://capxul-sdk-docs.pages.dev/docs/concepts/account-lane); what your app sees is a single readiness value, `AccountLifecycle`: ```ts 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`](https://capxul-sdk-docs.pages.dev/docs/hooks), which polls automatically while setup is in progress and exposes a `retry` mutation for the `failed` state: ```tsx "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

Setting up your account…

; } if (lifecycle.status === "failed") { return (

Account setup failed: {lifecycle.error.message}

); } if (!lifecycle.canTransact) { return

Your account is almost ready…

; } 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](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Signers [#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` [#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. ```tsx {children} ``` ### Local development and tests: `devPrivateKeySigner` [#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: ```tsx import { devPrivateKeySigner } from "@capxul/sdk"; {children} ``` 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](https://capxul-sdk-docs.pages.dev/docs/getting-started/test-keys) if you must, never with real money. ### That's the whole surface [#thats-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 [#next] * [Account lane](https://capxul-sdk-docs.pages.dev/docs/concepts/account-lane) — what provisioning actually does between `settingUp` and `ready`. * [Hooks reference](https://capxul-sdk-docs.pages.dev/docs/hooks) — `useCapxulAccountLifecycle` and the account/money hooks it gates. * [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) — which money capabilities are live in the published alpha. # Test keys & funds (https://capxul-sdk-docs.pages.dev/docs/getting-started/test-keys) `@capxul/sandbox` is the self-serve CLI for local development against the Capxul test environment. It does two things: mint a **test publishable key** and send **test funds**. Everything it produces is test-only — live keys are provisioned by Capxul through a secure handoff, not this CLI. ## Create a test publishable key [#create-a-test-publishable-key] ```bash npx @capxul/sandbox key create ``` The CLI is interactive. It asks for three things: 1. **Application name** — how your app is labeled in Capxul. 2. **Allowed local origin** — the exact origin your dev server runs on. Defaults to `http://localhost:3000` (Next.js); use `http://localhost:5173` for Vite. Keys are origin-locked, so this must match exactly. 3. **Secure handoff file path** — where the results are written. Defaults to `~/.config/capxul/quickstart-handoffs/customer-nextjs-quickstart.md`. It then creates a developer application and a test publishable key against the already-deployed Capxul sandbox backend. It does not deploy anything of yours, and it **never prints the raw key to the terminal** — the key is written to the handoff file, and the terminal shows only the application id, key id, allowed origin, and Capxul site URL. ### What the handoff file contains [#what-the-handoff-file-contains] Open the handoff file and you have everything the quickstarts ask for: * **Publishable key** — browser-safe, origin-locked. This is the only value that belongs in a browser env var. * **Capxul site URL** — the backend your dev proxy or rewrites point at. * **Package versions** — the validated `@capxul/sdk` / `@capxul/sdk-react` versions to pin. Treat the file like a credential anyway: don't commit it, and don't paste backend secrets, private keys, or operator credentials into your frontend app alongside it. ### Origins are gated twice [#origins-are-gated-twice] The key's origin allowlist covers **bootstrap** only. The Capxul backend keeps separate trusted-origin and CORS allowlists that must also include your origin — the CLI reminds you of this when it finishes. If bootstrap succeeds but sign-in calls fail, this split is the usual suspect. Before deploying anywhere, get the production origin added to both gates — see the [Next.js validation checklist](https://capxul-sdk-docs.pages.dev/docs/getting-started/nextjs#validate). ## Send test funds [#send-test-funds] You do not need test money to prove sign-in. Reach for the faucet only when your product exercises balance or money-movement flows and you have set an account [`requirement`](https://capxul-sdk-docs.pages.dev/docs/getting-started/requirements-and-signers). ```bash npx @capxul/sandbox faucet send ``` The CLI asks for a recipient address and an amount (default `10` test USDX), confirms, and mints the funds on a public test network — it prints the transaction hash when done. It cannot select a live environment; test funds never touch real money. The current release asks for a raw EVM address because it sends funds directly to that address. Treat that as a temporary developer-tool detail — a future release is expected to be account-based (pick or create a Capxul account, then fund it). Do not design customer onboarding around collecting addresses by hand. ## Faucet funding never ships in a production frontend [#faucet-funding-never-ships-in-a-production-frontend] The faucet exists for developer validation in test environments, full stop. The same rule covers `useCapxulAccountFund` — it is a dev/test funding proof, not a product feature. If your product needs users to bring real money in, that is a platform capability question: check [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) for what is live before you design the flow. ## Next [#next] * [Quickstart](https://capxul-sdk-docs.pages.dev/docs/getting-started) — use the key in a fresh React app. * [Next.js](https://capxul-sdk-docs.pages.dev/docs/getting-started/nextjs) — use the key in a Next.js app. * [Requirements & signers](https://capxul-sdk-docs.pages.dev/docs/getting-started/requirements-and-signers) — when your app needs an account, not just a session. # Build the auth flow (https://capxul-sdk-docs.pages.dev/docs/guides/authentication) This guide takes you from the quickstart's single login page to a complete auth flow: send an OTP, verify it, route the user to the right screen based on their session, profile, and account-readiness state, hand off to onboarding, and sign out. It follows the same routing the reference app uses — OTP in, dashboard immediately, provisioning automatic. ## Before you start [#before-you-start] * You have finished [Getting started](https://capxul-sdk-docs.pages.dev/docs/getting-started): `CapxulProvider` is mounted with a working publishable key and the two route rewrites are in place. * If your product moves money, mount the provider with `requirement="deployed"` so the account lane runs automatically after sign-in. Auth-only apps can omit `requirement`. See [`CapxulProvider`](https://capxul-sdk-docs.pages.dev/docs/hooks/capxul-provider) and the [account lane](https://capxul-sdk-docs.pages.dev/docs/concepts/account-lane). ## The four states that drive routing [#the-four-states-that-drive-routing] Every screen decision in the flow derives from four reads: | Read | Hook | Values that matter | | ----------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------- | | SDK startup | [`useCapxul`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul) | `"bootstrapping"` / `"ready"` / `"error"` (with `retry`) | | Session | [`useCapxulSession`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-session) | `Session` or `null` (signed out) | | Profile | [`useCapxulProfile`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-profile) | `Profile` or `null` — `null` until onboarding completes | | Account readiness | [`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle) | `loading` / `settingUp` / `ready` / `failed` | The routing rule, in order: 1. Bootstrap not `ready` or session still loading → show a splash. 2. No session → login screen. 3. Session but no profile → onboarding (the user has an identity, but has never told you who they are). 4. Profile but lifecycle `settingUp` or `failed` → provisioning screen. 5. Lifecycle `ready` → the app. ## Send the OTP [#send-the-otp] [`useCapxulSignIn`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-in) sends the one-time code. Sending does not sign the user in — `useCapxulSession` stays `null` until the code is verified, and this mutation invalidates nothing. ```tsx "use client"; import { useState } from "react"; import { useCapxulSignIn, useCapxulVerifyOtp } from "@capxul/sdk-react"; export function LoginForm() { const signIn = useCapxulSignIn(); const verifyOtp = useCapxulVerifyOtp(); const [email, setEmail] = useState(""); const [code, setCode] = useState(""); const [otpSent, setOtpSent] = useState(false); const sendOtp = async () => { await signIn.mutateAsync({ email }); setOtpSent(true); }; const verify = async () => { await verifyOtp.mutateAsync({ email, code }); // Session, profile, and lifecycle queries refetch automatically — // the routing gate below takes over from here. }; return (
{ event.preventDefault(); void (otpSent ? verify() : sendOtp()); }} > setEmail(event.target.value)} /> {otpSent ? ( setCode(event.target.value)} /> ) : null} {signIn.error ?

{signIn.error.message}

: null} {verifyOtp.error ?

{verifyOtp.error.message}

: null}
); } ``` Two failure modes worth handling explicitly (both surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling)): * `RATE_LIMITED` on send — back off and retry; the reference app waits ten seconds and resends once. * `WRONG_STATE` on verify — the OTP flow was reset (for example the code expired and a new one must be sent). Flip your UI back to the email step. ## Verify creates the identity [#verify-creates-the-identity] [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp) is both "create user" and "log in". If the email has never signed in before, Capxul creates the user identity during **verification** — there is no separate `createUser` call. On success the hook invalidates the auth boundary (session, profile, account lifecycle, balance), so every state read in the routing table refetches in the background. A freshly created identity has **no profile** — `useCapxulProfile` returns `null` until onboarding completes. That is the signal to route to onboarding, not an error. ## Route on auth state [#route-on-auth-state] Fold the four reads into one gate and derive the route from it — no effects, no imperative redirects scattered across screens. This mirrors how the reference app routes (`apps/reference` in the platform repo). ```tsx "use client"; import { useCapxul, useCapxulAccountLifecycle, useCapxulProfile, useCapxulSession, } from "@capxul/sdk-react"; import type { CapxulError } from "@capxul/sdk"; export type AuthGateState = | { status: "loading" } | { status: "bootstrap-error"; error: CapxulError | null; retry: () => void } | { status: "signed-out" } | { status: "needs-onboarding" } | { status: "provisioning" } | { status: "setup-failed"; error: CapxulError | null; retry: () => Promise; isRetrying: boolean; } | { status: "ready" }; export function useAuthGate(): AuthGateState { const bootstrap = useCapxul(); const session = useCapxulSession(); const profile = useCapxulProfile(); const setup = useCapxulAccountLifecycle(); if (bootstrap.status === "error") { return { status: "bootstrap-error", error: bootstrap.error, retry: bootstrap.retry }; } if (bootstrap.status === "bootstrapping" || session.isLoading) { return { status: "loading" }; } if (session.data == null) { return { status: "signed-out" }; } if (profile.isLoading) { return { status: "loading" }; } if (profile.data === null) { return { status: "needs-onboarding" }; } if (setup.lifecycle.status === "failed") { return { status: "setup-failed", error: setup.error, retry: setup.retry, isRetrying: setup.isRetrying, }; } if (setup.lifecycle.status !== "ready") { return { status: "provisioning" }; } return { status: "ready" }; } ``` Then render (or navigate) from the gate: ```tsx const gate = useAuthGate(); switch (gate.status) { case "loading": return ; case "bootstrap-error": return ; case "signed-out": return ; case "needs-onboarding": return ; case "provisioning": case "setup-failed": return ; case "ready": return ; } ``` For bootstrap errors, always offer the `retry` from [`useCapxul`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul) — it re-runs the SDK bootstrap in place. Never rebuild the provider yourself. ## Complete onboarding [#complete-onboarding] Onboarding is the on-ramp: the user onboards **once**, as an individual or as an organization founder, and then acts within the resulting entity. The two completion hooks are the bridge from "signed in, no profile" to "profile saved, account lane running": * [`useCapxulCompletePersonalOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-personal-onboarding) — takes `displayName`, `country`, and an optional `withdrawalAddress`; returns `{ lifecycle }`. * [`useCapxulCompleteOrganizationOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-organization-onboarding) — takes `organizationName`, `handle`, `country`, and `ownerDisplayName`; creates the org too and returns `{ org, lifecycle }`. Call the mutation from the submit handler and route on the returned `lifecycle.status` — no `useEffect`: ```tsx "use client"; import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { useCapxulCompletePersonalOnboarding } from "@capxul/sdk-react"; export function PersonalOnboardingForm() { const navigate = useNavigate(); const completePersonal = useCapxulCompletePersonalOnboarding(); const [displayName, setDisplayName] = useState(""); const [country, setCountry] = useState(""); const handleSubmit = async () => { const { lifecycle } = await completePersonal.mutateAsync({ displayName: displayName.trim(), country: country.trim(), // ISO-3166, e.g. "GH" }); navigate(lifecycle.status === "ready" ? "/dashboard" : "/signup/provisioning"); }; return (
{ event.preventDefault(); void handleSubmit(); }} > setDisplayName(e.target.value)} /> setCountry(e.target.value)} placeholder="GH" /> {completePersonal.error ?

{completePersonal.error.message}

: null}
); } ``` On success both hooks invalidate the profile and account-lifecycle queries (the organization variant also invalidates the org list), so the routing gate sees the new state on its next render. ## Build the provisioning screen [#build-the-provisioning-screen] While the account lane provisions, `useCapxulAccountLifecycle` self-polls every two seconds. The screen is pure derivation — the query is the source of truth, the route is derived from it: ```tsx "use client"; import { Navigate } from "react-router-dom"; import { useCapxulAccountLifecycle } from "@capxul/sdk-react"; export function ProvisioningScreen() { const { lifecycle, isSettingUp, error, retry, isRetrying } = useCapxulAccountLifecycle(); if (lifecycle.status === "ready") { return ; } if (lifecycle.status === "failed") { return (

Setup didn't finish

{error?.message ?? "Account setup failed."}

); } const step = lifecycle.status === "settingUp" ? lifecycle.step : "preparing"; return (

Setting up your account…

{isSettingUp ? `Step: ${step}` : "Preparing…"}

); } ``` `lifecycle.step` is one of `"connecting"`, `"confirmingIdentity"`, `"registering"`, `"activating"`. On `failed`, `retry` re-runs setup and refetches the auth boundary on success. Once `ready`, gate money UI on `lifecycle.canTransact` — `ready` with `canTransact: false` means the account exists but activation has not finished. ## Sign out [#sign-out] [`useCapxulSignOut`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-out) ends the session and immediately resets the cached session, profile, account-lifecycle, and balance queries, so the routing gate lands back on `signed-out` without a flash of stale data: ```tsx const signOut = useCapxulSignOut(); ``` Other cached reads (org lists, payments) are not reset on sign-out — see [what clears when](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query) if you need a full cache wipe. ## What you end up with [#what-you-end-up-with] * `/login` sends and verifies OTP codes; verification creates the identity on first sign-in. * A single `useAuthGate` routes every visit: signed out → login, no profile → onboarding, profile but not ready → provisioning, ready → app. * Onboarding completion mutations move the user forward without effects; the provisioning screen advances itself by polling. * Sign-out drops the authenticated cache and lands back on login. # Handle errors (https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) Every failure the SDK surfaces — query errors, mutation rejections, raw client results, bootstrap failures — is one type: `CapxulError`. This guide shows where it appears, how to render and branch on it, and the full public code catalog. ## Before you start [#before-you-start] * You have hooks running against a working provider ([Getting started](https://capxul-sdk-docs.pages.dev/docs/getting-started)). ## The error type [#the-error-type] ```ts import { CapxulError, isCapxulError } from "@capxul/sdk"; import type { CapxulErrorCode, CapxulErrorDetails } from "@capxul/sdk"; ``` `CapxulError` extends `Error` with structured fields: | Field | Type | Meaning | | --------------- | ---------------------------------- | ------------------------------------------------------------------------------ | | `code` | `CapxulErrorCode` | Stable machine-readable code — the field to branch on | | `message` | `string` | Human-readable, safe to show to users | | `details` | `CapxulErrorDetails` (optional) | Code-specific structured context, e.g. `{ field, reason }` for `INVALID_INPUT` | | `correlationId` | `string` (optional) | Ties the failure to backend telemetry — include it in bug reports | | `cause` | `unknown` (standard `Error.cause`) | The underlying error, when one exists | Branch on `code`, never by parsing `message` — messages can change between releases; codes are the contract. Use `isCapxulError(value)` to narrow an unknown caught value. ## Errors from query hooks [#errors-from-query-hooks] Query hooks are typed `UseQueryResult`, so `error` is a `CapxulError` — no casting: ```tsx "use client"; import { useCapxulPayments } from "@capxul/sdk-react"; function PaymentsList() { const payments = useCapxulPayments(); if (payments.isError) { return

{payments.error.message}

; } // … } ``` Remember the provider's defaults: queries retry twice before `isError` goes `true` (see [TanStack Query integration](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query)). ## Errors from mutation hooks [#errors-from-mutation-hooks] Mutations surface the same error two ways: * `mutation.error` — for rendering inline, exactly like queries. * `mutateAsync` — **rejects** with the `CapxulError`, for imperative handling in submit handlers. (`mutate` does not throw; it routes the error to `mutation.error` and callbacks.) ```tsx "use client"; import { isCapxulError } from "@capxul/sdk"; import { useCapxulPay } from "@capxul/sdk-react"; function PayForm({ email }: { readonly email: string }) { const pay = useCapxulPay(); const handlePay = async () => { try { await pay.mutateAsync({ to: { kind: "email", email }, amount: { currency: "USD", value: "25.00", decimals: 6 }, }); } catch (cause) { if (isCapxulError(cause)) { switch (cause.code) { case "INSUFFICIENT_BALANCE": showTopUpPrompt(); return; case "RATE_LIMITED": { const retryAfterMs = cause.details?.retryAfterMs; scheduleRetry(typeof retryAfterMs === "number" ? retryAfterMs : 10_000); return; } default: showToast(cause.message); return; } } throw cause; } }; return ( ); } ``` Mutations never retry automatically (`retry: 0`) — a failed money movement is yours to re-drive deliberately. ## Errors from the raw client [#errors-from-the-raw-client] The core client never throws for operational failures. Every method returns a `CapxulResult` — a discriminated union: ```ts type CapxulResult = | { readonly ok: true; readonly value: T } | { readonly ok: false; readonly error: CapxulError }; ``` ```ts const result = await client.payments.list(); if (!result.ok) { console.error(result.error.code, result.error.message); return; } render(result.value); ``` When you wrap raw client calls in your own TanStack queries, throw `result.error` so the hook's `error` field carries the same type as the first-class hooks — see [Drop to the raw client](https://capxul-sdk-docs.pages.dev/docs/guides/raw-client). ## Bootstrap errors [#bootstrap-errors] If the provider's startup fails (bad key, unreachable backend, disallowed origin), no hook ever leaves its pending state. The failure lives on [`useCapxul`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul): ```tsx "use client"; import { useCapxul } from "@capxul/sdk-react"; function BootstrapGate({ children }: { children: React.ReactNode }) { const bootstrap = useCapxul(); if (bootstrap.status === "error") { return (

{bootstrap.error?.message ?? "Capxul failed to start."}

); } return children; } ``` `retry()` re-runs the bootstrap in place. Do not rebuild the provider or the client yourself. One related code you may see from mutations: firing a mutation while the provider is still bootstrapping rejects with `WRONG_STATE` (`details.currentState: "bootstrapping"`). Query hooks do not have this problem — they stay disabled until the client is ready. ## The error-code catalog [#the-error-code-catalog] The public catalog is a closed set — every SDK failure maps to one of these codes: | Code | When you see it | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NOT_AUTHENTICATED` | An authenticated call ran without a signed-in session. | | `EMAIL_DELIVERY_FAILED` | The OTP (or invite) email could not be sent. | | `PROFILE_NOT_FOUND` | No identity profile exists for the user. | | `SMART_ACCOUNT_MISSING` | The user's account has not been provisioned yet. | | `PLAYER_NOT_FOUND` | A provider-side user record was not found. | | `ACCOUNT_NOT_FOUND` | A provider-side account record was not found. | | `PROVIDER_ERROR` | An upstream provider call failed; `details.provider` / `details.operation` name it, and `details.reason: "timeout"` marks timeouts. | | `INVALID_INPUT` | A field failed validation; `details.field` / `details.reason`. | | `ENV_MISSING` | Required configuration is missing (`details.name`). | | `NOT_IMPLEMENTED` | The method exists on the surface but is not available in this release (`details.domain` / `details.method`). | | `VERIFICATION_REQUIRED` | The operation needs a verification step the user has not completed. | | `INSUFFICIENT_BALANCE` | Not enough funds; `details.asset` / `available` / `required`. | | `INVALID_RECIPIENT` | The recipient was rejected — e.g. a bare address passed to `pay` (`details.reason`). | | `ROLE_PERMISSION_DENIED` | An org role denied the spend; `details.reason` is one of `over_cap`, `daily_cap`, `not_member`, `disallowed_recipient`, `condition_violation`. Distinct from `INSUFFICIENT_BALANCE` — the money was there; the role's authority was not. | | `TRANSACTION_FAILED` | A money movement failed to execute (`details.operation`). | | `RATE_LIMITED` | Too many attempts; `details.retryAfterMs` when known. | | `NETWORK_ERROR` | A network failure interrupted the operation. | | `UNKNOWN` | Unclassified failure — the `cause` chain has more. | | `OTP_EXPIRED` | The verification code expired; request a new one. | | `SIGNER_REJECTED` | The signing step was rejected or unavailable. | | `CANCELLED` | The operation was aborted (e.g. an `AbortSignal` fired). | | `WRONG_STATE` | The method was called from a state where its precondition fails; `details.method` / `currentState` / `validStates`. | `CapxulErrorCode` is the exported union of exactly these strings, so an exhaustive `switch` over codes type-checks. ## Practical rules [#practical-rules] * **Show `error.message`.** It is written to be user-safe; you do not need a message map to ship. * **Branch on `error.code`**, and on `details` for code-specific data. * **Log `correlationId`** with any error you report — it is the join key to backend telemetry. * **Let queries retry, re-drive mutations yourself.** The defaults already encode this. # Work with organizations (https://capxul-sdk-docs.pages.dev/docs/guides/organizations) This guide covers the organization surface end to end: list the organizations the signed-in user belongs to, create a new one, switch which entity the UI is acting as, read an org's treasury, members, and roles, and invite a member by email with a role. ## Before you start [#before-you-start] * You have a working auth flow ([Build the auth flow](https://capxul-sdk-docs.pages.dev/docs/guides/authentication)) and a signed-in, onboarded user. * Read [Identity & orgs](https://capxul-sdk-docs.pages.dev/docs/concepts/identity-and-orgs) if you want the model behind entities, members, and roles — this guide only uses it. Everything below is entity-scoped and explicit: the SDK holds **no** global "acting as" state. Every org read and write names its `orgId`, either as a hook argument or through `client.org(orgId)`. Render org screens behind your auth gate (bootstrap `ready`, session present). Most org hooks expect a bootstrapped provider and throw if rendered while the SDK is still starting up — the gate from [Build the auth flow](https://capxul-sdk-docs.pages.dev/docs/guides/authentication) guarantees they never are. ## List organizations [#list-organizations] [`useCapxulOrgs`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs) lists the orgs the user belongs to. It is a session-scoped authenticated read — gate it on auth readiness so it does not fire before the session settles and surface a spurious `NOT_AUTHENTICATED`: ```tsx "use client"; import { useCapxulOrgs, useCapxulSession } from "@capxul/sdk-react"; export function OrgList() { const session = useCapxulSession(); const orgs = useCapxulOrgs({ enabled: session.data != null }); if (orgs.isLoading) return

Loading organizations…

; if (orgs.isError) return

{orgs.error.message}

; return (
    {(orgs.data ?? []).map((org) => (
  • {org.name} · @{org.handle} · your role: {org.role}
  • ))}
); } ``` Each `OrgView` carries the org's `id`, `name`, `handle`, the viewing member's own `role` label within that org, and the org `treasury` snapshot. ## Create an organization [#create-an-organization] [`useCapxulCreateOrg`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-create-org) binds to `client.createOrg(input)`. The input takes: * `name` — display name. * `handle` — globally unique, normalized slug (`^[a-z0-9-]{3,32}$`). * `template` — `"Solo"`, `"Startup"`, or `"Custom"`; seeds the initial role set. For `"Custom"`, pass your authored `roles` definitions. * `country` — optional. ```tsx "use client"; import { useCapxulCreateOrg } from "@capxul/sdk-react"; const createOrg = useCapxulCreateOrg(); const handleCreate = async () => { const org = await createOrg.mutateAsync({ name: "Acme Studio", handle: "acme-studio", template: "Startup", }); navigate(`/orgs/${org.id}`); }; ``` On success the hook invalidates the org list, so any mounted `useCapxulOrgs` refetches automatically. If the user is onboarding as an organization founder for the first time, use [`useCapxulCompleteOrganizationOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-organization-onboarding) instead — it saves the founder's identity profile and creates the org in one step. `useCapxulCreateOrg` is for adding further orgs to an existing identity. ## Switch the acting entity [#switch-the-acting-entity] "Acting as" (personal account vs. a specific org) is **your app's state**, not the SDK's. The idiomatic pattern — used by the reference app — is to derive the active entity from the route (`/dashboard` = personal, `/orgs/:orgId` = that org) and pass the derived `orgId` into the org hooks on those screens. [`useCapxulSwitchActingEntity`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-switch-acting-entity) is the stable mutation seam for the switch interaction. It carries no SDK side effect — the actual context switch is whatever your app does afterwards (usually navigation): ```tsx "use client"; import { useNavigate } from "react-router-dom"; import { useCapxulOrgs, useCapxulSwitchActingEntity } from "@capxul/sdk-react"; export function EntitySwitcher({ activeOrgId }: { activeOrgId?: string }) { const navigate = useNavigate(); const orgs = useCapxulOrgs(); const switchEntity = useCapxulSwitchActingEntity(); const handleChange = (value: string) => { if (value === "personal") { void switchEntity.mutateAsync(undefined).then(() => navigate("/dashboard")); return; } const org = orgs.data?.find((candidate) => candidate.id === value); if (org === undefined) return; void switchEntity.mutateAsync({ orgId: org.id }).then(() => navigate(`/orgs/${org.id}`)); }; return ( ); } ``` Because scoping is explicit per call, two screens can act as two different entities at the same time without stepping on each other's cache — each org's data lives under its own `["capxul", "org", orgId, …]` keys (see [TanStack Query integration](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query)). ## Read treasury, members, and roles [#read-treasury-members-and-roles] All three reads are parameterized by `orgId` and disabled until it is defined. `OrgId` is an opaque type you get from SDK data — `OrgView.id` from the org list, or `org.id` from a create/onboarding result. To turn a route param back into an `OrgId`, match it against the org list (`orgs.data?.find((org) => org.id === param)?.id`) rather than casting the raw string. ```tsx "use client"; import { useCapxulOrg, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, } from "@capxul/sdk-react"; import type { OrgId } from "@capxul/sdk"; export function OrgDetail({ orgId }: { orgId: OrgId }) { const org = useCapxulOrg(orgId); const treasury = useCapxulOrgTreasury(orgId); const members = useCapxulOrgMembers(orgId); const roles = useCapxulOrgRoles(orgId); if (org.isLoading || treasury.isLoading || members.isLoading) { return

Loading organization…

; } if (org.isError) return

{org.error.message}

; if (org.data == null) return

Organization not found.

; return ( <>

{org.data.name}

{treasury.data ? (

Treasury: {treasury.data.balance.value} {treasury.data.balance.currency} (available:{" "} {treasury.data.available.value})

) : null}
    {members.data?.map((member) => (
  • {member.email} · {member.role} · {member.status}
  • ))}
    {roles.data?.map((role) => (
  • {role.label}
  • ))}
); } ``` What each returns: * [`useCapxulOrgTreasury`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-treasury) — the org's single treasury `Account`: a pot of money with `balance` and `available`. Nothing else. * [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) — `MemberView[]`: `email`, `name`, `role` label, and a lifecycle `status` running `pending` (including the intermediate `pending_safe` / `pending_grant` steps) → `active`, with `revoked` / `expired` off-ramps. A `pending` member has been invited but has not finished joining yet. * [`useCapxulOrgRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-roles) — `RoleView[]`: each role's `label` and its `definition`, including optional spend caps (`perTx`, `perDay`, allowed recipients) and management permissions. Role labels are what you pass when inviting or assigning. ## Invite a member with a role [#invite-a-member-with-a-role] [`useCapxulInviteMember`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-invite-member) is scoped to an org and takes an email plus a role **label** (one of the labels from `useCapxulOrgRoles`): ```tsx "use client"; import { useCapxulInviteMember } from "@capxul/sdk-react"; import type { OrgId } from "@capxul/sdk"; export function InviteForm({ orgId }: { orgId: OrgId }) { const invite = useCapxulInviteMember(orgId); const handleInvite = async (email: string, role: string) => { const member = await invite.mutateAsync({ email, role }); // member.status is "pending" until the invitee joins. }; // …form UI… } ``` Email is the universal entry point: inviting an address that is not yet a Capxul user still works — it creates a `pending` membership that resolves when that person signs in for the first time. On success the hook invalidates the org's member list. To offboard or change someone's role later, use [`useCapxulRemoveMember`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-remove-member) and [`useCapxulAssignRole`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-assign-role); both are scoped the same way and also refresh the member list. ## What you end up with [#what-you-end-up-with] * An org list gated on auth, a create form that lands on the new org's page, and an entity switcher whose state lives in your router. * An org detail screen reading treasury, members, and roles for one explicit `orgId`. * An invite form that adds members by email with a role, including people who are not Capxul users yet. Org **spending** (treasury payments, payroll) is a separate surface — see [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) for what is buildable today. # Send money (https://capxul-sdk-docs.pages.dev/docs/guides/payments) This guide covers moving money out of a personal account: paying a typed recipient, what happens when that recipient is not validated yet, listing and reading payments, cancelling, cashing out to an external wallet, and paying out to a saved destination. ## Before you start [#before-you-start] * Auth and onboarding are done ([Build the auth flow](https://capxul-sdk-docs.pages.dev/docs/guides/authentication)) and the account lifecycle is `ready` with `canTransact: true`. * The account holds test funds (see [test keys and the faucet](https://capxul-sdk-docs.pages.dev/docs/getting-started/test-keys)). * Skim [Money & accounts](https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts) for the model: Money, Account, Payment, Commitment. * Check [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) — the payments surface is graded **Partial**/**SDK-ready**, and this guide flags each constraint where it bites. Amounts everywhere are a `PaymentMoney`: `currency`, decimal string `value`, and `decimals` — for example `{ currency: "USD", value: "25.00", decimals: 6 }`. Never minor units, never a token amount. The current alpha settles at six-decimal precision — pass `decimals: 6`; other values are rejected with `INVALID_INPUT`. ## Pay a typed recipient [#pay-a-typed-recipient] [`useCapxulPay`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-pay) sends money to a **typed** recipient reference — a handle, an email, an organization handle, or a saved payee id: ```tsx "use client"; import { useCapxulPay } from "@capxul/sdk-react"; export function PayButton() { const pay = useCapxulPay(); const handlePay = async () => { const payment = await pay.mutateAsync({ to: { kind: "email", email: "dana@example.com" }, amount: { currency: "USD", value: "25.00", decimals: 6 }, }); // Check payment.status: an instant send to a validated recipient // settles; an unvalidated recipient escrows as "pending_claim". }; return ( ); } ``` The `to` reference is one of: | `kind` | Fields | Pays | | ---------------- | -------- | ---------------------------------------------------- | | `"handle"` | `handle` | a Capxul user by handle | | `"email"` | `email` | anyone by email — including people not on Capxul yet | | `"organization"` | `handle` | an org by its handle | | `"payee"` | `id` | a saved payee | Two constraints to design around (both by design, both graded on [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status)): * **Bare addresses are rejected.** `pay` never accepts a raw EVM address — that path fails with `INVALID_RECIPIENT`. Cash-out to an external wallet is its own lane ([withdraw](#withdraw-to-an-external-wallet)). * **`pay` is personal-actor only today.** Passing an organization `actor` fails with `NOT_IMPLEMENTED` — org treasury spending goes through the org surface, not `payments.pay`. Optionally attach a `paymentType` (`"invoice"`, `"payroll"`, `"reimbursement"`), a payment `document`, or a `timing` clause. Omitting `timing` means an instant send; a `scheduled` or `stream` clause always creates a Commitment (below). ## Unvalidated recipients become Commitments [#unvalidated-recipients-become-commitments] One rule explains most "why is this payment not settled?" moments: an **instant, irreversible send is only permitted to a validated recipient**. Paying anyone not yet validated — a fresh email, an unclaimed handle — does not fail. It escrows the money in a recoverable **Commitment** instead: * The `Payment` comes back with `status: "pending_claim"`. * The recipient claims it once they are on Capxul; `availableToClaim` on the payment tracks what they can take. * Until claimed, the sender can cancel (reclaiming the unvested remainder) or redirect it to another validated recipient. The SDK enforces this; it is not a bug to work around. The full model is in [Money & accounts](https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts). ## List payments and read one [#list-payments-and-read-one] [`useCapxulPayments`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payments) lists the payment ledger (most recent first); [`useCapxulPayment`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payment) reads one by id with its live release state: ```tsx "use client"; import { useCapxulPayment, useCapxulPayments } from "@capxul/sdk-react"; function PaymentsList() { const payments = useCapxulPayments(); if (payments.isLoading) return

Loading payments…

; if (payments.isError) return

{payments.error.message}

; return (
    {payments.data?.map((payment) => (
  • {payment.recipient.label} · {payment.amount.value} {payment.amount.currency} ·{" "} {payment.status}
  • ))}
); } function PaymentDetail({ paymentId }: { paymentId: string }) { const payment = useCapxulPayment(paymentId); if (payment.data == null) return null; return (

Released {payment.data.released.value} / {payment.data.amount.value} — claimable:{" "} {payment.data.availableToClaim.value}

); } ``` A `Payment` is leak-safe by construction: recipient `kind` + `label`, amount, `status` (`pending`, `submitted`, `pending_claim`, `scheduled`, `streaming`, `settled`, `cancelled`, `redirected`, `expired`, `failed`), `timing`, `released` / `availableToClaim`, and document references. There is no transaction hash and no address on it. Money mutations invalidate the payments list and detail automatically — see [TanStack Query integration](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cancel a payment [#cancel-a-payment] Commitments are cancellable by their creator before they are fully claimed: cancelling returns the unvested remainder to you, while anything already vested stays claimable by the recipient. [`useCapxulCancelPayment`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-cancel-payment) is the mutation surface for this: ```tsx const cancelPayment = useCapxulCancelPayment(); await cancelPayment.mutateAsync(payment.id); ``` In the current published alpha, `client.payments.cancel` is a stub: it always rejects with a `NOT_IMPLEMENTED` `CapxulError`. The Commitment cancel lane exists on the platform, but it is not wired through the browser SDK client yet. Ship the button behind a feature gate or handle the error explicitly, and check [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) before relying on it. ## Withdraw to an external wallet [#withdraw-to-an-external-wallet] [`useCapxulWithdraw`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-withdraw) is the cash-out lane: it sends funds from the user's account to an **external wallet**. This is the one place the SDK accepts a raw `0x` address, and it requires a signed Withdrawal document that carries the audit-visible destination: ```tsx "use client"; import { useCapxulWithdraw } from "@capxul/sdk-react"; export function WithdrawButton({ destination }: { readonly destination: string }) { const withdraw = useCapxulWithdraw(); const handleWithdraw = async () => { const dest = destination.toLowerCase(); const payment = await withdraw.mutateAsync({ to: dest, amount: { currency: "USD", value: "5.00", decimals: 6 }, document: { protocol: "capxul.payment-document", version: 1, domain: { name: "CapxulPayments", version: "1", chainId: 84532, verifyingContract: "0xe740ea65521edc9bef0ea75d30b5334711db99f4", }, primaryType: "Withdrawal", message: { kind: 4, reference: "WD-001", amount: "5000000", // minor units as a string ($5.00 at 6 decimals) currency: "USD", // fiat code, not a token symbol decimals: 6, destChain: 84532, destAddress: dest, // must match `to`, lowercased settledAt: Date.now(), provider: "", // "" means a direct send note: "", }, }, }); // payment.recipient.label is a redacted form like "0x1234…abcd" — // the full external address lives only on the withdrawal document. }; return ( ); } ``` Notes on the document: * The struct is strict — exactly these fields, no extras. `provider` and `note` are required strings (`""` when absent). * The `domain` values (`chainId`, `verifyingContract`) are environment configuration from your Capxul handoff; the values above are the test environment's. * The returned `Payment` never exposes the full destination address — its recipient label is redacted. Withdraw is graded **SDK-ready** on [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status): published and tested, but not yet proven by a product surface end to end. Build against it with that expectation. ## Pay out to a saved destination [#pay-out-to-a-saved-destination] Where withdraw takes a raw address every time, **payout** settles to a saved destination selected by opaque id — the user never re-enters wallet details. First save a destination with [`useCapxulAddDestination`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-add-destination), then list with [`useCapxulDestinations`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-destinations) and pay out with [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout): ```tsx "use client"; import { useCapxulAddDestination, useCapxulDestinations, useCapxulPayout, } from "@capxul/sdk-react"; export function PayoutPanel() { const addDestination = useCapxulAddDestination(); const destinations = useCapxulDestinations({ actor: { kind: "personal" } }); const payout = useCapxulPayout(); // 1. Save a wallet destination once. const saveWallet = async (address: string) => { await addDestination.mutateAsync({ target: { kind: "email", email: "dana@example.com" }, kind: "external_account", label: "Dana's wallet", payload: { network: "base-sepolia", address: address.toLowerCase() }, }); }; // 2. Pay out to it by id — no address re-entry. const payOut = async (destinationId: string) => { await payout.mutateAsync({ destinationId, amount: { currency: "USD", value: "10.00", decimals: 6 }, }); }; return (
    {destinations.data?.map((destination) => (
  • {destination.label ?? destination.id}
  • ))}
); } ``` **Payout settles wallet destinations only today.** Bank and mobile-money destination *metadata* can be stored (`kind: "bank_account"` / `"mobile_money"`), which is useful for building the destination book ahead of time — but a payout to them is rejected. Bank and mobile-money settlement is roadmap, not capability. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## What you end up with [#what-you-end-up-with] * A pay flow that sends to handles, emails, orgs, and payees — and degrades safely to escrowed Commitments for recipients who are not validated yet. * A payments screen listing the ledger and showing per-payment release state. * A cash-out path (withdraw) and a repeatable payout path over saved wallet destinations, each honest about today's grading. # Drop to the raw client (https://capxul-sdk-docs.pages.dev/docs/guides/raw-client) The hooks cover most of the client surface, but not all of it. When a method exists on `CapxulClient` without a matching hook, you can bind it to TanStack Query yourself in a few lines — the same pattern every first-class hook uses internally. ## Before you start [#before-you-start] * You know the hook catalog ([hooks reference](https://capxul-sdk-docs.pages.dev/docs/hooks)) — this guide is for methods that are **not** on it. * You have read [Handle errors](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) for the `CapxulResult` shape, and [TanStack Query integration](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query) for the key namespace. ## When not to do this [#when-not-to-do-this] If a first-class hook exists, use it. The hooks are not just bindings — they carry the cache contract: documented query keys, automatic invalidation on money and auth mutations, and telemetry on failures. A hand-rolled `useQuery` over `client.org(orgId).members()` works, but it will not be invalidated when an invite mutation succeeds, because the SDK invalidates *its* key, not yours. Check the [hooks reference](https://capxul-sdk-docs.pages.dev/docs/hooks) and the [client reference](https://capxul-sdk-docs.pages.dev/docs/client) first; drop down only for the gap. ## The recipe [#the-recipe] Four rules make a raw-client query behave like a first-class hook: 1. **Get the client with [`useCapxulClientOrNull`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul-client-or-null).** It returns `null` while `CapxulProvider` is still bootstrapping instead of throwing. 2. **Gate the query with `enabled: client !== null`** (plus any parameter gates), so it sits in `isPending` until the client resolves. 3. **Unwrap the `CapxulResult`**: `if (!result.ok) throw result.error;` — the thrown `CapxulError` lands in the query's `error` field with the same type every other hook uses. 4. **Key under `["capxul", …]`.** The provider treats the `"capxul"` prefix as client-scoped: when you bring your own QueryClient, re-bootstrap removes exactly those keys. Keys outside the namespace would survive a client swap and leak the previous session's data. Pick a segment the [SDK catalog](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query) does not already use. ## Worked example: org profile [#worked-example-org-profile] `client.org(orgId).profile.get()` returns the org's public profile (display name, handle) — a read with no first-class hook today. The binding below is exactly how the SDK's own hooks are built, applied to a method that does not have one yet: ```tsx "use client"; import { useQuery } from "@tanstack/react-query"; import { useCapxulClientOrNull } from "@capxul/sdk-react"; import type { OrgId } from "@capxul/sdk"; export function useOrgProfile(orgId: OrgId | undefined) { const client = useCapxulClientOrNull(); return useQuery({ // "profile" is not a segment the SDK uses under ["capxul", "org", orgId]. queryKey: ["capxul", "org", orgId ?? "pending", "profile"], queryFn: async () => { const result = await client!.org(orgId!).profile.get(); if (!result.ok) throw result.error; return result.value; }, enabled: client !== null && orgId !== undefined, }); } ``` Consuming it looks exactly like any SDK hook, including the typed error: ```tsx function OrgProfileHeader({ orgId }: { readonly orgId: OrgId }) { const profile = useOrgProfile(orgId); if (profile.isLoading) return

Loading…

; if (profile.isError) return

{profile.error.message}

; return (

{profile.data.displayName ?? "Unnamed org"} {profile.data.handle ? · @{profile.data.handle} : null}

); } ``` The non-null assertions are safe because `enabled` guarantees the query function never runs while `client` or `orgId` is missing — the same invariant the built-in hooks rely on. Note the `"pending"` placeholder while `orgId` is `undefined`: the query is disabled in that state, so nothing fetches under the placeholder key. This mirrors the SDK's own parameterized keys. ## Mutations and invalidation [#mutations-and-invalidation] The same unwrap works in a `useMutation`, and since your query lives in the shared cache, invalidate it yourself on success: ```tsx "use client"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useCapxulClientOrNull } from "@capxul/sdk-react"; import type { OrgId } from "@capxul/sdk"; export function useDeployOrgRolesRaw(orgId: OrgId | undefined) { const client = useCapxulClientOrNull(); const queryClient = useQueryClient(); return useMutation({ mutationFn: async () => { if (client === null || orgId === undefined) { throw new Error("client not ready"); } const result = await client.org(orgId).deployRoles(); if (!result.ok) throw result.error; return result.value; }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["capxul", "org", orgId, "roles"], }); }, }); } ``` (This particular verb has a first-class hook — `useCapxulOrgDeployRoles` — shown here only because a mutation example needs a real method. The recipe is what transfers.) ## Cancellation [#cancellation] Every client method accepts an optional `{ signal }` as its last argument. TanStack passes an `AbortSignal` to query functions; thread it through so unmounts abort in-flight reads: ```ts queryFn: async ({ signal }) => { const result = await client!.org(orgId!).profile.get({ signal }); if (!result.ok) throw result.error; return result.value; }, ``` An aborted call resolves as `{ ok: false }` with a `CANCELLED` error rather than hanging. ## What you end up with [#what-you-end-up-with] * A custom hook over any client method, indistinguishable from a first-class hook to its consumers: pending until bootstrap, `CapxulError` in `error`, cleaned up on re-bootstrap. * A clear rule for when to delete it: as soon as the first-class hook ships, switch and pick up its cache contract for free. # TanStack Query integration (https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query) Every `@capxul/sdk-react` hook is a thin binding over TanStack Query: query hooks return `UseQueryResult`, mutation hooks return `UseMutationResult`, and all reactivity — caching, refetching, invalidation — is TanStack's. This guide explains the pieces you interact with when your app shares the cache with the SDK. ## Before you start [#before-you-start] * `@tanstack/react-query` is installed (it is a peer dependency — the quickstart installs it). * `CapxulProvider` is mounted; you do not mount a `QueryClientProvider` yourself unless you bring your own client (below). ## The provider owns the QueryClient [#the-provider-owns-the-queryclient] `CapxulProvider` creates a `QueryClient` and renders its own `QueryClientProvider`, so the hooks work with zero TanStack setup. The defaults it creates: ```ts new QueryClient({ defaultOptions: { queries: { retry: 2, staleTime: 30_000 }, mutations: { retry: 0 }, }, }); ``` * Queries retry twice and are considered fresh for 30 seconds. * Mutations never retry — money movements must not be silently re-fired. Because the provider renders a real `QueryClientProvider`, your own `useQuery`/`useMutation` calls inside the tree resolve the same client and share its cache. ## Bring your own QueryClient [#bring-your-own-queryclient] Pass `queryClient` to the provider to share a client you configure yourself (your own defaults, devtools, persistence): ```tsx "use client"; import { QueryClient } from "@tanstack/react-query"; import { CapxulProvider } from "@capxul/sdk-react"; const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 1 } }, }); export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` The client is **pinned at mount**: swapping the `queryClient` prop later is ignored. Create it once (module scope or `useState` initializer) and leave it. Ownership also changes cleanup behavior — see [what clears when](#what-clears-on-sign-out-and-re-bootstrap). ## The key namespace [#the-key-namespace] Every SDK query key is an array whose first element is the literal `"capxul"`. The catalog is hierarchical; representative shapes: | Data | Key | | -------------------------------------------------- | -------------------------------------------------------------- | | Session | `["capxul", "session"]` | | Profile | `["capxul", "profile"]` | | Account lifecycle | `["capxul", "accountLifecycle"]` | | Personal balance | `["capxul", "accountBalance"]` | | Org list | `["capxul", "orgs"]` | | One org | `["capxul", "org", orgId]` | | Org members / roles / treasury | `["capxul", "org", orgId, "members" \| "roles" \| "treasury"]` | | Payments list | `["capxul", "payments"]` | | One payment | `["capxul", "payments", paymentId]` | | Actor-scoped reads (address book, inbox, insights) | `["capxul", "actor", "account" \| "org", …]` | | Destinations | `["capxul", "destinations", …]` | Parameterized keys use the placeholder `"pending"` while their parameter is still `undefined` (the query is disabled in that state, so nothing fetches under a placeholder key). The catalog object itself is internal — the stable contract is the `"capxul"` prefix plus the exact key documented on each hook's reference page (under "Query key"). If you [drop to the raw client](https://capxul-sdk-docs.pages.dev/docs/guides/raw-client), put your custom keys under the same prefix. ## Automatic invalidation [#automatic-invalidation] You should rarely invalidate SDK data yourself. Two families of mutations do it for you: **Money mutations** — `useCapxulPay`, `useCapxulPayout`, `useCapxulWithdraw` (and `useCapxulCancelPayment` when it lands) invalidate, on success: * the payments list and the affected payment's detail key, * the acting entity's insights summary and history, * the personal balance — or, for an org actor, that org's account and treasury keys, * the actor's address book (for `pay`, which can create a new counterparty). **Auth boundary changes**: * `useCapxulVerifyOtp` (and the account-lifecycle `retry`) **invalidates** session, profile, account lifecycle, and balance — a background refetch, so mounted screens update without hard resets. * `useCapxulSignOut` **resets** those same keys after cancelling in-flight balance fetches — cached authenticated rows are dropped immediately, not refetched. * The onboarding completion hooks invalidate profile and account lifecycle (the organization variant also invalidates the org list). Org mutations invalidate their own slices: `useCapxulCreateOrg` → the org list; `useCapxulInviteMember` / `useCapxulRemoveMember` / `useCapxulAssignRole` → that org's member list. Each hook's reference page documents its exact cache behavior. ## Manual invalidation [#manual-invalidation] For anything the automatic rules do not cover (for example a server-driven change you learn about out of band), use `useQueryClient` from `@tanstack/react-query`. To target every Capxul query, predicate on the first key element: ```tsx "use client"; import { useQueryClient } from "@tanstack/react-query"; const queryClient = useQueryClient(); // Refetch everything the SDK has cached: await queryClient.invalidateQueries({ predicate: (query) => query.queryKey[0] === "capxul", }); // Or one slice, using the documented key shape: await queryClient.invalidateQueries({ queryKey: ["capxul", "payments"] }); ``` `invalidateQueries` with a key prefix matches hierarchically — `["capxul", "payments"]` invalidates the list and every `["capxul", "payments", paymentId]` detail under it. ## What clears on sign-out and re-bootstrap [#what-clears-on-sign-out-and-re-bootstrap] Two different events, two different scopes: **Sign-out** resets exactly the auth boundary: session, profile, account lifecycle, and balance. Other `capxul` keys (org lists, payments, address book) stay cached; their next fetch runs against the signed-out session and fails with `NOT_AUTHENTICATED`. If your UX needs a hard wipe of all Capxul data at sign-out, remove by predicate after the mutation succeeds: ```tsx const signOut = useCapxulSignOut(); const queryClient = useQueryClient(); await signOut.mutateAsync(); queryClient.removeQueries({ predicate: (query) => query.queryKey[0] === "capxul", }); ``` **Re-bootstrap** — the provider replacing its client (a `retry` after a bootstrap error, a `publishableKey` change, unmount) — clears client-scoped data so nothing from the previous client leaks into the next one: * Provider-owned QueryClient: the provider calls `queryClient.clear()` — everything goes, including your app's own queries on that client. * Your own QueryClient: the provider removes only queries whose `queryKey[0] === "capxul"` — your app's queries survive. That asymmetry is the main reason to bring your own QueryClient once your app caches non-Capxul data in the same tree. # Actor scopes (https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) A **`CapxulActorScope`** names who is acting: the signed-in user's personal account, or one organization. Four hook families operate on relationship state that exists for both kinds of actor — the [Address book](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book), [Payment requests](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests), [Inbox](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-inbox), and [Insights](https://capxul-sdk-docs.pages.dev/docs/hooks/insights/use-capxul-insights-summary) families — so instead of parallel personal/org hook sets, every hook in those families takes a scope as its **first argument**. The same component works unchanged whether it renders for a person or for an org. ## Import [#import] ```ts import { capxulAccountScope, capxulOrgScope, type CapxulActorScope, } from "@capxul/sdk-react"; ``` ## The scope type and helpers [#the-scope-type-and-helpers] ```ts type CapxulActorScope = | { readonly kind: "account" } | { readonly kind: "org"; readonly orgId: string }; ``` * `capxulAccountScope` — the shared `{ kind: "account" }` constant for the signed-in user's personal account. * `capxulOrgScope(orgId)` — builds `{ kind: "org", orgId }`. It is undefined-safe: while `orgId` is `undefined` (for example, still loading from `useCapxulOrgs`) it returns `undefined` instead of a broken scope, so you can pass `capxulOrgScope(orgs.data?.[0]?.orgId)` straight through. The core SDK exposes the same shape as `ActorScopeRef`; the React scope maps onto it one-to-one. ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, capxulOrgScope, useCapxulInbox, useCapxulOrgs, type CapxulActorScope, } from "@capxul/sdk-react"; function InboxBadge({ actor }: { actor: CapxulActorScope | undefined }) { const inbox = useCapxulInbox(actor); const open = inbox.data?.filter((item) => item.status === "open") ?? []; return {open.length}; } function WorkspaceNav() { const orgs = useCapxulOrgs(); return ( ); } ``` `InboxBadge` never knows which kind of actor it renders for — that is the point of the scope argument. ## How hooks treat the scope [#how-hooks-treat-the-scope] **Query hooks** (`useCapxulAddressBook`, `useCapxulRequests`, `useCapxulInbox`, `useCapxulInsightsSummary`, …) accept `CapxulActorScope | undefined` and stay disabled while the scope is `undefined`. That makes org ids from other queries safe to pass directly — no manual `enabled` gating. **Mutation hooks** (`useCapxulAddAddressBookEntry`, `useCapxulIssueRequest`, `useCapxulApproveInboxRequest`, …) default the scope to `capxulAccountScope` when it is omitted. The mutation scope is a default parameter, so passing `undefined` explicitly also falls back to the personal scope — and `capxulOrgScope(orgId)` returns `undefined` while `orgId` is still loading. Keep org-scoped actions disabled until the org id is present, or the mutation will act on the personal account. ## Query keys are scoped per actor [#query-keys-are-scoped-per-actor] Every query in these families is namespaced under the actor: ```ts ["capxul", "actor", "account", ...] // personal scope ["capxul", "actor", "org", orgId, ...] // one organization ["capxul", "actor", "pending", ...] // scope not known yet (query disabled) ``` Caches never bleed across actors: approving a request as an org invalidates that org's inbox, requests, and insights — your personal queries (and other orgs') are untouched. Each hook page documents its exact key. ## Client mapping [#client-mapping] On the core SDK the scope picks which actor bundle a call routes to: | Scope | Client surface | | ----------------------- | --------------------- | | `capxulAccountScope` | `client.account.*` | | `capxulOrgScope(orgId)` | `client.org(orgId).*` | Both bundles expose the same relationship surface — `addressBook`, `requests`, `inbox`, and `insights` — see the [client reference](https://capxul-sdk-docs.pages.dev/docs/client). # CapxulProvider (https://capxul-sdk-docs.pages.dev/docs/hooks/capxul-provider) Wraps your app once and makes every `@capxul/sdk-react` hook work beneath it. The provider owns the client lifecycle and the TanStack Query cache — read bootstrap progress with [`useCapxul`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul), and reach the raw client (rarely needed) with [`useCapxulClientOrNull`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul-client-or-null). ## Import [#import] ```ts import { CapxulProvider } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { CapxulProvider } from "@capxul/sdk-react"; export function App() { return ( ); } ``` That is the whole setup for a browser app: one publishable key, no other wiring. You do not call `createCapxulClient`, mount a `QueryClientProvider`, or build a signer module — the provider does all of it. ## Modes [#modes] The provider has two mutually exclusive modes, selected by which prop you pass. Supply exactly one of `publishableKey` or `client`; passing both or neither throws at render time. ### Bootstrap mode (`publishableKey`) [#bootstrap-mode-publishablekey] The path for browser apps. The provider runs the async `createCapxulClient({ publishableKey, requirement, signer })` bootstrap itself, exposes progress through [`useCapxul`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul), and closes the client on unmount. Changing `publishableKey`, `requirement`, or `signer` — or calling `retry()` from `useCapxul` — re-bootstraps: the old client is closed and a new one is created. While the bootstrap is in flight, data hooks simply sit in `isPending`. A plain app needs no bootstrap awareness at all; a splash screen is opt-in via `useCapxul`. ### Injected client mode (`client`) [#injected-client-mode-client] The path for Node and server consumers that bootstrap before React, and for test harnesses. You build the client yourself with `createCapxulClient` from `@capxul/sdk` and hand it in; the provider is `ready` immediately and never closes that client — its lifecycle stays with you. ```tsx import { createCapxulClient } from "@capxul/sdk"; import { CapxulProvider } from "@capxul/sdk-react"; const result = await createCapxulClient({ publishableKey: process.env.CAPXUL_PUBLISHABLE_KEY!, }); if (!result.ok) throw result.error; function Root() { return ( ); } ``` `requirement` and `signer` are not accepted in this mode — they are inputs to the bootstrap you already ran. ## Props [#props] The exported prop type is a union of the two modes: ```ts import { type CapxulProviderProps } from "@capxul/sdk-react"; ``` ### publishableKey [#publishablekey] `string` Selects bootstrap mode. The provider passes it to `createCapxulClient` and manages the resulting client. Mutually exclusive with `client`. ### client [#client] `CapxulClient` Selects injected client mode: a pre-built client whose lifecycle stays with the caller. Mutually exclusive with `publishableKey`. Swapping the prop to a different client instance clears the capxul-scoped queries so no data leaks across clients. ### requirement [#requirement] `AccountRequirement | undefined` — bootstrap mode only. Default `"none"`. The init-time account readiness target. When it is not `"none"`, account setup starts automatically after sign-in — your app only calls `useCapxulVerifyOtp` and reads progress with [`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle). * `"none"` (default) — no readiness target; nothing is provisioned automatically. * `"counterfactual"` — the Account's address is reserved so money can be addressed to it, without full setup. * `"deployed"` — the Account is fully set up and ready to move money. ### signer [#signer] `CapxulSigner | undefined` — bootstrap mode only. An optional consumer-held signer for the deploy lane. Browser apps with `requirement: "deployed"` **omit it** — the SDK resolves what it needs from bootstrap and wires the embedded signer automatically. Pass one only when your app holds its own signer (for example a script or server flow that deploys with its own key material). ### queryClient [#queryclient] `QueryClient | undefined` Bring your own TanStack `QueryClient` if your app already has one; otherwise the provider creates and owns one. The value is **pinned at mount** — a later `queryClient` prop swap is ignored, so pass it once or not at all. See [Query cache ownership](#query-cache-ownership) for how ownership changes re-bootstrap behavior. ### children [#children] `ReactNode` Your app tree. Every `useCapxul*` hook must render beneath the provider. ## Query cache ownership [#query-cache-ownership] The provider renders the `QueryClientProvider` itself — consumers never mount one for Capxul hooks. When the provider creates the `QueryClient`, it applies these defaults: * queries: `retry: 2`, `staleTime: 30_000` (30 seconds) * mutations: `retry: 0` A `queryClient` you pass in keeps its own defaults — the provider does not modify it. On re-bootstrap (prop change or `retry()`) and whenever the client instance changes, stale data from the previous client is dropped: * provider-owned `QueryClient` — the entire cache is cleared; * your own `QueryClient` — only queries whose key starts with `"capxul"` are removed, so the rest of your app's cache is untouched. This is why custom queries built on [`useCapxulClientOrNull`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul-client-or-null) should use `["capxul", ...]` keys. ## Errors [#errors] Passing both `publishableKey` and `client`, or neither, throws at render time: ``` CapxulProvider requires exactly one of `publishableKey` or `client` ``` A failed bootstrap does **not** throw. The provider moves to `status: "error"` and exposes the [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) plus a `retry()` function through [`useCapxul`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul); data hooks stay in `isPending` until a retry succeeds. # Hooks (https://capxul-sdk-docs.pages.dev/docs/hooks) `@capxul/sdk-react` exposes the Capxul platform as React hooks. Every data hook is TanStack Query under the hood: queries return `UseQueryResult`, mutations return `UseMutationResult`, and each hook exports its concrete `UseCapxul*Return` alias. (A few hooks expose a richer shape — their pages state it exactly.) All hooks must render beneath a [`CapxulProvider`](https://capxul-sdk-docs.pages.dev/docs/hooks/capxul-provider); until its bootstrap resolves, queries simply sit in `isPending`. ## Provider & bootstrap [#provider--bootstrap] * [`CapxulProvider`](https://capxul-sdk-docs.pages.dev/docs/hooks/capxul-provider) — the root provider; owns the client bootstrap and the TanStack Query cache. * [`useCapxul`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul) — bootstrap `status` / `error` / `retry`, for opt-in splash and error UI. * [`useCapxulClientOrNull`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul-client-or-null) — advanced escape hatch: the raw `CapxulClient`, or `null` while bootstrapping. * [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) — concept: scoping address book, requests, inbox, and insights hooks to the personal Account or an Organization. ## Auth & session [#auth--session] * [`useCapxulSignIn`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-in) — start email OTP sign-in: send the one-time code. * [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp) — verify the code and establish the session. * [`useCapxulSignOut`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-out) — end the session and invalidate the auth boundary. * [`useCapxulSession`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-session) — the current `Session`, or `null` when signed out. * [`useCapxulProfile`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-profile) — the signed-in user's `Profile`, or `null`. * [`useCapxulCurrentUser`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-current-user) — the signed-in user's aggregated `CurrentUserContext`. ## Account [#account] * [`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle) — post-sign-in Account setup: progress, readiness, and retry. * [`useCapxulAccountBalance`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance) — the personal Account's balance and available balance. * [`useCapxulAccountFund`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-fund) — fund the personal Account with an amount of `Money`. ## Sub-accounts [#sub-accounts] * [`useCapxulSubAccountsList`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-accounts-list) — list an Account's sub-accounts. * [`useCapxulSubAccountCreate`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-account-create) — create a sub-account. * [`useCapxulSubAccountRename`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-account-rename) — rename a sub-account. * [`useCapxulSubAccountDelete`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-account-delete) — delete a sub-account. * [`useCapxulTransfer`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-transfer) — move money between an Account and its sub-accounts. ## Payments [#payments] * [`useCapxulPay`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-pay) — pay a validated recipient; returns the recorded `Payment`. * [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout) — send a payout to a saved destination. * [`useCapxulWithdraw`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-withdraw) — withdraw to an external wallet. * [`useCapxulCancelPayment`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-cancel-payment) — cancel a payment by id. * [`useCapxulPayments`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payments) — list the Payments ledger. * [`useCapxulPayment`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payment) — a single `Payment` by id, or `null`. ## Destinations [#destinations] * [`useCapxulDestinations`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-destinations) — list saved destinations. * [`useCapxulAddDestination`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-add-destination) — save a new destination. * [`useCapxulRemoveDestination`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-remove-destination) — remove a saved destination. ## Activity [#activity] * [`useCapxulActivity`](https://capxul-sdk-docs.pages.dev/docs/hooks/activity/use-capxul-activity) — the paginated activity feed (`ActivityPage`). ## Offramp [#offramp] * [`useCapxulOfframpQuote`](https://capxul-sdk-docs.pages.dev/docs/hooks/offramp/use-capxul-offramp-quote) — quote an offramp for a given input. * [`useCapxulOfframpStatus`](https://capxul-sdk-docs.pages.dev/docs/hooks/offramp/use-capxul-offramp-status) — the status of an offramp by id. ## Address book [#address-book] Actor-scoped — see [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). * [`useCapxulAddressBook`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book) — list the scope's address book entries. * [`useCapxulAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book-entry) — a single entry by id, or `null`. * [`useCapxulAddAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-add-address-book-entry) — add an entry. * [`useCapxulHideAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-hide-address-book-entry) — hide an entry. * [`useCapxulUnhideAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-unhide-address-book-entry) — unhide a hidden entry. * [`useCapxulLabelAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-label-address-book-entry) — set an entry's label. ## Payment requests [#payment-requests] Actor-scoped — see [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). * [`useCapxulRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests) — list the scope's issued payment requests. * [`useCapxulRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-request) — a single request by id, or `null`. * [`useCapxulIssueRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-issue-request) — issue a new payment request. * [`useCapxulCancelRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-cancel-request) — cancel a request by id. * [`useCapxulReconcileRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-reconcile-requests) — reconcile receivables: paid / exception status per request. ## Inbox [#inbox] Actor-scoped — see [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). * [`useCapxulInbox`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-inbox) — list incoming payment requests addressed to the scope. * [`useCapxulApproveInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-approve-inbox-request) — approve an inbox request; returns the resulting `Payment`. * [`useCapxulDeclineInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-decline-inbox-request) — decline an inbox request. ## Insights [#insights] Actor-scoped — see [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). * [`useCapxulInsightsSummary`](https://capxul-sdk-docs.pages.dev/docs/hooks/insights/use-capxul-insights-summary) — the scope's spending insights summary. * [`useCapxulInsightsHistory`](https://capxul-sdk-docs.pages.dev/docs/hooks/insights/use-capxul-insights-history) — the scope's payment history for insights. ## Payroll [#payroll] * [`useCapxulPayrollRoster`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-payroll-roster) — an organization's payroll roster lines. * [`useCapxulAddPayrollRosterLine`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-add-payroll-roster-line) — add a roster line. * [`useCapxulUpdatePayrollRosterLine`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-update-payroll-roster-line) — update a roster line. * [`useCapxulRemovePayrollRosterLine`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-remove-payroll-roster-line) — remove a roster line. * [`useCapxulRunPayroll`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-run-payroll) — run payroll for an organization; returns the list of resulting `Payment` records. ## Organizations [#organizations] * [`useCapxulOrgs`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs) — list the organizations you belong to. * [`useCapxulOrg`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org) — a single organization by id, or `null`. * [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) — an organization's members. * [`useCapxulOrgRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-roles) — an organization's roles. * [`useCapxulOrgDeployRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-deploy-roles) — provision an organization's role set. * [`useCapxulOrgTreasury`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-treasury) — an organization's treasury Account: balance and available balance. * [`useCapxulOrganizationAccount`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-organization-account) — an organization's `OrganizationAccount` record. * [`useCapxulOrganizationAuditLog`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-organization-audit-log) — an organization's audit log. * [`useCapxulCreateOrg`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-create-org) — create an organization. * [`useCapxulInviteMember`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-invite-member) — invite a member to an organization. * [`useCapxulRemoveMember`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-remove-member) — remove a member from an organization. * [`useCapxulAssignRole`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-assign-role) — assign a role to a member. * [`useCapxulSwitchActingEntity`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-switch-acting-entity) — mutation seam for switching the acting entity (personal Account ↔ Organization); carries no SDK side effect. ## Onboarding [#onboarding] * [`useCapxulCompletePersonalOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-personal-onboarding) — persist the first identity profile and start Account setup. * [`useCapxulCompleteOrganizationOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-organization-onboarding) — complete organization onboarding. ## Legacy exports [#legacy-exports] The package still exports six headless slot components — `SendMoney`, `AddressBook`, `RequestInbox`, `InsightsSummary`, `PayrollRoster`, and `Destinations`. They are being retired and are intentionally undocumented: do not build on them. Every capability they covered is available through the hooks above. # useCapxulClientOrNull (https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul-client-or-null) Returns the bootstrapped `CapxulClient` from the surrounding [`CapxulProvider`](https://capxul-sdk-docs.pages.dev/docs/hooks/capxul-provider), or `null` while the bootstrap is still in flight (or has failed). This is the advanced escape hatch: use it when you need a client method that has **no dedicated hook yet**, and wrap the call in your own TanStack `useQuery` or `useMutation`. The built-in query hooks use it internally — that is how they sit in `isPending` (as a disabled query) until the client resolves instead of throwing during bootstrap. If a [hook exists](https://capxul-sdk-docs.pages.dev/docs/hooks) for what you need, use the hook. ## Import [#import] ```ts import { useCapxulClientOrNull } from "@capxul/sdk-react"; ``` ## Usage [#usage] Pair the client with your own `useQuery` under a `["capxul", ...]` key, gate on `client !== null`, and unwrap the `CapxulResult` yourself: ```tsx "use client"; import { useQuery } from "@tanstack/react-query"; import { useCapxulClientOrNull } from "@capxul/sdk-react"; function RequestPreview({ linkToken }: { linkToken: string }) { const client = useCapxulClientOrNull(); const request = useQuery({ queryKey: ["capxul", "requestByLink", linkToken], enabled: client !== null, queryFn: async () => { if (client === null) throw new Error("unreachable: query is disabled"); const result = await client.paymentRequests.getByLink(linkToken); if (!result.ok) throw result.error; return result.value; }, }); if (request.isPending) return

Loading…

; if (request.error) return

{request.error.message}

; if (request.data === null) return

No such request.

; return (

{request.data.reference}: {request.data.amount.value}{" "} {request.data.amount.currency} ({request.data.status})

); } ``` The pieces that make this behave like a built-in hook: * **`enabled: client !== null`** — the query stays pending during bootstrap and wakes up on its own once the client resolves, exactly like the shipped hooks. * **`["capxul", ...]` query key** — the provider clears capxul-scoped queries when the client changes (re-bootstrap, retry). Keying your custom queries under `"capxul"` opts them into that hygiene so no data leaks across clients. * **Unwrapping** — client methods return a `CapxulResult`; throw `result.error` (a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling)) so it lands on the query's `error` field. See [Using the raw client](https://capxul-sdk-docs.pages.dev/docs/guides/raw-client) for the full pattern, including mutations and cache invalidation. ## Parameters [#parameters] The hook takes no parameters. ## Return type [#return-type] ```ts import type { CapxulClient } from "@capxul/sdk"; // CapxulClient | null ``` `null` while the provider is bootstrapping and when the bootstrap has failed (check [`useCapxul`](https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul) to tell those apart). In [injected client mode](https://capxul-sdk-docs.pages.dev/docs/hooks/capxul-provider#injected-client-mode-client) the injected client is returned from the first render. The hook never throws for "not ready" — it returns `null` instead. ## Errors [#errors] Calling the hook outside a `CapxulProvider` throws — missing provider is a wiring bug, not a pending state. # useCapxul (https://capxul-sdk-docs.pages.dev/docs/hooks/use-capxul) Reads the bootstrap state of the surrounding [`CapxulProvider`](https://capxul-sdk-docs.pages.dev/docs/hooks/capxul-provider): a `status`, the bootstrap `error` (if any), and a `retry` function. You rarely need it. Data hooks stay in `isPending` until the client is ready, so a plain app requires **zero bootstrap awareness** — render your normal loading states and everything resolves on its own. Reach for `useCapxul` only when you want an opt-in splash screen or a dedicated bootstrap-error panel with a retry button. ## Import [#import] ```ts import { useCapxul } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { ReactNode } from "react"; import { useCapxul } from "@capxul/sdk-react"; function BootstrapGate({ children }: { children: ReactNode }) { const { status, error, retry } = useCapxul(); if (status === "bootstrapping") return

Starting up…

; if (status === "error") { return (

{error?.message}

); } return <>{children}; } ``` Mount the gate directly under `CapxulProvider` and the rest of the tree only renders once the client is ready. ## Parameters [#parameters] The hook takes no parameters. ## Return type [#return-type] This is not a TanStack Query hook — it returns a plain `CapxulBootstrapState` object: ```ts import { type CapxulBootstrapState, type CapxulBootstrapStatus } from "@capxul/sdk-react"; // { status: CapxulBootstrapStatus; error: CapxulError | null; retry: () => void } ``` ### status [#status] * `"bootstrapping"` — the provider is creating the client (the initial state in bootstrap mode, and again after `retry`). * `"ready"` — the client resolved; data hooks are live. In [injected client mode](https://capxul-sdk-docs.pages.dev/docs/hooks/capxul-provider#injected-client-mode-client) this is the state from the first render. * `"error"` — the bootstrap failed; `error` holds the [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling). ### error [#error] The bootstrap failure as a `CapxulError`, or `null` outside the `"error"` state. This is the *bootstrap* error only — per-query and per-mutation errors surface on each hook's own `error` field. ### retry [#retry] Re-runs the bootstrap with the same provider props: status returns to `"bootstrapping"`, and capxul-scoped queries from the previous attempt are cleared once the new client lands. In injected client mode there is no bootstrap to re-run, so `retry` is a no-op. ## Errors [#errors] Calling the hook outside a `CapxulProvider` throws: ``` useCapxul must be used within ``` # useCapxulAccountBalance (https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance) Reads the signed-in user's personal **Account** — the logical pot of money with its balance and available balance. ## Import [#import] ```ts import { useCapxulAccountBalance } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulAccountBalance } from "@capxul/sdk-react"; function BalanceCard() { const balance = useCapxulAccountBalance(); if (balance.isLoading) return

Loading balance…

; if (balance.error) return

{balance.error.message}

; const account = balance.data; return (

{account.balance.value} {account.balance.currency}

Available: {account.available.value} {account.available.currency}

); } ``` ## Parameters [#parameters] ### options [#options] `UseCapxulAccountBalanceOptions | undefined` Pass `enabled: false` to skip the balance read until your UI is ready for it — for example, while the [account lane](https://capxul-sdk-docs.pages.dev/docs/concepts/account-lane) is still provisioning: ```tsx const lifecycle = useCapxulAccountLifecycle(); const balance = useCapxulAccountBalance({ enabled: lifecycle.data?.phase === "ready", }); ``` ## Return type [#return-type] ```ts import { type UseCapxulAccountBalanceReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an `Account`: the opaque account id, its `balance`, and its `available` balance (both are `Money` — a currency, a decimal value, and a precision; never a raw token integer). The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "accountBalance"]` — invalidated automatically by money mutations (`useCapxulPay`, `useCapxulTransfer`, …) and cleared on sign-out. ## Client method [#client-method] This hook wraps [`client.accounts.read()`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulAccountFund (https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-fund) Funds the signed-in user's personal Account from the **test faucet**. This is a developer/testing surface for proving balance and money flows in test environments — it is not how customers add money, and it must not ship in a production frontend. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) for what customer-facing funding exists today. ## Import [#import] ```ts import { useCapxulAccountFund } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulAccountFund } from "@capxul/sdk-react"; function DevFundButton() { const fund = useCapxulAccountFund(); // Gate on your own dev/test flag — never render this in production. if (process.env.NEXT_PUBLIC_APP_ENV !== "test") return null; return ( ); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `Money` — the amount to credit: | Field | Type | Description | | ---------- | -------- | --------------------------------------------------- | | `currency` | `string` | Currency code, e.g. `"USD"`. | | `value` | `string` | Major-unit decimal string, e.g. `"25"` or `"1.50"`. | | `decimals` | `number` | The currency's precision (USD test money uses `6`). | ## Return type [#return-type] ```ts import { type UseCapxulAccountFundReturn } from "@capxul/sdk-react"; // UseMutationResult<{ txHash: string }, CapxulError, Money> ``` On success, `data` is a receipt: `{ txHash: string }`, the identifier of the faucet transfer. Treat it as an opaque proof value for test assertions. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates `["capxul", "accountBalance"]`, so [`useCapxulAccountBalance`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance) refetches and shows the new balance. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the faucet is not available for the key's environment. Faucet funding only exists for test environments. ## Client method [#client-method] This hook wraps the core SDK's dev-only faucet, [`client._internal.accounts.fund(amount)`](https://capxul-sdk-docs.pages.dev/docs/client). The faucet is deliberately kept off the public `client.accounts` surface so it can never leak into production integrations — this hook is the supported way to reach it in a test app. # useCapxulAccountLifecycle (https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle) Reads the **account lane** — the setup that runs after sign-in to get the user's personal Account ready — as a small state machine: `loading` → `settingUp` → `ready` (or `failed`). It self-polls while setup is in flight and exposes a `retry` for the failed state. See the [account lane](https://capxul-sdk-docs.pages.dev/docs/concepts/account-lane) concept page; the money itself is read with [`useCapxulAccountBalance`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance). ## Import [#import] ```ts import { useCapxulAccountLifecycle } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulAccountLifecycle } from "@capxul/sdk-react"; function ProvisioningScreen({ onReady }: { onReady: () => void }) { const { lifecycle, isSettingUp, error, retry, isRetrying } = useCapxulAccountLifecycle(); if (lifecycle.status === "ready") { onReady(); return null; } if (lifecycle.status === "failed") { return (

{error?.message ?? "Account setup failed."}

); } return (

Setting up your account…

{isSettingUp ?

Step: {lifecycle.step}

:

Preparing…

}
); } ``` The lane starts automatically after a successful [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp) when the provider's `requirement` is `"counterfactual"` or `"deployed"`. With the default `requirement` (`"none"`, auth-only apps) the lane does not run and the lifecycle stays out of the way. While the status is `loading` or `settingUp`, the hook refetches every 2 seconds and stops on `ready` or `failed`. ## Parameters [#parameters] None. ## Return type [#return-type] ```ts import { type UseCapxulAccountLifecycleReturn } from "@capxul/sdk-react"; ``` Unlike most query hooks, this one does **not** return a raw `UseQueryResult` — it returns a shaped object that merges the lifecycle query with the retry mutation: | Field | Type | Description | | ------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `lifecycle` | `AccountLifecycle` | The current lifecycle snapshot (see below). Never `undefined` — it reads as `{ status: "loading" }` before the first fetch. | | `isSettingUp` | `boolean` | `true` while `lifecycle.status === "settingUp"`. | | `error` | `CapxulError \| null` | The failure: the `failed` lifecycle's error, or the query's own fetch error. | | `isLoading` | `boolean` | `true` during the first fetch. | | `isFetching` | `boolean` | `true` whenever a fetch (including a poll) is in flight. | | `isError` | `boolean` | `true` when the lifecycle read itself failed. | | `retry` | `() => Promise` | Resume setup after `failed`; resolves with the updated snapshot. | | `isRetrying` | `boolean` | `true` while the retry is in flight. | `AccountLifecycle` is a discriminated union on `status`: | `status` | Extra fields | Meaning | | ------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------- | | `"loading"` | — | Not known yet (also shown while signed out). | | `"settingUp"` | `step` | Setup in flight. `step` is one of `"connecting"`, `"confirmingIdentity"`, `"registering"`, `"activating"`. | | `"ready"` | `accountId`, `canTransact` | The Account exists. `canTransact` is `true` once the account is fully activated for money movement. | | `"failed"` | `at`, `error` | Setup stopped at step `at` with a `CapxulError`. Offer `retry`. | If the lifecycle read itself fails while still loading, the hook coerces `lifecycle` to `{ status: "failed", at: "connecting", error }` so your UI has one failure path to handle. ## Query key [#query-key] `["capxul", "accountLifecycle"]` — invalidated (background refetch) by [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp), by a successful `retry`, and by the onboarding mutations ([`useCapxulCompletePersonalOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-personal-onboarding), [`useCapxulCompleteOrganizationOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-organization-onboarding)); reset by [`useCapxulSignOut`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-out). ## Errors [#errors] Both the query and the retry surface failures as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) — on the hook's `error` field, or thrown from `retry()`. ## Client method [#client-method] This hook wraps [`client.account.getLifecycle()`](https://capxul-sdk-docs.pages.dev/docs/client) for the read and [`client.account.retrySetup()`](https://capxul-sdk-docs.pages.dev/docs/client) for `retry` on the core SDK. # useCapxulActivity (https://capxul-sdk-docs.pages.dev/docs/hooks/activity/use-capxul-activity) Reads the **activity feed** — one cursor-paged timeline across payments, payouts, withdrawals, deposits, and claims, scoped to the acting entity. For the raw payment ledger use [`useCapxulPayments`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payments). In the published alpha, `client.activity.list` is a stub: **every fetch fails** with a `NOT_IMPLEMENTED` [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) ("activity.list is not yet implemented"). The hook, parameters, types, and query keys are published so a feed UI can be typed and wired today, but the query will sit in the error state until a later release. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulActivity } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulActivity } from "@capxul/sdk-react"; function ActivityFeed() { const activity = useCapxulActivity({ limit: 20 }); if (activity.isLoading) return

Loading activity…

; if (activity.error) return

{activity.error.message}

; return (
    {activity.data.items.map((item) => (
  • {item.title} — {item.amount.value} {item.amount.currency} ( {item.direction})
  • ))}
); } ``` ## Parameters [#parameters] ### params [#params] `ActivityListParams | undefined` Optional filters and paging. Omit it entirely for the default feed. `actor` scopes the feed to the personal account (default) or an organization. `cursor` pages through results — pass the previous page's `nextCursor`. `paymentType` and `timing` filter by payment classification and timing kind. ### options [#options] `{ enabled?: boolean } | undefined` Pass `enabled: false` to skip the read until your UI is ready for it. ## Return type [#return-type] ```ts import { type UseCapxulActivityReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an `ActivityPage`: `items` (a read-only array of `ActivityItem`) and `nextCursor` (`null` on the last page). Each `ActivityItem` carries its `kind` (`payment`, `payout`, `withdrawal`, `deposit`, or `claim`), a `direction` (`in`, `out`, or `internal`), the payment `status` and `amount`, a display `title` and `subtitle`, the counterparty `target` when known, and the linked `paymentId` when the item maps to a ledger row. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "activity", ]` — one cache entry per actor / cursor / limit / filter combination. No mutation invalidates activity keys automatically today; call `refetch` (or change `params`) to reload the feed after a money mutation. ## Errors [#errors] Every fetch currently fails with `NOT_IMPLEMENTED` (see the callout above). Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error`. ## Client method [#client-method] This hook wraps [`client.activity.list(params)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulAddAddressBookEntry (https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-add-address-book-entry) Adds a counterparty to an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes)'s address book by hand — most entries appear automatically from payment history, this hook is for saving someone *before* any money has moved. List entries with [`useCapxulAddressBook`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book); rename with [`useCapxulLabelAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-label-address-book-entry). ## Import [#import] ```ts import { capxulOrgScope, useCapxulAddAddressBookEntry } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulOrgScope, useCapxulAddAddressBookEntry, useCapxulOrgs, } from "@capxul/sdk-react"; function AddSupplierButton({ email }: { email: string }) { const orgs = useCapxulOrgs(); const orgId = orgs.data?.[0]?.orgId; const addEntry = useCapxulAddAddressBookEntry(capxulOrgScope(orgId)); return ( ); } ``` For the personal address book, call the hook with `capxulAccountScope` (or no argument at all — that is the default). ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` — defaults to `capxulAccountScope` Which actor's address book to write to. An explicit `undefined` also falls back to the personal scope, so when scoping to an org keep the control disabled until the org id has loaded (as in the example above). See [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). ### Mutation input [#mutation-input] The mutation takes an `AddressBookAddInput`: `ref` must be a typed reference — a handle, email, org handle, payee id, or Capxul user id. Bare EVM addresses are rejected by design. `label` is the display name; when omitted, the entry falls back to a label derived from the reference itself. ## Return type [#return-type] ```ts import { type UseCapxulAddAddressBookEntryReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the created `AddressBookEntry` — its `id` is what the other address-book hooks take as `entryId`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates, for the acting scope only, the address book list and the new entry's detail query. Org additions never refetch the personal address book, and vice versa. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example an empty reference value or a raw `0x…` address passed where a typed reference is required. ## Client method [#client-method] This hook wraps [`client.account.addressBook.add(input)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).addressBook.add(input)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulAddressBookEntry (https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book-entry) Reads a single address-book entry for an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). Use it for a contact detail view after listing entries with [`useCapxulAddressBook`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book). ## Import [#import] ```ts import { capxulAccountScope, useCapxulAddressBookEntry } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulAddressBookEntry } from "@capxul/sdk-react"; function ContactCard({ entryId }: { entryId: string }) { const entry = useCapxulAddressBookEntry(capxulAccountScope, entryId); if (entry.isLoading) return

Loading contact…

; if (entry.error) return

{entry.error.message}

; if (entry.data === null) return

No relationship with this contact yet.

; return (

{entry.data.label}

{entry.data.relationship.join(", ")}

); } ``` ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` The [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) whose address book to read. The query stays disabled while the scope is `undefined`. ### entryId [#entryid] `string | undefined` The entry id, as returned by [`useCapxulAddressBook`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book) or one of the address-book mutations. The query stays disabled while `entryId` is `undefined`, so you can pass route params or other query results directly. ### options [#options] `{ enabled?: boolean } | undefined` Set `enabled: false` to hold the query even when scope and id are ready. ## Return type [#return-type] ```ts import { type UseCapxulAddressBookEntryReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is the `AddressBookEntry`, or `null` when the actor has no relationship with that counterparty: The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] ```ts ["capxul", "actor", "account", "addressBook", entryId] // personal scope ["capxul", "actor", "org", orgId, "addressBook", entryId] // org scope ``` (`"pending"` fills the actor and/or `entryId` segment while either is `undefined`.) Invalidated by any address-book mutation for the same scope that returns this entry, and by payment-requests / inbox mutations that name its id. ## Errors [#errors] An `entryId` that is not a valid address-book entry id fails with a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) (invalid input) on `error` — always pass ids the SDK produced, not hand-built strings. ## Client method [#client-method] This hook wraps [`client.account.addressBook.get(entryId)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).addressBook.get(entryId)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulAddressBook (https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book) Lists the address book of an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes): every counterparty the person or organization has paid, been paid by, requested money from, or added by hand. Read a single entry with [`useCapxulAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book-entry); write with [`useCapxulAddAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-add-address-book-entry), [`useCapxulHideAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-hide-address-book-entry), [`useCapxulUnhideAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-unhide-address-book-entry), and [`useCapxulLabelAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-label-address-book-entry). The address book family is graded **SDK-ready**: published and tested, but not yet proven end-to-end by a product surface. Build against it with that expectation — see [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { capxulAccountScope, useCapxulAddressBook } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulAddressBook } from "@capxul/sdk-react"; function ContactsList() { const addressBook = useCapxulAddressBook(capxulAccountScope); if (addressBook.isLoading) return

Loading contacts…

; if (addressBook.error) return

{addressBook.error.message}

; const visible = addressBook.data.filter((entry) => !entry.hidden); return (
    {visible.map((entry) => (
  • {entry.label} — {entry.relationship.join(", ")}
  • ))}
); } ``` The same component reads an organization's address book by swapping the scope: ```tsx const addressBook = useCapxulAddressBook(capxulOrgScope(orgId)); ``` ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` The [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) whose address book to read — `capxulAccountScope` for the signed-in user, `capxulOrgScope(orgId)` for an organization. The query stays disabled while the scope is `undefined`, so you can pass `capxulOrgScope(orgs.data?.[0]?.orgId)` directly — no manual gating. ### options [#options] `{ enabled?: boolean } | undefined` Set `enabled: false` to hold the query even when the scope is ready. ## Return type [#return-type] ```ts import { type UseCapxulAddressBookReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an array of `AddressBookEntry`: `ref` is the typed counterparty reference (a handle, email, org handle, payee id, or Capxul user id). `relationship` lists how the counterparty relates to the actor, derived from actual history — `paid`, `paidBy`, `requested`, `member`, `employee`. `hidden` is the display flag toggled by the hide/unhide mutations; filter on it in your UI. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] ```ts ["capxul", "actor", "account", "addressBook"] // personal scope ["capxul", "actor", "org", orgId, "addressBook"] // org scope ``` (`"pending"` replaces the actor segment while the scope is `undefined`.) Invalidated — for the same scope only — by every address-book mutation, by every payment-requests and inbox mutation (issuing, approving, or declining a request touches relationship edges), and by money mutations that create a new counterparty relationship (for example `useCapxulPay` and payroll runs). ## Client method [#client-method] This hook wraps [`client.account.addressBook.list()`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).addressBook.list()`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulHideAddressBookEntry (https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-hide-address-book-entry) Hides an address-book entry for an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). Hiding is a display flag, not a delete: the relationship history stays, and [`useCapxulUnhideAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-unhide-address-book-entry) restores the entry. List entries (and read their `hidden` flag) with [`useCapxulAddressBook`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book). ## Import [#import] ```ts import { capxulAccountScope, useCapxulHideAddressBookEntry } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulHideAddressBookEntry, } from "@capxul/sdk-react"; function HideContactButton({ entryId }: { entryId: string }) { const hideEntry = useCapxulHideAddressBookEntry(capxulAccountScope); return ( ); } ``` To hide an entry in an organization's address book, scope the hook with `capxulOrgScope(orgId)` instead. ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` — defaults to `capxulAccountScope` Which actor's address book to write to. An explicit `undefined` also falls back to the personal scope — gate org-scoped controls on the org id being loaded. See [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). ### Mutation input [#mutation-input] The mutation takes the entry id (`string`), as returned by [`useCapxulAddressBook`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book). ## Return type [#return-type] ```ts import { type UseCapxulHideAddressBookEntryReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the updated `AddressBookEntry` with `hidden: true`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates, for the acting scope only, the address book list and this entry's detail query. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the id is not a valid address-book entry id. ## Client method [#client-method] This hook wraps [`client.account.addressBook.hide(entryId)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).addressBook.hide(entryId)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulLabelAddressBookEntry (https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-label-address-book-entry) Sets the display label of an address-book entry for an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). Labels are per-actor: renaming a contact in an org's address book does not touch the same counterparty in your personal one. List entries with [`useCapxulAddressBook`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book). ## Import [#import] ```ts import { capxulAccountScope, useCapxulLabelAddressBookEntry } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useState } from "react"; import { capxulAccountScope, useCapxulLabelAddressBookEntry, } from "@capxul/sdk-react"; function RenameContactForm({ entryId }: { entryId: string }) { const labelEntry = useCapxulLabelAddressBookEntry(capxulAccountScope); const [label, setLabel] = useState(""); return (
{ event.preventDefault(); await labelEntry.mutateAsync({ entryId, label }); }} > setLabel(event.target.value)} placeholder="e.g. Studio landlord" /> {labelEntry.error ?

{labelEntry.error.message}

: null}
); } ``` To rename an entry in an organization's address book, scope the hook with `capxulOrgScope(orgId)` instead. ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` — defaults to `capxulAccountScope` Which actor's address book to write to. An explicit `undefined` also falls back to the personal scope — gate org-scoped controls on the org id being loaded. See [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). ### Mutation input [#mutation-input] The mutation takes an `AddressBookLabelInput`: ## Return type [#return-type] ```ts import { type UseCapxulLabelAddressBookEntryReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the updated `AddressBookEntry` carrying the new `label`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates, for the acting scope only, the address book list and this entry's detail query. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the id is not a valid address-book entry id. ## Client method [#client-method] This hook wraps [`client.account.addressBook.label(input)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).addressBook.label(input)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulUnhideAddressBookEntry (https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-unhide-address-book-entry) Restores a hidden address-book entry for an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) — the counterpart of [`useCapxulHideAddressBookEntry`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-hide-address-book-entry). List entries (including hidden ones, flagged `hidden: true`) with [`useCapxulAddressBook`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book). ## Import [#import] ```ts import { capxulAccountScope, useCapxulUnhideAddressBookEntry } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulUnhideAddressBookEntry, } from "@capxul/sdk-react"; function RestoreContactButton({ entryId }: { entryId: string }) { const unhideEntry = useCapxulUnhideAddressBookEntry(capxulAccountScope); return ( ); } ``` To restore an entry in an organization's address book, scope the hook with `capxulOrgScope(orgId)` instead. ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` — defaults to `capxulAccountScope` Which actor's address book to write to. An explicit `undefined` also falls back to the personal scope — gate org-scoped controls on the org id being loaded. See [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). ### Mutation input [#mutation-input] The mutation takes the entry id (`string`), as returned by [`useCapxulAddressBook`](https://capxul-sdk-docs.pages.dev/docs/hooks/address-book/use-capxul-address-book). ## Return type [#return-type] ```ts import { type UseCapxulUnhideAddressBookEntryReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the updated `AddressBookEntry` with `hidden: false`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates, for the acting scope only, the address book list and this entry's detail query. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the id is not a valid address-book entry id. ## Client method [#client-method] This hook wraps [`client.account.addressBook.unhide(entryId)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).addressBook.unhide(entryId)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulCurrentUser (https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-current-user) Reads the aggregated **current-user context**: who is signed in, their personal account, and every organization they belong to — one query instead of composing [`useCapxulProfile`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-profile), [`useCapxulAccountBalance`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance), and `useCapxulOrgs` yourself. Built for entity switchers and app shells. ## Import [#import] ```ts import { useCapxulCurrentUser } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulCurrentUser } from "@capxul/sdk-react"; function EntitySwitcher() { const currentUser = useCapxulCurrentUser(); if (currentUser.isLoading) return

Loading…

; if (currentUser.error) return

{currentUser.error.message}

; const { user, organizations } = currentUser.data; return ( ); } ``` This is a signed-in surface: it reads the current user, so gate it on [`useCapxulSession`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-session) (or pass `enabled: false`) while signed out. ## Parameters [#parameters] ### options [#options] `{ enabled?: boolean } | undefined` | Field | Type | Description | | --------- | ---------------------- | -------------------------------------------------------------------------------- | | `enabled` | `boolean \| undefined` | Set `false` to skip the read (for example while signed out). Defaults to `true`. | ## Return type [#return-type] ```ts import { type UseCapxulCurrentUserReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is a `CurrentUserContext`: | Field | Type | Description | | ----------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `user` | `object` | `id`, `email`, `displayName`, plus `handle` and `paymentLink`. | | `personalAccount` | `object \| null` | The personal Account: its opaque `id` and its `address` (`null` until the account is activated on the network). | | `organizations` | `array` | One entry per membership: `id`, `name`, `handle`, `role` (your role label in that org), and the org's `account` (`id` + `address`). | In the published alpha, `user.handle` and `user.paymentLink` are always `null` — payment handles are not populated on this surface yet. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "currentUser"]` — no SDK mutation invalidates this key automatically today (it is not part of the auth-boundary invalidation that covers session/profile/account). After sign-in, sign-out, or creating an organization, call `refetch()` — or remount the screen — to pick up the new context. ## Client method [#client-method] This hook wraps [`client.currentUser.get()`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulProfile (https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-profile) Reads the signed-in user's identity **profile** — display name, country, KYC tier. Distinct from [`useCapxulSession`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-session) (am I signed in?) and from [`useCapxulCurrentUser`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-current-user) (the aggregated user + accounts + orgs view). ## Import [#import] ```ts import { useCapxulProfile } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulProfile } from "@capxul/sdk-react"; function ProfileCard() { const profile = useCapxulProfile(); if (profile.isLoading) return

Loading profile…

; if (profile.error) return

{profile.error.message}

; if (!profile.data) { // Fresh identity — signed in, but onboarding has not completed yet. return Finish setting up your account; } return (

{profile.data.displayName ?? profile.data.email}

Country: {profile.data.country ?? "—"}

); } ``` `data` can be `null` for a brand-new identity: OTP verification creates the auth user, but the profile record is created during [onboarding](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-personal-onboarding) / account setup, not at verification. Treat `null` as "route to onboarding", not as an error. ## Parameters [#parameters] None. The query is gated automatically: it stays pending until the provider finishes bootstrapping, with no manual `enabled` wiring needed. ## Return type [#return-type] ```ts import { type UseCapxulProfileReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is a `Profile` or `null` (no profile record yet — see above): | Field | Type | Description | | ------------- | ------------------ | --------------------------------------------------- | | `authUserId` | `string` | The auth user id — same id as `Session.authUserId`. | | `email` | `string` | The signed-in email. | | `displayName` | `string \| null` | Set during onboarding; `null` until then. | | `country` | `string \| null` | ISO-3166 country code, set during onboarding. | | `kycTier` | `0 \| 1 \| 2 \| 3` | The identity's verification tier. | | `createdAt` | `number` | Epoch milliseconds. | | `updatedAt` | `number` | Epoch milliseconds. | The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "profile"]` — invalidated (background refetch) by [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp) and by the onboarding mutations ([`useCapxulCompletePersonalOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-personal-onboarding), [`useCapxulCompleteOrganizationOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-organization-onboarding)), and reset by [`useCapxulSignOut`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-out). ## Client method [#client-method] This hook wraps [`client.identity.loadCurrent()`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulSession (https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-session) Reads the current session. `data` is the `Session` when someone is signed in and `null` when no one is — the primary signal for gating signed-in UI. Pair it with [`useCapxulSignIn`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-in) / [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp) to establish a session and [`useCapxulSignOut`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-out) to end it. ## Import [#import] ```ts import { useCapxulSession } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulSession } from "@capxul/sdk-react"; function AccountHeader() { const session = useCapxulSession(); if (session.isLoading) return

Loading…

; if (session.error) return

{session.error.message}

; if (!session.data) return Sign in; return (

Signed in as {session.data.email}

User id: {session.data.authUserId}

); } ``` ## Parameters [#parameters] None. The query is gated automatically: it stays pending until the provider finishes bootstrapping, with no manual `enabled` wiring needed. ## Return type [#return-type] ```ts import { type UseCapxulSessionReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is a `Session` — the `authUserId`, the signed-in `email`, the session `token`, and `expiresAt` (epoch milliseconds) — or `null` when no one is signed in. `null` is a **successful** result, not an error: check `session.data`, not `session.isError`, to branch on auth state. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "session"]` — invalidated (background refetch) by [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp) on successful verification, and reset (dropped immediately) by [`useCapxulSignOut`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-out). ## Client method [#client-method] This hook wraps [`client.auth.getSession()`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulSignIn (https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-in) Starts email OTP sign-in: sends a one-time code to the given email address. Pair it with [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp) to complete the login. ## Import [#import] ```ts import { useCapxulSignIn } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useState } from "react"; import { useCapxulSignIn } from "@capxul/sdk-react"; function SignInForm({ onOtpSent }: { onOtpSent: (email: string) => void }) { const signIn = useCapxulSignIn(); const [email, setEmail] = useState(""); return (
{ event.preventDefault(); await signIn.mutateAsync({ email }); onOtpSent(email); }} > setEmail(event.target.value)} /> {signIn.error ?

{signIn.error.message}

: null}
); } ``` Sending the code does not sign the user in — `useCapxulSession` stays `null` until the code is verified. If the email has never signed in before, the user identity is created during **verification**, not here. ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `SignInInput`: ## Return type [#return-type] ```ts import { type UseCapxulSignInReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is a `SignInSuccess`: The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] Sending an OTP does not change any signed-in state, so this mutation invalidates nothing. The auth boundary (session, profile, account queries) is invalidated by [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp) and [`useCapxulSignOut`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-out). ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the email is rejected or OTP sending is unavailable for the key's environment. ## Client method [#client-method] This hook wraps [`client.auth.signIn(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulSignOut (https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-out) Ends the current session. The counterpart to [`useCapxulSignIn`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-in) / [`useCapxulVerifyOtp`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp): after it succeeds, [`useCapxulSession`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-session) reads `null` again. ## Import [#import] ```ts import { useCapxulSignOut } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulSession, useCapxulSignOut } from "@capxul/sdk-react"; function SignOutButton() { const session = useCapxulSession(); const signOut = useCapxulSignOut(); if (!session.data) return null; return ( ); } ``` ## Parameters [#parameters] None — the hook takes no parameters, and the mutation takes no input. Call `mutate()` / `mutateAsync()` with no arguments. ## Return type [#return-type] ```ts import { type UseCapxulSignOutReturn } from "@capxul/sdk-react"; // UseMutationResult ``` There is no `data` payload — success simply means the session ended. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] Sign-out is the hard side of the auth boundary. When the mutation starts it cancels any in-flight balance fetch, and on success it **resets** (drops immediately, rather than refetching in the background) the auth-boundary queries: * `["capxul", "session"]` ([`useCapxulSession`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-session)) * `["capxul", "profile"]` ([`useCapxulProfile`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-profile)) * `["capxul", "accountLifecycle"]` ([`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle)) * `["capxul", "accountBalance"]` ([`useCapxulAccountBalance`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance)) The reset means cached authenticated rows never linger on screen after sign-out. Queries outside this boundary (org lists, activity, the current-user context) are not reset automatically — unmount them when the session goes `null`. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync`. The React Query cache reset above only runs when the mutation succeeds. ## Client method [#client-method] This hook wraps [`client.auth.signOut()`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulVerifyOtp (https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-verify-otp) Completes email OTP sign-in: verifies the code sent by [`useCapxulSignIn`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-in) and signs the user in. On success the mutation returns the new `Session` immediately. ## Import [#import] ```ts import { useCapxulVerifyOtp } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useState } from "react"; import { useCapxulVerifyOtp } from "@capxul/sdk-react"; function VerifyOtpForm({ email }: { email: string }) { const verifyOtp = useCapxulVerifyOtp(); const [code, setCode] = useState(""); return (
{ event.preventDefault(); await verifyOtp.mutateAsync({ email, code }); }} > setCode(event.target.value)} /> {verifyOtp.error ?

{verifyOtp.error.message}

: null}
); } ``` Verification is both "create user" and "log in": if the email has never signed in before, Capxul creates the user identity during this call — your app does not call a separate create-user API. After the code is accepted the SDK also runs its post-verification setup: it detects and accepts any pending organization invitations for the email (where organization support is configured), and it starts the [account lane](https://capxul-sdk-docs.pages.dev/docs/concepts/account-lane) in the background when the provider's `requirement` is not `"none"` — watch that setup with [`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle). ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `VerifyOtpInput`: ## Return type [#return-type] ```ts import { type UseCapxulVerifyOtpReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the new `Session`: the `authUserId`, the signed-in `email`, the session `token`, and `expiresAt` (epoch milliseconds). This is the same value [`useCapxulSession`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-session) serves after its refetch. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the **auth boundary** — the queries whose answers change when someone signs in: * `["capxul", "session"]` ([`useCapxulSession`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-session)) * `["capxul", "profile"]` ([`useCapxulProfile`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-profile)) * `["capxul", "accountLifecycle"]` ([`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle)) * `["capxul", "accountBalance"]` ([`useCapxulAccountBalance`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance)) These are background refetches (invalidate, not reset) so mounted screens update without flashing empty state. The hard reset happens on [`useCapxulSignOut`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-sign-out). ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — most commonly an incorrect or expired code. A failure in the SDK's post-verification setup (invitation detection, signer session reset) also surfaces on this mutation, even though the code itself was accepted. ## Client method [#client-method] This hook wraps [`client.auth.verifyOtp(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulApproveInboxRequest (https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-approve-inbox-request) Approves a payment request from an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes)'s inbox. **Approving pays**: the mutation settles the requested amount to the issuer and resolves with the resulting `Payment`. List actionable items with [`useCapxulInbox`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-inbox); decline instead with [`useCapxulDeclineInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-decline-inbox-request). ## Import [#import] ```ts import { capxulAccountScope, useCapxulApproveInboxRequest } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { InboxItem } from "@capxul/sdk"; import { capxulAccountScope, useCapxulApproveInboxRequest, } from "@capxul/sdk-react"; function ApproveButton({ item }: { item: InboxItem }) { const approve = useCapxulApproveInboxRequest(capxulAccountScope); return ( ); } ``` To approve from an organization's inbox — paying from the org treasury — scope the hook with `capxulOrgScope(orgId)` and gate the control on the org id being loaded. ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` — defaults to `capxulAccountScope` Which actor approves (and pays). An explicit `undefined` also falls back to the personal scope — gate org-scoped controls on the org id being loaded. See [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). ### Mutation input [#mutation-input] The mutation takes an `InboxApproveInput`: `timing` is an optional timing clause; omitted means the payment is instant. ## Return type [#return-type] ```ts import { type UseCapxulApproveInboxRequestReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the resulting `Payment` — id, `status`, `amount`, recipient (the request's issuer), timing, and attached documents. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] Approving settles money, so this mutation has the widest invalidation set in the family. For the acting scope only: the inbox, the requests list, the request's detail query, the address book, and both insights queries. Because the result is a `Payment`, it additionally invalidates money state — the payments list, the settled payment's detail, and the acting scope's balance: `["capxul", "accountBalance"]` for the personal scope, or the org's treasury and account keys for an org scope. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the request is no longer open, or the payment cannot be settled. ## Client method [#client-method] This hook wraps [`client.account.inbox.approve(input)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).inbox.approve(input)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulDeclineInboxRequest (https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-decline-inbox-request) Declines a payment request from an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes)'s inbox. No money moves; the issuer sees the request move to `declined`. List actionable items with [`useCapxulInbox`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-inbox); pay instead with [`useCapxulApproveInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-approve-inbox-request). ## Import [#import] ```ts import { capxulAccountScope, useCapxulDeclineInboxRequest } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulDeclineInboxRequest, } from "@capxul/sdk-react"; function DeclineButton({ requestId }: { requestId: string }) { const decline = useCapxulDeclineInboxRequest(capxulAccountScope); return ( ); } ``` To decline from an organization's inbox, scope the hook with `capxulOrgScope(orgId)` instead. ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` — defaults to `capxulAccountScope` Which actor declines. An explicit `undefined` also falls back to the personal scope — gate org-scoped controls on the org id being loaded. See [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). ### Mutation input [#mutation-input] The mutation takes the request id (`string`) — the `id` of the `InboxItem`, as returned by [`useCapxulInbox`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-inbox). ## Return type [#return-type] ```ts import { type UseCapxulDeclineInboxRequestReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the updated `InboxItem` with `status: "declined"`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates, for the acting scope only: the inbox, the requests list, the declined request's detail query, the address book, and both insights queries. No money moves, so balances are untouched. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the request is no longer open. ## Client method [#client-method] This hook wraps [`client.account.inbox.decline(requestId)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).inbox.decline(requestId)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulInbox (https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-inbox) Lists the payment requests other actors have issued **to** an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) — the money the person or org is being asked to pay. Act on items with [`useCapxulApproveInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-approve-inbox-request) and [`useCapxulDeclineInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-decline-inbox-request). Requests the actor has issued itself live on the [Payment requests](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests) side. The inbox family is graded **SDK-ready**: the approve / decline primitives are published and tested, but a full invoice product workflow has not proven them end-to-end yet. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { capxulAccountScope, useCapxulInbox } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulInbox } from "@capxul/sdk-react"; function InboxList() { const inbox = useCapxulInbox(capxulAccountScope); if (inbox.isLoading) return

Loading inbox…

; if (inbox.error) return

{inbox.error.message}

; const open = inbox.data.filter((item) => item.status === "open"); if (open.length === 0) return

No requests waiting on you.

; return (
    {open.map((item) => (
  • {item.reference}: {item.amount.value} {item.amount.currency}
  • ))}
); } ``` The same component renders an organization's inbox by swapping the scope: ```tsx const inbox = useCapxulInbox(capxulOrgScope(orgId)); ``` ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` The [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) whose inbox to read. The query stays disabled while the scope is `undefined`. ### options [#options] `{ enabled?: boolean } | undefined` Set `enabled: false` to hold the query even when the scope is ready. ## Return type [#return-type] ```ts import { type UseCapxulInboxReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an array of `InboxItem`: `issuer` is the typed reference of whoever is asking to be paid. Only `status: "open"` items are actionable; the other statuses record how a request left the inbox (`approved`, `declined`, `paid`, `cancelled`, `expired`). The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] ```ts ["capxul", "actor", "account", "inbox"] // personal scope ["capxul", "actor", "org", orgId, "inbox"] // org scope ``` (`"pending"` replaces the actor segment while the scope is `undefined`.) Invalidated — for the same scope only — by [`useCapxulApproveInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-approve-inbox-request), [`useCapxulDeclineInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-decline-inbox-request), and [`useCapxulCancelRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-cancel-request). ## Client method [#client-method] This hook wraps [`client.account.inbox.list()`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).inbox.list()`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulAddDestination (https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-add-destination) Saves a **destination** for a counterparty — a bank account, mobile-money number, or wallet that can later be selected by id in [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout). List saved destinations with [`useCapxulDestinations`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-destinations). All three destination kinds can be **stored**, but only wallet (`external_account`) destinations can be **settled** by [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout) today. Saving a bank or mobile-money destination models the counterparty; it does not enable bank or mobile-money cash-out. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulAddDestination } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulAddDestination } from "@capxul/sdk-react"; function AddBankDestination({ email }: { email: string }) { const addDestination = useCapxulAddDestination(); return ( ); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `DestinationAddInput`: **`target` or `ref`** names the counterparty the destination belongs to — one of the two is required (`INVALID_INPUT` otherwise). Saving a destination also records the counterparty in the acting entity's address book. **`payload`** is validated by kind: * `bank_account` — `accountHolderName`, `bankName`, `country` (ISO-3166 alpha-2), `currency` (ISO-4217), and `accountNumberLast4` (exactly four digits). Only the last four digits are ever stored. * `mobile_money` — `provider`, `country`, `currency`, and `phoneNumberLast4` (exactly four digits). * `external_account` (wallet) — `network` (must be `base-sepolia` in the current alpha) and a `0x` + 40-hex `address`. **`label`** is optional and at most 120 characters. **`actor`** scopes the destination to the personal account (default) or an organization; org actors need spend authority in that org. ## Return type [#return-type] ```ts import { type UseCapxulAddDestinationReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the saved `Destination`, including the opaque `id` to pass to [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout). The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates every destinations query (the `["capxul", "destinations"]` prefix) and the acting entity's address book, since adding a destination also records the counterparty there. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — `INVALID_INPUT` when neither `target` nor `ref` is given, when the payload fails the kind-specific validation above, or when the label is too long. ## Client method [#client-method] This hook wraps [`client.destinations.add(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulDestinations (https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-destinations) Lists the **saved destinations** of the acting entity — the places money can be paid out to, saved once and later selected by id in [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout). Save and retire them with [`useCapxulAddDestination`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-add-destination) and [`useCapxulRemoveDestination`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-remove-destination). Destination metadata can be stored for all three kinds — bank accounts, mobile-money numbers, and wallets — but the platform can only **settle** payouts to wallet (`external_account`) destinations today. Bank and mobile-money payouts are rejected. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulDestinations } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulDestinations } from "@capxul/sdk-react"; function DestinationList() { const destinations = useCapxulDestinations({}); if (destinations.isLoading) return

Loading destinations…

; if (destinations.error) return

{destinations.error.message}

; return (
    {destinations.data.map((destination) => (
  • {destination.label ?? destination.counterpartyId} — {destination.kind}
  • ))}
); } ``` ## Parameters [#parameters] ### input [#input] `DestinationListInput | undefined` The query stays disabled while `input` is `undefined`. Pass `{}` to list every destination of the personal actor. `target` (or the lower-level `ref`) narrows the list to destinations saved for one counterparty. `kind` filters by destination kind (`bank_account`, `mobile_money`, `external_account`). `actor` scopes the list to the personal account (default) or an organization. ### options [#options] `{ enabled?: boolean } | undefined` Pass `enabled: false` to skip the read until your UI is ready for it. ## Return type [#return-type] ```ts import { type UseCapxulDestinationsReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is a read-only array of `Destination`: the opaque `id` (what [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout) takes), the counterparty it belongs to, its `kind` and `rail`, an optional `label`, and a kind-specific `payload`. Bank and mobile-money payloads are stored **masked**: account holder, bank name or operator, country, currency, and only the last four digits of the account or phone number. Wallet payloads carry the network and address. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "destinations", ]` — one cache entry per actor / target / kind combination, all sharing the `["capxul", "destinations"]` prefix. Invalidated automatically by [`useCapxulAddDestination`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-add-destination) and [`useCapxulRemoveDestination`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-remove-destination). ## Client method [#client-method] This hook wraps [`client.destinations.list(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulRemoveDestination (https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-remove-destination) Retires a **saved destination** by id. A removed destination stops appearing in [`useCapxulDestinations`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-destinations) results and can no longer be paid out to with [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout). ## Import [#import] ```ts import { useCapxulRemoveDestination } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulRemoveDestination } from "@capxul/sdk-react"; function RemoveDestinationButton({ destinationId }: { destinationId: string }) { const removeDestination = useCapxulRemoveDestination(); return ( ); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `DestinationRemoveInput`: **`destinationId`** must name a destination belonging to the acting entity — otherwise the mutation fails with `INVALID_INPUT` ("destination not found"). **`actor`** scopes the removal to the personal account (default) or an organization; org actors need spend authority in that org. Removing an already-removed destination succeeds without changing anything. ## Return type [#return-type] ```ts import { type UseCapxulRemoveDestinationReturn } from "@capxul/sdk-react"; // UseMutationResult<{ readonly id: string }, CapxulError, DestinationRemoveInput> ``` On success, `data` is `{ id }` — the id of the removed destination. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates every destinations query (the `["capxul", "destinations"]` prefix). It does not touch the address book — the counterparty entry recorded when the destination was added stays. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — `INVALID_INPUT` when the id does not name a destination of the acting entity. ## Client method [#client-method] This hook wraps [`client.destinations.remove(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulOfframpQuote (https://capxul-sdk-docs.pages.dev/docs/hooks/offramp/use-capxul-offramp-quote) Prices a **cash-out**: given a saved destination and an amount, returns a quote with the fee, the amount the recipient would receive, and when the quote expires. Track an offramp in flight with [`useCapxulOfframpStatus`](https://capxul-sdk-docs.pages.dev/docs/hooks/offramp/use-capxul-offramp-status); manage destinations with [`useCapxulDestinations`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-destinations). Offramp is new in this alpha train and **not yet callable**: in the published SDK, `client.offramp.quote` is a stub that fails every request with a `NOT_IMPLEMENTED` [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) ("offramp.quote is not yet implemented"). The quote and status contracts are published so integrations can be typed today. The settlement rail behind them is also constrained: the platform can settle wallet destinations only — bank / mobile-money settlement is roadmap. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulOfframpQuote } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulOfframpQuote } from "@capxul/sdk-react"; function QuotePreview({ destinationId }: { destinationId: string }) { const quote = useCapxulOfframpQuote({ destinationId, amount: { currency: "USD", value: "100.00", decimals: 2 }, }); if (quote.isLoading) return

Getting quote…

; if (quote.error) return

{quote.error.message}

; return (

You receive {quote.data.receivedAmount.value}{" "} {quote.data.receivedAmount.currency} (fee {quote.data.fee.value}{" "} {quote.data.fee.currency})

); } ``` ## Parameters [#parameters] ### input [#input] `OfframpQuoteInput | undefined` The query stays disabled while `input` is `undefined`, so you can pass a destination id that arrives asynchronously without manual gating. `destinationId` is the saved destination to cash out to (see [`useCapxulDestinations`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-destinations)); `amount` is the money to cash out; `actor` scopes the quote to the personal account (default) or an organization. ### options [#options] `{ enabled?: boolean } | undefined` Pass `enabled: false` to skip the read until your UI is ready for it. ## Return type [#return-type] ```ts import { type UseCapxulOfframpQuoteReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an `OfframpQuote`: the quote `id`, the echoed `input`, `expiresAt` (epoch milliseconds — after this the quote is stale), the `receivedAmount` after fees, and the `fee`. Both amounts are decimal money values, never raw token units. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "offramp", "quote", ]` — one cache entry per actor / destination / amount combination, so editing the amount fetches a fresh quote. No mutation invalidates quote keys automatically; refetch to re-price after `expiresAt`. ## Errors [#errors] Every request currently fails with `NOT_IMPLEMENTED` (see the callout above). Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error`. ## Client method [#client-method] This hook wraps [`client.offramp.quote(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulOfframpStatus (https://capxul-sdk-docs.pages.dev/docs/hooks/offramp/use-capxul-offramp-status) Tracks an **offramp** in flight: its lifecycle status, the destination it settles to, and the ledger `Payment` it produces once one exists. Get the offramp priced with [`useCapxulOfframpQuote`](https://capxul-sdk-docs.pages.dev/docs/hooks/offramp/use-capxul-offramp-quote). Offramp is new in this alpha train and **not yet callable**: in the published SDK, `client.offramp.status` is a stub that fails every request with a `NOT_IMPLEMENTED` [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) ("offramp.status is not yet implemented"). The contract is published so integrations can be typed today; the settlement rail behind it settles wallet destinations only. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulOfframpStatus } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulOfframpStatus } from "@capxul/sdk-react"; function OfframpTracker({ offrampId }: { offrampId: string }) { const status = useCapxulOfframpStatus(offrampId); if (status.isLoading) return

Checking status…

; if (status.error) return

{status.error.message}

; return (

Offramp {status.data.id}: {status.data.status} {status.data.paymentId ? ` (payment ${status.data.paymentId})` : null}

); } ``` ## Parameters [#parameters] ### offrampId [#offrampid] `string | undefined` The offramp to track. The query stays disabled while `offrampId` is `undefined`, so you can pass an id that arrives asynchronously without manual gating. ### options [#options] `{ enabled?: boolean } | undefined` Pass `enabled: false` to skip the read until your UI is ready for it. ## Return type [#return-type] ```ts import { type UseCapxulOfframpStatusReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an `OfframpStatus`: the offramp `id`, its `status` (`pending`, `processing`, `completed`, `failed`, or `cancelled`), the `destinationId` it settles to, the linked `paymentId` (`null` until the offramp lands on the payment ledger), and `updatedAt`. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "offramp", "status", offrampId]` — scoped per offramp. No mutation invalidates status keys automatically; refetch (or poll with TanStack Query's `refetchInterval`) to follow an offramp to a terminal state. ## Errors [#errors] Every request currently fails with `NOT_IMPLEMENTED` (see the callout above). Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error`. ## Client method [#client-method] This hook wraps [`client.offramp.status(offrampId)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulInsightsHistory (https://capxul-sdk-docs.pages.dev/docs/hooks/insights/use-capxul-insights-history) Reads the payment history of an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) — the `Payment` records whose aggregates [`useCapxulInsightsSummary`](https://capxul-sdk-docs.pages.dev/docs/hooks/insights/use-capxul-insights-summary) reports. ## Import [#import] ```ts import { capxulOrgScope, useCapxulInsightsHistory } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulOrgScope, useCapxulInsightsHistory, useCapxulOrgs, } from "@capxul/sdk-react"; function OrgPaymentHistory() { const orgs = useCapxulOrgs(); const history = useCapxulInsightsHistory( capxulOrgScope(orgs.data?.[0]?.orgId), ); if (history.isLoading) return

Loading history…

; if (history.error) return

{history.error.message}

; return (
    {history.data.map((payment) => (
  • {payment.recipient.label}: {payment.amount.value}{" "} {payment.amount.currency} — {payment.status}
  • ))}
); } ``` For the signed-in user's own history, scope with `capxulAccountScope` instead. ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` The [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) whose history to read. The query stays disabled while the scope is `undefined`, so an org id from `useCapxulOrgs` can be passed directly (as above). ### options [#options] `{ enabled?: boolean } | undefined` Set `enabled: false` to hold the query even when the scope is ready. ## Return type [#return-type] ```ts import { type UseCapxulInsightsHistoryReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an array of `Payment` records: each has an `id`, `status`, `amount` (a decimal money value), `paymentType`, a labeled `recipient`, a `timing` clause (`instant`, `scheduled`, or `stream`), attached `documents`, and `createdAt` / `updatedAt` timestamps. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] ```ts ["capxul", "actor", "account", "insights", "history"] // personal scope ["capxul", "actor", "org", orgId, "insights", "history"] // org scope ``` (`"pending"` replaces the actor segment while the scope is `undefined`.) Invalidated — for the same scope only — by every payment-requests and inbox mutation, and by money mutations (`useCapxulPay`, payouts, withdrawals, payroll runs) through the shared money-state invalidation. ## Client method [#client-method] This hook wraps [`client.account.insights.history()`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).insights.history()`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulInsightsSummary (https://capxul-sdk-docs.pages.dev/docs/hooks/insights/use-capxul-insights-summary) Reads the money-flow summary of an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes): what is pending, what got paid this month, open drafts, and reconciliation counters. It is the dashboard-tile companion to [`useCapxulInsightsHistory`](https://capxul-sdk-docs.pages.dev/docs/hooks/insights/use-capxul-insights-history), which returns the underlying payment records. The insights family is graded **SDK-ready**: published and tested, but not yet proven end-to-end by a product surface. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { capxulAccountScope, useCapxulInsightsSummary } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulInsightsSummary, } from "@capxul/sdk-react"; function InsightsTiles() { const insights = useCapxulInsightsSummary(capxulAccountScope); if (insights.isLoading) return

Loading insights…

; if (insights.error) return

{insights.error.message}

; const { pending, paidThisMonth, reconciliation } = insights.data; return (
Awaiting payment
{pending.count} requests · {pending.total.value}{" "} {pending.total.currency}
Paid this month
{paidThisMonth.count} payments · {paidThisMonth.total.value}{" "} {paidThisMonth.total.currency}
Reconciliation
{reconciliation.open} open · {reconciliation.exceptions} exceptions
); } ``` The same tiles render for an organization by swapping the scope: ```tsx const insights = useCapxulInsightsSummary(capxulOrgScope(orgId)); ``` ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` The [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) whose summary to read. The query stays disabled while the scope is `undefined`. ### options [#options] `{ enabled?: boolean } | undefined` Set `enabled: false` to hold the query even when the scope is ready. ## Return type [#return-type] ```ts import { type UseCapxulInsightsSummaryReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an `InsightsSummary`: The `total` fields are decimal money values (currency, string value, decimals). The `reconciliation` counters correspond to what [`useCapxulReconcileRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-reconcile-requests) reports per receivable. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] ```ts ["capxul", "actor", "account", "insights", "summary"] // personal scope ["capxul", "actor", "org", orgId, "insights", "summary"] // org scope ``` (`"pending"` replaces the actor segment while the scope is `undefined`.) Invalidated — for the same scope only — by every payment-requests and inbox mutation, and by money mutations (`useCapxulPay`, payouts, withdrawals, payroll runs) through the shared money-state invalidation. ## Client method [#client-method] This hook wraps [`client.account.insights.summary()`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).insights.summary()`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulCompleteOrganizationOnboarding (https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-organization-onboarding) Completes **organization onboarding** after the founder's first sign-in: persists their personal identity profile, creates the organization, triggers account setup, and returns the created `org` plus a `lifecycle` snapshot to route on. For individual users, use [`useCapxulCompletePersonalOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-personal-onboarding) instead. ## Import [#import] ```ts import { useCapxulCompleteOrganizationOnboarding } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useState } from "react"; import { useCapxulCompleteOrganizationOnboarding } from "@capxul/sdk-react"; import { useRouter } from "next/navigation"; function OrganizationOnboardingForm() { const router = useRouter(); const completeOrg = useCapxulCompleteOrganizationOnboarding(); const [organizationName, setOrganizationName] = useState(""); const [handle, setHandle] = useState(""); const [country, setCountry] = useState(""); const [ownerDisplayName, setOwnerDisplayName] = useState(""); return (
{ event.preventDefault(); const { org, lifecycle } = await completeOrg.mutateAsync({ organizationName: organizationName.trim(), handle: handle.trim(), country: country.trim(), ownerDisplayName: ownerDisplayName.trim(), }); console.log(`Created ${org.name} (${org.id})`); router.push(lifecycle.status === "ready" ? "/dashboard" : "/signup/provisioning"); }} > setOrganizationName(e.target.value)} /> setHandle(e.target.value)} /> setOwnerDisplayName(e.target.value)} /> setCountry(e.target.value)} /> {completeOrg.error ?

{completeOrg.error.message}

: null}
); } ``` Call it from a submit handler and route on `lifecycle.status`, exactly like personal onboarding. Keep the returned `org.id` — every later org call is scoped by it (the org-scoped hooks such as [`useCapxulOrgTreasury`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-treasury) take the org id as their first parameter). ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `CompleteOrganizationOnboardingInput`: | Field | Type | Description | | ------------------ | -------- | ----------------------------------------------------------------------------------------- | | `organizationName` | `string` | The organization's display name. Required. | | `handle` | `string` | The globally-unique org slug (e.g. `"acme-co"`). Required. | | `country` | `string` | ISO-3166 country code (e.g. `"GH"`). Required. | | `ownerDisplayName` | `string` | The founder's personal display name — this also creates their identity profile. Required. | ## Return type [#return-type] ```ts import { type UseCapxulCompleteOrganizationOnboardingReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is `{ org, lifecycle }`: * `org` — the created organization: its `id`, `name`, `handle`, your `role` in it, and its treasury `Account`. * `lifecycle` — the account-setup snapshot; `lifecycle.status` is the routing source of truth, documented on the [`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle) page. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates: * `["capxul", "profile"]` ([`useCapxulProfile`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-profile)) — the founder's profile is no longer `null`. * `["capxul", "accountLifecycle"]` ([`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle)). * `["capxul", "orgs"]` (`useCapxulOrgs`) — the org list now includes the new organization. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example a handle that is already taken, an invalid country code, or calling it without a signed-in session. ## Client method [#client-method] This hook wraps [`client.onboarding.completeOrganization(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulCompletePersonalOnboarding (https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-personal-onboarding) Completes **personal onboarding** after the first sign-in: persists the user's identity profile (display name, country, optional withdrawal address), triggers account setup, and returns a `lifecycle` snapshot to route on. For founders creating a company, use [`useCapxulCompleteOrganizationOnboarding`](https://capxul-sdk-docs.pages.dev/docs/hooks/onboarding/use-capxul-complete-organization-onboarding) instead. ## Import [#import] ```ts import { useCapxulCompletePersonalOnboarding } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useState } from "react"; import { useCapxulCompletePersonalOnboarding } from "@capxul/sdk-react"; import { useRouter } from "next/navigation"; function PersonalOnboardingForm() { const router = useRouter(); const completePersonal = useCapxulCompletePersonalOnboarding(); const [displayName, setDisplayName] = useState(""); const [country, setCountry] = useState(""); return (
{ event.preventDefault(); const { lifecycle } = await completePersonal.mutateAsync({ displayName: displayName.trim(), country: country.trim(), }); router.push(lifecycle.status === "ready" ? "/dashboard" : "/signup/provisioning"); }} > setDisplayName(e.target.value)} /> setCountry(e.target.value)} /> {completePersonal.error ?

{completePersonal.error.message}

: null}
); } ``` Call it from a submit handler and route on `lifecycle.status` — `"ready"` goes straight to the app, anything else goes to a provisioning screen driven by [`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle) (which self-polls until setup settles). Onboarding is a one-time step: before it completes, [`useCapxulProfile`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-profile) reads `null` for the fresh identity. ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `CompletePersonalOnboardingInput`: | Field | Type | Description | | ------------------- | --------------------- | --------------------------------------------------------------------------------------------------------- | | `displayName` | `string` | The user's display name. Required. | | `country` | `string` | ISO-3166 country code (e.g. `"GH"`). Required. | | `withdrawalAddress` | `string \| undefined` | Optional cash-out destination — a `0x`-prefixed address string, validated and normalized at the boundary. | ## Return type [#return-type] ```ts import { type UseCapxulCompletePersonalOnboardingReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is `{ lifecycle: AccountLifecycle }` — the post-onboarding account-setup snapshot. `lifecycle.status` is the single routing source of truth; its values are documented on the [`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle) page. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates: * `["capxul", "profile"]` ([`useCapxulProfile`](https://capxul-sdk-docs.pages.dev/docs/hooks/auth/use-capxul-profile)) — the profile is no longer `null`. * `["capxul", "accountLifecycle"]` ([`useCapxulAccountLifecycle`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-lifecycle)) — the provisioning screen reflects the new state. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example an invalid country code or withdrawal address, or calling it without a signed-in session. ## Client method [#client-method] This hook wraps [`client.onboarding.completePersonal(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulAddPayrollRosterLine (https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-add-payroll-roster-line) Adds a line to an organization's **payroll roster**: who gets paid, how much, and with what timing. Adding a line stores the standing instruction — no money moves until [`useCapxulRunPayroll`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-run-payroll) executes a run. Read the roster with [`useCapxulPayrollRoster`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-payroll-roster). ## Import [#import] ```ts import { useCapxulAddPayrollRosterLine } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulAddPayrollRosterLine, useCapxulOrgs } from "@capxul/sdk-react"; function AddEmployeeButton() { const orgs = useCapxulOrgs(); const org = orgs.data?.[0]; const addLine = useCapxulAddPayrollRosterLine(org?.id); if (org === undefined) return null; return ( ); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization whose roster the line is added to. Unlike a query, a mutation cannot be disabled — firing it while `orgId` is still `undefined` rejects with a `CapxulError`. The mutation takes a `PayrollRosterAddInput`: `employee` is a validated recipient `Ref` — one of `{ kind: "handle" }`, `{ kind: "email" }`, `{ kind: "orgHandle" }`, `{ kind: "capxulUserId" }`, or `{ kind: "payeeId" }` — never a raw address. `timing` is a `PaymentTiming` (`{ kind: "instant" }`, `{ kind: "scheduled", at }`, or a `stream` clause). `payslipTemplate` optionally attaches either a simple `{ title, memo? }` template or a full payment document envelope used for the payslip on each run. ## Return type [#return-type] ```ts import { type UseCapxulAddPayrollRosterLineReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the created `PayrollRosterLine` (with its `id` and `status: "active"`). The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the org's roster (`["capxul", "org", orgId, "payroll", "roster"]`) and the org's insights summary and history. Editing the roster moves no money, so balance and payments caches are untouched — those are invalidated by [`useCapxulRunPayroll`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-run-payroll). ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when `orgId` is still `undefined`, when the employee ref cannot be validated, when the signed-in member lacks spend authority in the org, or when the mutation fires while `` is still bootstrapping. ## Client method [#client-method] This hook wraps [`client.org(orgId).payroll.roster.add(input)`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulPayrollRoster (https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-payroll-roster) Reads an organization's **payroll roster**: the standing list of employees, amounts, and timing that a payroll run pays. Payroll is an organization paying many people at once — each run settles as real Payments. Manage lines with [`useCapxulAddPayrollRosterLine`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-add-payroll-roster-line), [`useCapxulUpdatePayrollRosterLine`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-update-payroll-roster-line), and [`useCapxulRemovePayrollRosterLine`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-remove-payroll-roster-line); execute a run with [`useCapxulRunPayroll`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-run-payroll). Payroll is graded **SDK-ready**: the roster and run primitives are published and tested, but the scheduling / streaming payroll product experience is not SDK-complete yet. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulPayrollRoster } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulOrgs, useCapxulPayrollRoster } from "@capxul/sdk-react"; function RosterList() { const orgs = useCapxulOrgs(); const orgId = orgs.data?.[0]?.id; const roster = useCapxulPayrollRoster(orgId); if (roster.isLoading) return

Loading roster…

; if (roster.error) return

{roster.error.message}

; return (
    {roster.data.map((line) => (
  • {line.amount.value} {line.amount.currency} — {line.status}
  • ))}
); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization whose roster to read. The query stays disabled while `orgId` is `undefined`, so you can pass the result of another query directly — no manual gating needed. ### options [#options] `{ enabled?: boolean } | undefined` Pass `enabled: false` to pause the query even when `orgId` is set. ## Return type [#return-type] ```ts import { type UseCapxulPayrollRosterReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an array of `PayrollRosterLine`: `employee` is a validated recipient `Ref` (`handle`, `email`, `orgHandle`, `capxulUserId`, or `payeeId` — never a raw address). Removed lines drop out of this list; a run pays only lines whose `status` is `"active"`. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "org", orgId, "payroll", "roster"]` — org-scoped, and invalidated automatically by the four payroll mutations for that org (add / update / remove roster line, and `useCapxulRunPayroll`) without touching other orgs. ## Client method [#client-method] This hook wraps [`client.org(orgId).payroll.roster.list()`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulRemovePayrollRosterLine (https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-remove-payroll-roster-line) Removes a line from an organization's **payroll roster**. The employee drops out of future runs; payments already created by earlier runs are unaffected. Read the roster with [`useCapxulPayrollRoster`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-payroll-roster) — removed lines no longer appear in it. ## Import [#import] ```ts import { useCapxulRemovePayrollRosterLine } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulRemovePayrollRosterLine } from "@capxul/sdk-react"; import type { OrgId } from "@capxul/sdk"; function RemoveLineButton({ orgId, rosterLineId }: { orgId: OrgId; rosterLineId: string }) { const removeLine = useCapxulRemovePayrollRosterLine(orgId); return ( ); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization that owns the roster line. Unlike a query, a mutation cannot be disabled — firing it while `orgId` is still `undefined` rejects with a `CapxulError`. The mutation input is the roster line id: | Field | Type | Description | | ------- | -------- | ---------------------------------------------- | | (input) | `string` | The `id` of the `PayrollRosterLine` to remove. | ## Return type [#return-type] ```ts import { type UseCapxulRemovePayrollRosterLineReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the removed `PayrollRosterLine` with `status: "ended"` — useful for an undo-style confirmation UI. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the org's roster (`["capxul", "org", orgId, "payroll", "roster"]`) and the org's insights summary and history. Removing a line moves no money, so balance and payments caches are untouched. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when `orgId` is still `undefined`, when the roster line does not exist in this org, when the signed-in member lacks spend authority in the org, or when the mutation fires while `` is still bootstrapping. ## Client method [#client-method] This hook wraps [`client.org(orgId).payroll.roster.remove(rosterLineId)`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulRunPayroll (https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-run-payroll) Executes a **payroll run**: pays every `"active"` line on the organization's [roster](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-payroll-roster) for one period, drawing from an org sub-account envelope. Unlike a [Transfer](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-transfer), payroll is real money leaving the organization — each roster line becomes a `Payment` with `paymentType: "payroll"`. Payroll is graded **SDK-ready**: the roster and run primitives are published and tested, but the scheduling / streaming payroll product experience is not SDK-complete yet. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulRunPayroll } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulRunPayroll } from "@capxul/sdk-react"; import type { OrgId, SubAccountId } from "@capxul/sdk"; function RunPayrollButton({ orgId, payrollEnvelopeId, }: { orgId: OrgId; payrollEnvelopeId: SubAccountId; }) { const runPayroll = useCapxulRunPayroll(orgId); return (
{runPayroll.data ?

{runPayroll.data.length} payments created

: null} {runPayroll.error ?

{runPayroll.error.message}

: null}
); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization running payroll. Unlike a query, a mutation cannot be disabled — firing it while `orgId` is still `undefined` rejects with a `CapxulError`. The mutation takes a `PayrollRunInput`: `from` is the org sub-account envelope the run draws from. `period` names the pay period and must be a `YYYY-MM` string (for example `"2026-07"`). Re-running the **same period** with an unchanged roster is idempotent: instead of paying everyone twice, the run returns the payments already created for that period. Changing the roster (or the `from` envelope) makes it a new run. ## Return type [#return-type] ```ts import { type UseCapxulRunPayrollReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is an array of `Payment` — one per active roster line, each with `paymentType: "payroll"` and the roster line's amount, recipient, and timing. Lines whose status is `"paused"` or `"ended"` are skipped. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the org's roster (`["capxul", "org", orgId, "payroll", "roster"]`) and the org's insights summary and history. Then, because a run creates real payments, it also invalidates the money state for each returned `Payment`: the payments list, that payment's detail, the org's treasury and org account, and the org's address book. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — notable modes: * `orgId` is still `undefined` when the mutation fires. * `period` is not a `YYYY-MM` string. * The roster has no `"active"` lines — a run needs at least one. * The run's total exceeds the signed-in member's org spending cap. The whole run is rejected; there are no partial payments. * The mutation fires while `` is still bootstrapping. ## Client method [#client-method] This hook wraps [`client.org(orgId).payroll.run(input)`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. (The lower-level `client.org(orgId).batchPayroll(input)` pays an ad-hoc list of recipients without a roster; see the [client reference](https://capxul-sdk-docs.pages.dev/docs/client).) # useCapxulUpdatePayrollRosterLine (https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-update-payroll-roster-line) Edits a line on an organization's **payroll roster**. Pass only the fields that change — everything else on the line stays as it is. The change applies to future runs; payments already created by [`useCapxulRunPayroll`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-run-payroll) are unaffected. Read the roster with [`useCapxulPayrollRoster`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-payroll-roster). ## Import [#import] ```ts import { useCapxulUpdatePayrollRosterLine } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulUpdatePayrollRosterLine } from "@capxul/sdk-react"; import type { OrgId, PayrollRosterLine } from "@capxul/sdk"; function GiveRaiseButton({ orgId, line }: { orgId: OrgId; line: PayrollRosterLine }) { const updateLine = useCapxulUpdatePayrollRosterLine(orgId); return ( ); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization that owns the roster line. Unlike a query, a mutation cannot be disabled — firing it while `orgId` is still `undefined` rejects with a `CapxulError`. The mutation takes a `UseCapxulUpdatePayrollRosterLineInput`: `input` is a `Partial` — any subset of `employee`, `amount`, `timing`, and `payslipTemplate`. Omitted fields are left unchanged. See [`useCapxulAddPayrollRosterLine`](https://capxul-sdk-docs.pages.dev/docs/hooks/payroll/use-capxul-add-payroll-roster-line) for what each field means. ## Return type [#return-type] ```ts import { type UseCapxulUpdatePayrollRosterLineReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the updated `PayrollRosterLine`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the org's roster (`["capxul", "org", orgId, "payroll", "roster"]`) and the org's insights summary and history. Editing the roster moves no money, so balance and payments caches are untouched. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when `orgId` is still `undefined`, when the roster line does not exist in this org, when a new employee ref cannot be validated, or when the mutation fires while `` is still bootstrapping. ## Client method [#client-method] This hook wraps [`client.org(orgId).payroll.roster.update(rosterLineId, input)`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulAssignRole (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-assign-role) Assigns a role to an existing member of an organization — changes their authority: what they can spend and manage inside the org. The member is identified by their `personalSafeAddress`, taken from the member's row in [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members); the role label must be one of the org's roles (see [`useCapxulOrgRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-roles)). ## Import [#import] ```ts import { useCapxulAssignRole } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { MemberView, OrgId } from "@capxul/sdk"; import { useCapxulAssignRole, useCapxulOrgRoles } from "@capxul/sdk-react"; function RolePicker({ orgId, member }: { orgId: OrgId; member: MemberView }) { const roles = useCapxulOrgRoles(orgId); const assignRole = useCapxulAssignRole(orgId); if (member.personalSafeAddress === null) return null; const memberSafeAddress = member.personalSafeAddress; return ( ); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization the member belongs to. Accepts `undefined` so you can wire it to a not-yet-resolved value, but firing the mutation before it resolves rejects with a `CapxulError`. The mutation takes an `AssignRoleInput`: | Field | Type | Description | | ------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `memberSafeAddress` | `Address` | The member's identity key — take it from the member's `personalSafeAddress` on the [`MemberView`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) row. | | `role` | `string` | The role label to grant — one of the org's roles. The label maps deterministically to the enforced `roleKey`. | ## Return type [#return-type] ```ts import { type UseCapxulAssignRoleReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the updated `MemberView` — see [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) for the field list. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the org's member list (`["capxul", "org", orgId, "members"]`), so [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) refetches with the member's new role. The role definitions themselves are unchanged, so the roles list is not invalidated. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the mutation fires while `orgId` is still `undefined`, or when the grant is rejected. ## Client method [#client-method] This hook wraps [`client.org(orgId).assignRole(input)`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulCreateOrg (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-create-org) Creates an organization: claims its globally-unique **handle**, seeds the initial role set from a template, and returns the new org with you as its Owner. The new org appears in [`useCapxulOrgs`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs) automatically. ## Import [#import] ```ts import { useCapxulCreateOrg } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useState } from "react"; import { useCapxulCreateOrg } from "@capxul/sdk-react"; function CreateOrgForm({ onCreated }: { onCreated: (orgId: string) => void }) { const createOrg = useCapxulCreateOrg(); const [name, setName] = useState(""); const [handle, setHandle] = useState(""); return (
{ event.preventDefault(); const org = await createOrg.mutateAsync({ name, handle, template: "Startup", }); onCreated(org.id); }} > setName(event.target.value)} /> setHandle(event.target.value)} /> {createOrg.error ?

{createOrg.error.message}

: null}
); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `CreateOrgInput`: | Field | Type | Description | | ---------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | `string` | The org's display name. | | `handle` | `string` | The org's handle — normalized (`^[a-z0-9-]{3,32}$`) and globally unique; claimed at creation. | | `template` | `OrgTemplate` | `"Solo" \| "Startup" \| "Custom"` — seeds the initial role set. | | `country` | `string \| undefined` | Optional country, stored as provided. | | `roles` | `readonly RoleDefinition[] \| undefined` | The authored role set for `template: "Custom"`. See [`useCapxulOrgRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-roles) for the `RoleDefinition` shape. | ## Return type [#return-type] ```ts import { type UseCapxulCreateOrgReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the created `OrgView` — see [`useCapxulOrgs`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs) for the field list. Your `role` in the new org is `"Owner"`, and its treasury starts at zero. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the org list (`["capxul", "orgs"]`), so [`useCapxulOrgs`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs) refetches and the new org shows up without manual wiring. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the handle does not match the required shape or is already claimed. ## Client method [#client-method] This hook wraps [`client.createOrg(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulInviteMember (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-invite-member) Invites a person into an organization **by email**, granting them a role. Membership is authority: paying someone from the org does not make them a member — inviting them with a role does. The email does not need to belong to an existing Capxul user; inviting a new address creates a `pending` membership that resolves when they sign in. See [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) for reading the resulting member list. ## Import [#import] ```ts import { useCapxulInviteMember } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useState } from "react"; import type { OrgId } from "@capxul/sdk"; import { useCapxulInviteMember } from "@capxul/sdk-react"; function InviteForm({ orgId }: { orgId: OrgId }) { const invite = useCapxulInviteMember(orgId); const [email, setEmail] = useState(""); return (
{ event.preventDefault(); await invite.mutateAsync({ email, role: "Member" }); setEmail(""); }} > setEmail(event.target.value)} /> {invite.error ?

{invite.error.message}

: null}
); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization to invite into. Accepts `undefined` so you can wire it to a not-yet-resolved value, but firing the mutation before it resolves rejects with a `CapxulError`. The mutation takes an `InviteMemberInput`: | Field | Type | Description | | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `email` | `string` | The invitee's email. They do not need to be a Capxul user yet. | | `role` | `string` | The role label the invitee is granted on accept — one of the org's roles (see [`useCapxulOrgRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-roles)). | ## Return type [#return-type] ```ts import { type UseCapxulInviteMemberReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the new `MemberView` — see [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) for the field list. A fresh invite starts with status `pending` and stays that way until the invitee signs in and provisioning completes. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the org's member list (`["capxul", "org", orgId, "members"]`), so [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) refetches with the new `pending` row. Other orgs' caches are untouched. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the mutation fires while `orgId` is still `undefined`, or when the invite is rejected. ## Client method [#client-method] This hook wraps [`client.org(orgId).invite(input)`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulOrgDeployRoles (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-deploy-roles) Deploys an organization's seeded role set, making it live so role grants and spend caps are enforced. This is the org's **deploy-readiness** surface, not its money — the org's balance lives on [`useCapxulOrgTreasury`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-treasury). Read the current role set with [`useCapxulOrgRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-roles). ## Import [#import] ```ts import { useCapxulOrgDeployRoles } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { OrgId } from "@capxul/sdk"; import { useCapxulOrgDeployRoles } from "@capxul/sdk-react"; function DeployRolesButton({ orgId }: { orgId: OrgId }) { const deployRoles = useCapxulOrgDeployRoles(); return (
{deployRoles.error ?

{deployRoles.error.message}

: null}
); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes the `OrgId` of the organization whose role set to deploy: ```ts deployRoles.mutate(orgId); ``` ## Return type [#return-type] ```ts import { type UseCapxulOrgDeployRolesReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the deployed role list — `readonly RoleView[]`, the same shape returned by [`useCapxulOrgRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-roles). The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the org's role list (`["capxul", "org", orgId, "roles"]`), the org detail (`["capxul", "org", orgId]`), and the org list (`["capxul", "orgs"]`), so the readiness change is reflected everywhere the org appears. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when role deployment is not available for the key's environment. ## Client method [#client-method] This hook wraps [`client.org(orgId).deployRoles()`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulOrgMembers (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) Reads an organization's **members** — the people who hold authority inside the org: a role and the caps that come with it. Membership is about authority, not money flow: being paid by an org does not make someone a member; inviting them with a role via [`useCapxulInviteMember`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-invite-member) does. ## Import [#import] ```ts import { useCapxulOrgMembers } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { OrgId } from "@capxul/sdk"; import { useCapxulOrgMembers } from "@capxul/sdk-react"; function MemberList({ orgId }: { orgId: OrgId }) { const members = useCapxulOrgMembers(orgId); if (members.isLoading) return

Loading members…

; if (members.error) return

{members.error.message}

; if (members.data.length === 0) return

No members yet.

; return (
    {members.data.map((member) => (
  • {member.email} · {member.role} · {member.status}
  • ))}
); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization whose members to read. The query stays disabled while `orgId` is `undefined`, so you can pass the result of another query directly — no manual gating needed. ### options [#options] `UseCapxulOrgMembersOptions | undefined` ## Return type [#return-type] ```ts import { type UseCapxulOrgMembersReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is a list of `MemberView` records: | Field | Type | Description | | --------------------- | ----------------- | --------------------------------------------------------------------------------------- | | `orgId` | `OrgId` | The org this membership belongs to. | | `email` | `Email` | The member's email — the universal entry point for inviting. | | `name` | `string \| null` | Display name, when known. | | `personalSafeAddress` | `Address \| null` | The member-identity key. `null` while the member is still `pending`. | | `role` | `string` | The member's role label. | | `roleKey` | `RoleKey \| null` | The enforced key the role label maps to. `null` until the grant lands. | | `status` | `MemberStatus` | `"pending" \| "pending_safe" \| "pending_grant" \| "active" \| "revoked" \| "expired"`. | | `grantTxHash` | `string \| null` | Set once the member's role grant has been recorded. | | `revokeTxHash` | `string \| null` | Set once the member's removal has been recorded. | A member starts `pending` when invited — including people who are not Capxul users yet — moves through provisioning (`pending_safe`, `pending_grant`), and becomes `active` once their role is granted. `revoked` means they were removed; `expired` means a pending invitation timed out. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "org", orgId, "members"]` — org-scoped. Invalidated on success by [`useCapxulInviteMember`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-invite-member), [`useCapxulAssignRole`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-assign-role), and [`useCapxulRemoveMember`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-remove-member) for the same org. ## Client method [#client-method] This hook wraps [`client.org(orgId).members()`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulOrgRoles (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-roles) Reads the **roles** defined on an organization — each a label with a definition: spend caps, sub-account scope, and management permissions. This is a read; making the role set enforceable is [`useCapxulOrgDeployRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-deploy-roles), and granting a role to a member is [`useCapxulAssignRole`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-assign-role). ## Import [#import] ```ts import { useCapxulOrgRoles } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { OrgId } from "@capxul/sdk"; import { useCapxulOrgRoles } from "@capxul/sdk-react"; function RoleList({ orgId }: { orgId: OrgId }) { const roles = useCapxulOrgRoles(orgId); if (roles.isLoading) return

Loading roles…

; if (roles.error) return

{roles.error.message}

; return (
    {roles.data.map((role) => { const perTx = role.definition.spend?.perTx; return (
  • {role.label} {perTx ? ` — up to ${perTx.value} ${perTx.currency} per transaction` : " — no per-transaction cap"}
  • ); })}
); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization whose roles to read. The query stays disabled while `orgId` is `undefined`, so you can pass the result of another query directly — no manual gating needed. ### options [#options] `UseCapxulOrgRolesOptions | undefined` ## Return type [#return-type] ```ts import { type UseCapxulOrgRolesReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is a list of `RoleView` records: | Field | Type | Description | | ------------ | ---------------- | ----------------------------------------------------------- | | `orgId` | `OrgId` | The org the role is defined on. | | `label` | `string` | The role's display label (for example `"Owner"`). | | `roleKey` | `RoleKey` | The enforced key, derived deterministically from the label. | | `definition` | `RoleDefinition` | The full role definition (below). | A `RoleDefinition` carries: | Field | Type | Description | | ------------------ | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `label` | `string` | The role's label. | | `spend` | `RoleSpendCap \| undefined` | Platform-enforced spend caps: `perTx` and `perDay` (both `Money`) plus `toRecipients` (`"anyone"` or an allowed recipient list). An omitted axis is unbounded — the Owner role omits all of them. | | `subAccounts` | `{ scope: "all" \| readonly SubAccountId[] } \| undefined` | Which sub-accounts the role can draw from. | | `canManageMembers` | `boolean \| undefined` | Whether the role can invite and remove members. | | `canManageRoles` | `boolean \| undefined` | Whether the role can manage the role set itself. | The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "org", orgId, "roles"]` — org-scoped. Invalidated on success by [`useCapxulOrgDeployRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-deploy-roles) for the same org. ## Client method [#client-method] This hook wraps [`client.org(orgId).roles()`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulOrgTreasury (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-treasury) Reads an organization's **treasury** — the org's `Account`, with balance and available balance. This is the money view of the org, not its deploy-readiness status. ## Import [#import] ```ts import { useCapxulOrgTreasury } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulOrgs, useCapxulOrgTreasury } from "@capxul/sdk-react"; function TreasuryCard() { const orgs = useCapxulOrgs(); const orgId = orgs.data?.[0]?.id; const treasury = useCapxulOrgTreasury(orgId); if (treasury.isLoading) return

Loading treasury…

; if (treasury.error) return

{treasury.error.message}

; return (

{treasury.data.balance.value} {treasury.data.balance.currency}

); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization to read. The query stays disabled while `orgId` is `undefined`, so you can pass the result of another query directly — no manual gating needed. ### options [#options] `UseCapxulOrgTreasuryOptions | undefined` ## Return type [#return-type] ```ts import { type UseCapxulOrgTreasuryReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is the treasury `Account`. Note this is the org's **money** (`Account` with balances), never the deploy-readiness ladder — those live on the org lifecycle surface. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "org", orgId, "treasury"]` — org-scoped, so org money mutations (for example `useCapxulRunPayroll` for that org) invalidate it without touching other orgs. ## Client method [#client-method] This hook wraps [`client.org(orgId).treasury()`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulOrg (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org) Reads one organization from your org list, by id. Returns `null` when the org is not in your list — an org you do not belong to is indistinguishable from one that does not exist. For the full list use [`useCapxulOrgs`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs). ## Import [#import] ```ts import { useCapxulOrg } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { OrgId } from "@capxul/sdk"; import { useCapxulOrg } from "@capxul/sdk-react"; function OrgHeader({ orgId }: { orgId: OrgId }) { const org = useCapxulOrg(orgId); if (org.isLoading) return

Loading organization…

; if (org.error) return

{org.error.message}

; if (org.data === null) return

Organization not found.

; return (

{org.data.name}

@{org.data.handle} · your role: {org.data.role}

); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization to read. The query stays disabled while `orgId` is `undefined`, so you can pass a not-yet-resolved value (a route param, the result of another query) directly — no manual gating needed. ### options [#options] `UseCapxulOrgOptions | undefined` ## Return type [#return-type] ```ts import { type UseCapxulOrgReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is the `OrgView` for the requested org — see [`useCapxulOrgs`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs) for the field list — or `null` when the org is not in your list. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "org", orgId]` — the root of the org-scoped key hierarchy (`members`, `roles`, `treasury`, … nest under it). Invalidated on success by [`useCapxulOrgDeployRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-deploy-roles) for the same org. ## Client method [#client-method] This hook wraps [`client.orgs()`](https://capxul-sdk-docs.pages.dev/docs/client) and narrows the result to the requested `orgId` — the core SDK has no single-org read on its surface. # useCapxulOrganizationAccount (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-organization-account) Reads an organization's **account summary** — one flat record with the account id, the org's account address, its `balance`, and its `available` balance. The money numbers are the same as [`useCapxulOrgTreasury`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-treasury); this hook adds the org's address on top. Like the treasury, this is the org's money view — deploy readiness lives on [`useCapxulOrgDeployRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-deploy-roles). ## Import [#import] ```ts import { useCapxulOrganizationAccount } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { OrgId } from "@capxul/sdk"; import { useCapxulOrganizationAccount } from "@capxul/sdk-react"; function OrgAccountCard({ orgId }: { orgId: OrgId }) { const account = useCapxulOrganizationAccount(orgId); if (account.isLoading) return

Loading account…

; if (account.error) return

{account.error.message}

; return (

{account.data.balance.value} {account.data.balance.currency}

Available: {account.data.available.value}{" "} {account.data.available.currency}

); } ``` ## Parameters [#parameters] ### organizationId [#organizationid] `OrgId | undefined` The organization to read. The query stays disabled while `organizationId` is `undefined`, so you can pass the result of another query directly — no manual gating needed. ### options [#options] `UseCapxulOrganizationAccountOptions | undefined` ## Return type [#return-type] ```ts import { type UseCapxulOrganizationAccountReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an `OrganizationAccount`: | Field | Type | Description | | ----------- | ---------------- | ------------------------------------------------------------------------------------- | | `id` | `string` | The org account's id. | | `address` | `string \| null` | The org's account address. `null` when the org cannot be resolved from your org list. | | `balance` | `Money` | The org's balance. | | `available` | `Money` | The spendable portion of the balance. | The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "org", orgId, "account"]` — org-scoped, invalidated alongside the treasury key by org money mutations (for example `useCapxulRunPayroll` for that org) without touching other orgs. ## Client method [#client-method] This hook wraps [`client.org(orgId).account.get()`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. The client composes the record from the treasury read plus your org list. # useCapxulOrganizationAuditLog (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-organization-audit-log) Reads an organization's **audit log** — a list of administrative actions recorded against the org, each with an action name, actor, and human-readable summary. It sits alongside the other org-scoped reads such as [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members). In the published alpha the underlying client method is not implemented: every fetch fails with a not-implemented [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling). The hook and its types are published so you can wire the surface today, but do not ship a screen that depends on this data yet. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulOrganizationAuditLog } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { OrgId } from "@capxul/sdk"; import { useCapxulOrganizationAuditLog } from "@capxul/sdk-react"; function AuditLog({ orgId }: { orgId: OrgId }) { const auditLog = useCapxulOrganizationAuditLog(orgId); if (auditLog.isLoading) return

Loading audit log…

; if (auditLog.error) return

{auditLog.error.message}

; return (
    {auditLog.data.map((item) => (
  • {item.summary} {item.actor ? `— ${item.actor}` : null}
  • ))}
); } ``` ## Parameters [#parameters] ### organizationId [#organizationid] `OrgId | undefined` The organization whose audit log to read. The query stays disabled while `organizationId` is `undefined`, so you can pass the result of another query directly — no manual gating needed. ### options [#options] `UseCapxulOrganizationAuditLogOptions | undefined` ## Return type [#return-type] ```ts import { type UseCapxulOrganizationAuditLogReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is a list of `OrganizationAuditLogItem` records: | Field | Type | Description | | ---------------- | -------------------------------------- | -------------------------------------------------- | | `id` | `string` | The log entry's id. | | `organizationId` | `string` | The org the action was recorded against. | | `action` | `string` | Machine-readable action name. | | `actor` | `string \| null` | Who performed the action, when known. | | `summary` | `string` | Human-readable description of the action. | | `createdAt` | `number` | Numeric timestamp of when the action was recorded. | | `metadata` | `Record \| undefined` | Optional structured detail. | The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "org", orgId, "auditLog"]` — org-scoped, keyed under the same hierarchy as the org's other reads. ## Errors [#errors] Today every fetch fails: the published client resolves a not-implemented [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) for this read. Treat `error` as the steady state until [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status) grades this capability as live. ## Client method [#client-method] This hook wraps [`client.org(orgId).auditLog()`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulOrgs (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs) Lists the organizations you belong to — one `OrgView` per org, with its name, handle, your role inside it, and a treasury snapshot. Pair it with [`useCapxulOrg`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org) to read a single org, or [`useCapxulCreateOrg`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-create-org) to start a new one. ## Import [#import] ```ts import { useCapxulOrgs } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulOrgs } from "@capxul/sdk-react"; function OrgList() { const orgs = useCapxulOrgs(); if (orgs.isLoading) return

Loading organizations…

; if (orgs.error) return

{orgs.error.message}

; return (
    {orgs.data.map((org) => (
  • {org.name} @{org.handle} — your role: {org.role}
  • ))}
); } ``` ## Parameters [#parameters] ### options [#options] `UseCapxulOrgsOptions | undefined` This is a session-scoped authenticated read. Firing it before the session has settled surfaces a spurious `NOT_AUTHENTICATED` error, so gate it on auth readiness when the surface can render before sign-in completes: ```tsx const session = useCapxulSession(); const orgs = useCapxulOrgs({ enabled: session.data !== null }); ``` ## Return type [#return-type] ```ts import { type UseCapxulOrgsReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is a list of `OrgView` records — the orgs you are a member of: | Field | Type | Description | | ------------- | --------- | --------------------------------------------------------------------- | | `id` | `OrgId` | The org's identifier. Pass it to every org-scoped hook. | | `name` | `string` | The org's display name. | | `handle` | `string` | The globally-unique org handle claimed at creation. | | `safeAddress` | `Address` | The org account's address. | | `role` | `string` | **Your** role label within this org. | | `treasury` | `Account` | A snapshot of the org's treasury — its balance and available balance. | The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "orgs"]` — invalidated on success by [`useCapxulCreateOrg`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-create-org) and [`useCapxulOrgDeployRoles`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-deploy-roles). ## Client method [#client-method] This hook wraps [`client.orgs()`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulRemoveMember (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-remove-member) Removes a member from an organization — revokes their authority: their role and the spend allowances that came with it. The member is identified by their `personalSafeAddress`, taken from the member's row in [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members). To change what a member can do without removing them, use [`useCapxulAssignRole`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-assign-role). ## Import [#import] ```ts import { useCapxulRemoveMember } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import type { MemberView, OrgId } from "@capxul/sdk"; import { useCapxulRemoveMember } from "@capxul/sdk-react"; function RemoveMemberButton({ orgId, member, }: { orgId: OrgId; member: MemberView; }) { const removeMember = useCapxulRemoveMember(orgId); if (member.personalSafeAddress === null) return null; const memberSafeAddress = member.personalSafeAddress; return ( ); } ``` ## Parameters [#parameters] ### orgId [#orgid] `OrgId | undefined` The organization to remove the member from. Accepts `undefined` so you can wire it to a not-yet-resolved value, but firing the mutation before it resolves rejects with a `CapxulError`. The mutation takes a `RemoveMemberInput`: | Field | Type | Description | | ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `memberSafeAddress` | `Address` | The member's identity key — take it from the member's `personalSafeAddress` on the [`MemberView`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) row (that field is `null` while the member is still `pending`). | ## Return type [#return-type] ```ts import { type UseCapxulRemoveMemberReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success there is no payload — `data` is `void`; read the updated roster from the refetched member list. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the org's member list (`["capxul", "org", orgId, "members"]`), so [`useCapxulOrgMembers`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-org-members) refetches. Other orgs' caches are untouched. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the mutation fires while `orgId` is still `undefined`, or when the removal is rejected. ## Client method [#client-method] This hook wraps [`client.org(orgId).removeMember(input)`](https://capxul-sdk-docs.pages.dev/docs/client) — the entity-scoped org bundle on the core SDK. # useCapxulSwitchActingEntity (https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-switch-acting-entity) Switches the **acting entity** — whether your UI is operating as the personal account or as one of your organizations. In the Capxul SDK there is no global "acting as" state: every org read and mutation names its org explicitly (`client.org(orgId)`), so the acting entity is state your app owns. This hook is the stable mutation seam to drive that switch through — it gives you `isPending` for disabling the switcher and a promise for sequencing navigation — and it performs no network call. List the orgs to switch between with [`useCapxulOrgs`](https://capxul-sdk-docs.pages.dev/docs/hooks/organizations/use-capxul-orgs). ## Import [#import] ```ts import { useCapxulSwitchActingEntity } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useState } from "react"; import type { OrgId } from "@capxul/sdk"; import { useCapxulOrgs, useCapxulSwitchActingEntity } from "@capxul/sdk-react"; import { toOrgId } from "@capxul/types"; function EntitySwitcher({ onSwitch, }: { onSwitch: (orgId: OrgId | undefined) => void; }) { const orgs = useCapxulOrgs(); const switchEntity = useCapxulSwitchActingEntity(); const [value, setValue] = useState("personal"); const handleChange = async (next: string) => { setValue(next); const orgId = next === "personal" ? undefined : toOrgId(next); await switchEntity.mutateAsync(orgId === undefined ? undefined : { orgId }); onSwitch(orgId); }; return ( ); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `SwitchActingEntityInput`, or `undefined` to switch back to acting as yourself: ## Return type [#return-type] ```ts import { type UseCapxulSwitchActingEntityReturn } from "@capxul/sdk-react"; // UseMutationResult ``` There is no payload — `data` is `void`. Because this mutation never talks to the server, its error type is a plain `Error` rather than a `CapxulError`, and in practice it does not fail. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] This mutation invalidates nothing. Org-scoped queries are keyed by org id (`["capxul", "org", orgId, …]`), so cached data for the personal account and for each org coexist — nothing needs flushing when the acting entity changes. ## Client method [#client-method] This hook wraps no client method. The core SDK scopes every call explicitly via [`client.org(orgId)`](https://capxul-sdk-docs.pages.dev/docs/client), so switching the acting entity is purely a consumer-side state change. # useCapxulCancelPayment (https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-cancel-payment) Cancels a payment by id. Pair it with [`useCapxulPayment`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payment) to read the payment's status before and after. In the published alpha, `client.payments.cancel` is a stub: **every call fails** with a `NOT_IMPLEMENTED` [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) ("payments.cancel is not yet implemented"). The hook and its types are published so UI can be wired ahead of the implementation, but no payment can be cancelled through this surface today. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulCancelPayment } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulCancelPayment } from "@capxul/sdk-react"; function CancelButton({ paymentId }: { paymentId: string }) { const cancelPayment = useCapxulCancelPayment(); return (
{cancelPayment.error ?

{cancelPayment.error.message}

: null}
); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes the payment id: ### paymentId [#paymentid] `string` The id of the payment to cancel, as returned by [`useCapxulPay`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-pay) or listed by [`useCapxulPayments`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payments). ## Return type [#return-type] ```ts import { type UseCapxulCancelPaymentReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` will be the updated `Payment`. In the current alpha the mutation never succeeds (see the callout above). The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation refreshes the personal money state: the payments list (and payment detail queries under it), the personal [account balance](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance), and the personal insights summary and history. Until the client method is implemented, `onSuccess` is unreachable and nothing is invalidated. ## Errors [#errors] Every call currently fails with `NOT_IMPLEMENTED` — surfaced as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync`. ## Client method [#client-method] This hook wraps [`client.payments.cancel(paymentId)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulPay (https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-pay) Sends money from the signed-in user's Account to a **recipient** — a handle, email, organization, or saved payee. For cash-out to a saved destination use [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout); for an external wallet use [`useCapxulWithdraw`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-withdraw). Read results back with [`useCapxulPayments`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payments). ## Import [#import] ```ts import { useCapxulPay } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulPay } from "@capxul/sdk-react"; function PayButton({ email }: { email: string }) { const pay = useCapxulPay(); return (
{pay.error ?

{pay.error.message}

: null} {pay.data ?

Payment {pay.data.id} is {pay.data.status}.

: null}
); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `PaymentsPayInput`: **`to` — the recipient.** Recipients are typed references, never raw addresses. The accepted forms are: * `{ kind: "handle", handle }` — a handle you have reserved for one of your payees (a leading `@` is tolerated and stripped). * `{ kind: "email", email }` — an email address. * `{ kind: "organization", handle }` — an organization by its handle. * `{ kind: "payee", id }` — a saved payee by id. A bare EVM address (`0x` + 40 hex characters) is **rejected by design** with an `INVALID_INPUT` error — the SDK and the backend both enforce it. The one lane that accepts a raw address is [`useCapxulWithdraw`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-withdraw). The `TargetReference` union also has a `destination` variant, but `pay` rejects it with `NOT_IMPLEMENTED` — use [`useCapxulPayout`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout) for saved destinations. **`actor`.** Only the personal actor can pay in the current alpha. Passing an organization actor fails with `NOT_IMPLEMENTED` (`payments.pay.organizationActor`). See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). **`timing`.** Omitted means `instant` — the direct-send path. A `{ kind: "scheduled", at }` or `{ kind: "stream", startsAt, endsAt, cliffAt? }` clause turns the payment into a **Commitment**: an escrowed, cancellable, redirectable payment that the recipient claims as it vests. Timestamps are epoch milliseconds. Commitment payments enter the ledger as `scheduled`, `streaming`, or `pending_claim` instead of `pending`. An instant, irreversible payment is only permitted to a **validated** recipient. Paying anyone not yet validated always goes through a recoverable Commitment (escrowed, cancellable, redirectable). The SDK enforces this; it is not a bug to work around. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). **`document` and `lineItems`.** An attached document (for example an invoice) is hashed, stored, and committed with the payment; the returned `Payment` carries only a document reference. The backend enforces the bindings: an attached invoice's amount due must equal the amount being paid, `paymentType` (when given) must match the document kind, and `lineItems` are accepted only with an invoice document whose committed line-items hash and total they reproduce exactly. ## Return type [#return-type] ```ts import { type UseCapxulPayReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the recorded `Payment`: its id, `status`, `amount`, the recipient's kind and label, attached document references, the `timing` clause, and the `released` / `availableToClaim` amounts for commitments. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation refreshes the personal money state: the payments list (and payment detail queries under it), the personal [account balance](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance), the personal insights summary and history, and the personal address book (paying someone new records them as a counterparty). ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync`. Notable modes: `INVALID_INPUT` for a raw `0x` address, an empty or unresolvable recipient (unknown handle, payee, or org handle), or an invoice document that disagrees with the amount or line items; `NOT_IMPLEMENTED` for an organization actor or a `destination` target. ## Client method [#client-method] This hook wraps [`client.payments.pay(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulPayment (https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payment) Reads a single **Payment** by id — for a payment detail screen or for polling one payment's status. List the ledger with [`useCapxulPayments`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payments). ## Import [#import] ```ts import { useCapxulPayment } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulPayment } from "@capxul/sdk-react"; function PaymentDetail({ paymentId }: { paymentId: string }) { const payment = useCapxulPayment(paymentId); if (payment.isLoading) return

Loading payment…

; if (payment.error) return

{payment.error.message}

; if (payment.data === null) return

Payment not found.

; return (

{payment.data.amount.value} {payment.data.amount.currency} to{" "} {payment.data.recipient.label} — {payment.data.status}

); } ``` ## Parameters [#parameters] ### paymentId [#paymentid] `string | undefined` The payment to read. The query stays disabled while `paymentId` is `undefined`, so you can pass a value that arrives asynchronously (for example from route params or another query) without manual gating. ### options [#options] `{ enabled?: boolean } | undefined` Pass `enabled: false` to skip the read until your UI is ready for it. ## Return type [#return-type] ```ts import { type UseCapxulPaymentReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is the `Payment`, or `null` when no payment with that id is visible to the signed-in user — "not found" is data, not an error. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "payments", paymentId]` — nested under the payments prefix, so every money mutation that refreshes [`useCapxulPayments`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payments) refreshes payment details too. Mutations that return a specific payment (`useCapxulPay`, `useCapxulPayout`, `useCapxulWithdraw`) also invalidate that payment's key directly. ## Client method [#client-method] This hook wraps [`client.payments.get(paymentId)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulPayments (https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payments) Reads the signed-in user's **payment ledger** — every `Payment` the user owns, including instant sends, commitments, payouts, and withdrawals. Read a single payment with [`useCapxulPayment`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payment); create payments with [`useCapxulPay`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-pay). ## Import [#import] ```ts import { useCapxulPayments } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulPayments } from "@capxul/sdk-react"; function PaymentList() { const payments = useCapxulPayments(); if (payments.isLoading) return

Loading payments…

; if (payments.error) return

{payments.error.message}

; return (
    {payments.data.map((payment) => (
  • {payment.recipient.label} — {payment.amount.value}{" "} {payment.amount.currency} ({payment.status})
  • ))}
); } ``` ## Parameters [#parameters] ### options [#options] `{ enabled?: boolean } | undefined` Pass `enabled: false` to skip the read until your UI is ready for it. The query is also disabled automatically until the provider finishes bootstrapping. ## Return type [#return-type] ```ts import { type UseCapxulPaymentsReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is a read-only array of `Payment`: id, `status` (`pending`, `submitted`, `pending_claim`, `scheduled`, `streaming`, `settled`, `cancelled`, `redirected`, `expired`, or `failed`), `amount` (a decimal money value, never raw token units), `paymentType`, the recipient's kind and label, attached document references, the `timing` clause, and the `released` / `availableToClaim` amounts for commitments. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "payments"]` — invalidated automatically by money mutations (`useCapxulPay`, `useCapxulPayout`, `useCapxulWithdraw`, payroll runs, …), so the ledger refreshes after every send. Note `useCapxulTransfer` does not invalidate it — a Transfer stays inside your Account and never appears in the payments ledger. ## Client method [#client-method] This hook wraps [`client.payments.list()`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulPayout (https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-payout) Pays out to a **saved destination** selected by its opaque id — the caller never re-enters account details on this path. Save destinations with [`useCapxulAddDestination`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-add-destination) and list them with [`useCapxulDestinations`](https://capxul-sdk-docs.pages.dev/docs/hooks/destinations/use-capxul-destinations). The backend currently settles **wallet** (`external_account`) destinations only. A payout to a bank or mobile-money destination is rejected with `INVALID_INPUT` ("payout settlement requires a wallet destination") — the destination *metadata* can be stored, but there is no bank / mobile-money cash-out rail yet. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { useCapxulPayout } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulDestinations, useCapxulPayout } from "@capxul/sdk-react"; function PayoutButton() { const destinations = useCapxulDestinations({ kind: "external_account" }); const payout = useCapxulPayout(); const wallet = destinations.data?.[0]; if (!wallet) return

No wallet destination saved yet.

; return (
{payout.error ?

{payout.error.message}

: null}
); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `PaymentsPayoutInput`: **`destinationId`** must name a destination that belongs to the acting entity and has not been removed; otherwise the mutation fails with `INVALID_INPUT` ("destination not found"). **`actor`.** The input accepts an organization actor, but the backend rejects org payouts today ("org destination payout requires org settlement support") — payouts are personal-only in the current alpha. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Return type [#return-type] ```ts import { type UseCapxulPayoutReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the recorded `Payment` in `pending` status with instant timing. Its recipient kind is `external_address` and its label is the **redacted** destination address — the full address is never returned. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation refreshes the money state for the acting entity: the payments list (and payment detail queries under it), the insights summary and history, and the personal [account balance](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance) (or the org treasury and account, once org payouts exist). ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync`. Notable modes: `INVALID_INPUT` when the destination id is unknown, removed, or not a wallet destination, and when an organization actor is passed. ## Client method [#client-method] This hook wraps [`client.payments.payout(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulWithdraw (https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-withdraw) Cashes out to an **external wallet**. This is the one lane on the payments surface that takes a raw `0x` address — the [`useCapxulPay`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-pay) lane rejects bare addresses by design. Every withdrawal carries a committed **Withdrawal document** that records the audited destination and amount. ## Import [#import] ```ts import { useCapxulWithdraw } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulWithdraw } from "@capxul/sdk-react"; import type { PaymentsWithdrawInput } from "@capxul/sdk"; function WithdrawButton({ input }: { input: PaymentsWithdrawInput }) { const withdraw = useCapxulWithdraw(); return (
{withdraw.error ?

{withdraw.error.message}

: null} {withdraw.data ? (

Sent to {withdraw.data.recipient.label} — {withdraw.data.status}

) : null}
); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation takes a `PaymentsWithdrawInput`: **`to`** must be an external wallet address: `0x` followed by exactly 40 hex characters. Anything else fails with `INVALID_INPUT`. **`document`** is **required** — the Withdrawal document envelope (`WithdrawalDocumentEnvelopeV1`) minted by the surface that prepares the withdrawal. The backend binds it to the request: the document's destination address must equal `to`, and its committed amount must equal `amount` — a mismatch is rejected with `INVALID_INPUT`, so the audited cash-out record can never disagree with where or how much money is sent. Documents of any other kind are rejected. ## Return type [#return-type] ```ts import { type UseCapxulWithdrawReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is a `Payment` in `pending` status with instant timing. Its recipient kind is `external_address` and its label is the **redacted** destination address — the full address never appears on the returned payment. The committed Withdrawal document is referenced in `documents`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation refreshes the personal money state: the payments list (and payment detail queries under it), the personal [account balance](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance), and the personal insights summary and history. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — `INVALID_INPUT` when `to` is not a valid external address, when the document is missing or not a Withdrawal document, or when the document's destination or amount disagrees with the request. ## Client method [#client-method] This hook wraps [`client.payments.withdraw(input)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulCancelRequest (https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-cancel-request) Cancels a payment request an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) has issued. The request stops being payable and its status moves to `cancelled`. List issued requests with [`useCapxulRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests); issue new ones with [`useCapxulIssueRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-issue-request). ## Import [#import] ```ts import { capxulAccountScope, useCapxulCancelRequest } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulCancelRequest } from "@capxul/sdk-react"; function CancelRequestButton({ requestId }: { requestId: string }) { const cancelRequest = useCapxulCancelRequest(capxulAccountScope); return ( ); } ``` To cancel a request issued by an organization, scope the hook with `capxulOrgScope(orgId)` instead. ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` — defaults to `capxulAccountScope` Which actor's request to cancel. An explicit `undefined` also falls back to the personal scope — gate org-scoped controls on the org id being loaded. See [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). ### Mutation input [#mutation-input] The mutation takes the request id (`string`), as returned by [`useCapxulRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests) or [`useCapxulIssueRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-issue-request). ## Return type [#return-type] ```ts import { type UseCapxulCancelRequestReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the updated `ActorRequest` with `status: "cancelled"`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates, for the acting scope only: the requests list, the cancelled request's detail query, the address book, the scope's inbox (so a cancelled request stops showing as actionable anywhere in the same session), and both insights queries. No money moves, so balances are untouched. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the request is not in a cancellable state. ## Client method [#client-method] This hook wraps [`client.account.requests.cancel(requestId)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).requests.cancel(requestId)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulIssueRequest (https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-issue-request) Issues a payment request from an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) to a payer — the receivable side of "send an invoice". The request appears in the actor's [`useCapxulRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests) list and lands in the payer's [Inbox](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-inbox), where they approve or decline it. ## Import [#import] ```ts import { capxulOrgScope, useCapxulIssueRequest } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulOrgScope, useCapxulIssueRequest, useCapxulOrgs, } from "@capxul/sdk-react"; function IssueInvoiceButton({ payerEmail }: { payerEmail: string }) { const orgs = useCapxulOrgs(); const orgId = orgs.data?.[0]?.orgId; const issueRequest = useCapxulIssueRequest(capxulOrgScope(orgId)); return ( ); } ``` To request money as yourself rather than as an org, call the hook with `capxulAccountScope` (or no argument — that is the default). ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` — defaults to `capxulAccountScope` Which actor issues the request. An explicit `undefined` also falls back to the personal scope, so when scoping to an org keep the control disabled until the org id has loaded (as in the example above). See [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). ### Mutation input [#mutation-input] The mutation takes an `ActorRequestIssueInput`: `payer` must be a typed reference — a handle, email, org handle, payee id, or Capxul user id; bare EVM addresses are rejected by design. `amount` is a decimal money value (currency, string value, decimals). `reference` is the human-readable identifier shown on both sides (for example an invoice number). `expiresAt` is an optional expiry timestamp in epoch milliseconds. ## Return type [#return-type] ```ts import { type UseCapxulIssueRequestReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the created `ActorRequest` with its current `status`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates, for the acting scope only: the requests list, the new request's detail query, the address book (issuing a request creates or refreshes the payer's relationship entry), and both insights queries (summary and history). The acting scope's own inbox is untouched — the request lands in the **payer's** inbox, not the issuer's. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when `payer` is not a valid typed reference. ## Client method [#client-method] This hook wraps [`client.account.requests.issue(input)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).requests.issue(input)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulReconcileRequests (https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-reconcile-requests) Runs a reconciliation pass over the payment requests an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) has issued: which receivables have settled, and which need attention. It is exposed as a mutation because you trigger a pass explicitly — it reads settlement state rather than moving money. List the underlying requests with [`useCapxulRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests). ## Import [#import] ```ts import { capxulAccountScope, useCapxulReconcileRequests } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulReconcileRequests, } from "@capxul/sdk-react"; function ReconcilePanel() { const reconcile = useCapxulReconcileRequests(capxulAccountScope); return (
{reconcile.data ? (
    {reconcile.data.map((entry) => (
  • {entry.reference}: {entry.status}
  • ))}
) : null} {reconcile.error ?

{reconcile.error.message}

: null}
); } ``` To reconcile an organization's receivables, scope the hook with `capxulOrgScope(orgId)` instead. ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` — defaults to `capxulAccountScope` Which actor's issued requests to reconcile. An explicit `undefined` also falls back to the personal scope — gate org-scoped controls on the org id being loaded. See [Actor scopes](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). ### Mutation input [#mutation-input] None — call `reconcile.mutate()` / `reconcile.mutateAsync()` with no argument. ## Return type [#return-type] ```ts import { type UseCapxulReconcileRequestsReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is an array of `ReconciliationEntry` — one row per reconciled receivable: `status` is `"paid"` (settled, with `settledPaymentId` and, once collected, a `receiptDocumentHash`) or `"exception"` (needs attention). The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates, for the acting scope only: the requests list, the address book, and both insights queries — the reconciliation counters on [`useCapxulInsightsSummary`](https://capxul-sdk-docs.pages.dev/docs/hooks/insights/use-capxul-insights-summary) refetch after a pass. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync`. ## Client method [#client-method] This hook wraps [`client.account.requests.reconcile()`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).requests.reconcile()`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulRequest (https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-request) Reads a single issued payment request for an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes). Use it for a request detail view after listing receivables with [`useCapxulRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests). ## Import [#import] ```ts import { capxulAccountScope, useCapxulRequest } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulRequest } from "@capxul/sdk-react"; function RequestDetail({ requestId }: { requestId: string }) { const request = useCapxulRequest(capxulAccountScope, requestId); if (request.isLoading) return

Loading request…

; if (request.error) return

{request.error.message}

; if (request.data === null) return

Request not found.

; const { reference, amount, status, expiresAt } = request.data; return (

{reference}

{amount.value} {amount.currency} — {status}

{expiresAt !== null ? (

Expires {new Date(expiresAt).toLocaleDateString()}

) : null}
); } ``` For an organization's request, scope with `capxulOrgScope(orgId)` instead. ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` The [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) that issued the request. The query stays disabled while the scope is `undefined`. ### requestId [#requestid] `string | undefined` The request id, as returned by [`useCapxulRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests) or [`useCapxulIssueRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-issue-request). The query stays disabled while `requestId` is `undefined`, so route params can be passed directly. ### options [#options] `{ enabled?: boolean } | undefined` Set `enabled: false` to hold the query even when scope and id are ready. ## Return type [#return-type] ```ts import { type UseCapxulRequestReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is the `ActorRequest`, or `null` when no request with that id exists for the actor: The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] ```ts ["capxul", "actor", "account", "requests", requestId] // personal scope ["capxul", "actor", "org", orgId, "requests", requestId] // org scope ``` (`"pending"` fills the actor and/or `requestId` segment while either is `undefined`.) Invalidated alongside the requests list whenever a requests/inbox mutation for the same scope names this request id — for example cancelling it, or the payer approving or declining it from their inbox rendered in the same session. ## Client method [#client-method] This hook wraps [`client.account.requests.get(requestId)`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).requests.get(requestId)`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulRequests (https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-requests) Lists the payment requests an [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) has **issued** — the money it is waiting to receive. Read one request with [`useCapxulRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-request); create, cancel, and reconcile with [`useCapxulIssueRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-issue-request), [`useCapxulCancelRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-cancel-request), and [`useCapxulReconcileRequests`](https://capxul-sdk-docs.pages.dev/docs/hooks/requests/use-capxul-reconcile-requests). Requests issued **to** the actor live on the [Inbox](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-inbox) side. The payment-requests family is graded **SDK-ready**: the create / send / cancel / reconcile primitives are published and tested, but a full invoice product workflow has not proven them end-to-end yet. See [Capability status](https://capxul-sdk-docs.pages.dev/docs/capability-status). ## Import [#import] ```ts import { capxulAccountScope, useCapxulRequests } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { capxulAccountScope, useCapxulRequests } from "@capxul/sdk-react"; function ReceivablesList() { const requests = useCapxulRequests(capxulAccountScope); if (requests.isLoading) return

Loading requests…

; if (requests.error) return

{requests.error.message}

; return (
    {requests.data.map((request) => (
  • {request.reference}: {request.amount.value}{" "} {request.amount.currency} — {request.status}
  • ))}
); } ``` The same component lists an organization's receivables by swapping the scope: ```tsx const requests = useCapxulRequests(capxulOrgScope(orgId)); ``` ## Parameters [#parameters] ### actor [#actor] `CapxulActorScope | undefined` The [actor scope](https://capxul-sdk-docs.pages.dev/docs/hooks/actor-scopes) whose issued requests to read. The query stays disabled while the scope is `undefined`. ### options [#options] `{ enabled?: boolean } | undefined` Set `enabled: false` to hold the query even when the scope is ready. ## Return type [#return-type] ```ts import { type UseCapxulRequestsReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an array of `ActorRequest`: `status` walks `draft → sent → viewed → pending_settlement → paid`, or ends in `declined`, `cancelled`, or `expired`. `amount` is a decimal money value (currency, string value, decimals) — never a raw token integer. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] ```ts ["capxul", "actor", "account", "requests"] // personal scope ["capxul", "actor", "org", orgId, "requests"] // org scope ``` (`"pending"` replaces the actor segment while the scope is `undefined`.) Invalidated — for the same scope only — by every mutation in this family and by the inbox mutations ([`useCapxulApproveInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-approve-inbox-request), [`useCapxulDeclineInboxRequest`](https://capxul-sdk-docs.pages.dev/docs/hooks/inbox/use-capxul-decline-inbox-request)). ## Client method [#client-method] This hook wraps [`client.account.requests.list()`](https://capxul-sdk-docs.pages.dev/docs/client) for the personal scope, or [`client.org(orgId).requests.list()`](https://capxul-sdk-docs.pages.dev/docs/client) for an org scope. # useCapxulSubAccountCreate (https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-account-create) Creates a **sub-account** — a named envelope inside an Account. Creating an envelope only names it; it holds no money until you fund it with [`useCapxulTransfer`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-transfer). The new envelope appears in [`useCapxulSubAccountsList`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-accounts-list). ## Import [#import] ```ts import { useCapxulSubAccountCreate } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useState } from "react"; import { useCapxulAccountBalance, useCapxulSubAccountCreate } from "@capxul/sdk-react"; function NewEnvelopeForm() { const account = useCapxulAccountBalance(); const createSubAccount = useCapxulSubAccountCreate(); const [name, setName] = useState(""); const accountId = account.data?.id; if (accountId === undefined) return null; return (
{ event.preventDefault(); await createSubAccount.mutateAsync({ accountId, name }); setName(""); }} > setName(event.target.value)} placeholder="Rent" /> {createSubAccount.error ?

{createSubAccount.error.message}

: null}
); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation input is an object with two fields: | Field | Type | Description | | ----------- | ----------- | ------------------------------------------------------------------------------- | | `accountId` | `AccountId` | The Account to create the envelope in. | | `name` | `string` | Display name for the new sub-account. Must be non-empty, at most 64 characters. | ## Return type [#return-type] ```ts import { type UseCapxulSubAccountCreateReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the created `SubAccount` — its `id`, `accountId`, `name`, `balance` (a `Money`), and `createdAt`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the Account's sub-account list (`["capxul", "subAccounts", accountId]`) and the personal balance (`["capxul", "accountBalance"]`), so envelope lists and available-balance reads refetch together. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the name is empty or longer than 64 characters, or when the mutation fires while `` is still bootstrapping. ## Client method [#client-method] This hook wraps [`client.subAccounts.create(accountId, { name })`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulSubAccountDelete (https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-account-delete) Deletes a **sub-account**. The envelope must be empty: a delete with money still assigned is rejected, so move the balance back to `main` (or to another envelope) with [`useCapxulTransfer`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-transfer) first. Deleting an envelope never destroys money — an empty envelope is just a name. ## Import [#import] ```ts import { useCapxulSubAccountDelete } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulSubAccountDelete, useCapxulTransfer } from "@capxul/sdk-react"; import type { SubAccount } from "@capxul/sdk"; function DeleteEnvelopeButton({ subAccount }: { subAccount: SubAccount }) { const transfer = useCapxulTransfer(); const deleteSubAccount = useCapxulSubAccountDelete(); return ( ); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation input is an object with two fields: | Field | Type | Description | | -------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `accountId` | `AccountId` | The parent Account. Carried only so the hook can invalidate the right sub-account list — the delete itself is addressed by `subAccountId`. | | `subAccountId` | `SubAccountId` | The sub-account to delete. Must have a zero balance. | ## Return type [#return-type] ```ts import { type UseCapxulSubAccountDeleteReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is `undefined` — a successful delete returns nothing. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the Account's sub-account list (`["capxul", "subAccounts", accountId]`) and the personal balance (`["capxul", "accountBalance"]`), so the deleted envelope drops out of lists and the available balance refetches. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — most notably when the sub-account still holds money (delete requires zero balance), when the sub-account does not exist, or when the mutation fires while `` is still bootstrapping. ## Client method [#client-method] This hook wraps [`client.subAccounts.delete(subAccountId)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulSubAccountRename (https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-account-rename) Renames a **sub-account** — the envelope keeps its id, balance, and history; only the display name changes. Pair it with [`useCapxulSubAccountsList`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-accounts-list) to show the result. ## Import [#import] ```ts import { useCapxulSubAccountRename } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulSubAccountRename } from "@capxul/sdk-react"; import type { SubAccount } from "@capxul/sdk"; function RenameButton({ subAccount }: { subAccount: SubAccount }) { const rename = useCapxulSubAccountRename(); return ( ); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation input is an object with three fields: | Field | Type | Description | | -------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `accountId` | `AccountId` | The parent Account. Carried only so the hook can invalidate the right sub-account list — the rename itself is addressed by `subAccountId`. | | `subAccountId` | `SubAccountId` | The sub-account to rename. | | `name` | `string` | The new display name. Must be non-empty, at most 64 characters. | ## Return type [#return-type] ```ts import { type UseCapxulSubAccountRenameReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is the updated `SubAccount` with the new `name`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the Account's sub-account list (`["capxul", "subAccounts", accountId]`) and the personal balance (`["capxul", "accountBalance"]`). ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when the name is empty or longer than 64 characters, when the sub-account does not exist, or when the mutation fires while `` is still bootstrapping. ## Client method [#client-method] This hook wraps [`client.subAccounts.rename(subAccountId, name)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulSubAccountsList (https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-accounts-list) Lists an Account's **sub-accounts** — named envelopes that partition the Account's money (Rent, Payroll, Emergency fund, …). Whatever is not assigned to an envelope is the Account's **main balance**; it is a remainder, not a row, so it never appears in this list. Create envelopes with [`useCapxulSubAccountCreate`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-sub-account-create) and fund them with [`useCapxulTransfer`](https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-transfer). ## Import [#import] ```ts import { useCapxulSubAccountsList } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulAccountBalance, useCapxulSubAccountsList } from "@capxul/sdk-react"; function EnvelopeList() { const account = useCapxulAccountBalance(); const subAccounts = useCapxulSubAccountsList(account.data?.id); if (subAccounts.isLoading) return

Loading envelopes…

; if (subAccounts.error) return

{subAccounts.error.message}

; return (
    {subAccounts.data.map((subAccount) => (
  • {subAccount.name}: {subAccount.balance.value} {subAccount.balance.currency}
  • ))}
); } ``` ## Parameters [#parameters] ### accountId [#accountid] `AccountId | undefined` The Account whose sub-accounts to list. The query stays disabled while `accountId` is `undefined`, so you can pass the `id` from [`useCapxulAccountBalance`](https://capxul-sdk-docs.pages.dev/docs/hooks/account/use-capxul-account-balance) directly — no manual gating needed. ### options [#options] `UseCapxulSubAccountsListOptions | undefined` ## Return type [#return-type] ```ts import { type UseCapxulSubAccountsListReturn } from "@capxul/sdk-react"; // UseQueryResult ``` `data` is an array of `SubAccount`: the sub-account's opaque `id`, its parent `accountId`, its display `name`, its `balance` (a `Money` — currency, decimal value, precision), and `createdAt`. An Account with no envelopes returns an empty array. The hook returns TanStack Query's `UseQueryResult`. The most-used fields: | Field | Description | | ------------ | ---------------------------------------------------------------------------------- | | `data` | The query data (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last fetch failed, otherwise `null`. | | `status` | `'pending'` \| `'error'` \| `'success'`. | | `isLoading` | `true` during the first fetch (no data yet). | | `isFetching` | `true` whenever a fetch is in flight, including background refetches. | | `refetch` | Manually refetch the query. | Capxul query hooks stay `pending` until the provider finishes bootstrapping — you do not need to gate them on `useCapxul()` yourself. The full field list is in the [TanStack Query `useQuery` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Query key [#query-key] `["capxul", "subAccounts", accountId]` — scoped per Account, and invalidated automatically by the four sub-account mutations for that Account (`useCapxulSubAccountCreate`, `useCapxulSubAccountRename`, `useCapxulSubAccountDelete`, `useCapxulTransfer`). ## Client method [#client-method] This hook wraps [`client.subAccounts.list(accountId)`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK. # useCapxulTransfer (https://capxul-sdk-docs.pages.dev/docs/hooks/sub-accounts/use-capxul-transfer) Moves money between two balances of the **same Account**: main balance into an envelope ("Add money"), envelope back to main ("Move money out"), or envelope to envelope. A Transfer never leaves the Account and never reaches another party — it is **not a Payment**, and nothing settles externally. To send money to someone else, use [`useCapxulPay`](https://capxul-sdk-docs.pages.dev/docs/hooks/payments/use-capxul-pay). See [Money & accounts](https://capxul-sdk-docs.pages.dev/docs/concepts/money-and-accounts) for the model. ## Import [#import] ```ts import { useCapxulTransfer } from "@capxul/sdk-react"; ``` ## Usage [#usage] ```tsx "use client"; import { useCapxulAccountBalance, useCapxulTransfer } from "@capxul/sdk-react"; import type { SubAccountId } from "@capxul/sdk"; function AddMoneyButton({ subAccountId }: { subAccountId: SubAccountId }) { const balance = useCapxulAccountBalance(); const transfer = useCapxulTransfer(); const account = balance.data; if (account === undefined) return null; return ( ); } ``` ## Parameters [#parameters] The hook itself takes no parameters. The mutation input is a `TransferInput` plus an `accountId`: ### accountId [#accountid] `AccountId` The Account the transfer happens inside. It is carried only so the hook can invalidate the right cache keys — the backend resolves the Account from the signed-in session, not from this field. ### from, to, amount [#from-to-amount] `from` and `to` are each a `TransferEndpoint`: either the literal `"main"` or a `SubAccountId`. The **main balance** is the remainder left after every envelope is funded — it is not a stored row. The two endpoints must differ, and `amount` (a `Money`) must be positive. ## Return type [#return-type] ```ts import { type UseCapxulTransferReturn } from "@capxul/sdk-react"; // UseMutationResult ``` On success, `data` is a `TransferResult`: `available` is the recomputed unassigned money (`balance − Σ` envelope balances). The `from` / `to` slots carry the updated sub-account rows; an endpoint that is `"main"` has no stored row, so its slot is `null`. The hook returns TanStack Query's `UseMutationResult`. The most-used fields: | Field | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `mutate` | Fire the mutation (fire-and-forget; pair with `onSuccess`/`onError` callbacks). | | `mutateAsync` | Fire the mutation and get a `Promise` of the result. Rejects with a `CapxulError` on failure. | | `data` | The mutation result (typed per hook, shown above). `undefined` until the first success. | | `error` | A `CapxulError` when the last attempt failed, otherwise `null`. | | `isPending` | `true` while the mutation is in flight. Use it to disable submit buttons. | | `reset` | Clear the mutation state (`data`, `error`) back to idle. | Mutations do not retry by default. The full field list is in the [TanStack Query `useMutation` reference](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation); see also the [TanStack Query integration guide](https://capxul-sdk-docs.pages.dev/docs/guides/tanstack-query). ## Cache behavior [#cache-behavior] On success this mutation invalidates the Account's sub-account list (`["capxul", "subAccounts", accountId]`) and the personal balance (`["capxul", "accountBalance"]`). Because a Transfer is not a Payment, it does not touch the payments, activity, or insights caches. ## Errors [#errors] Failures surface as a [`CapxulError`](https://capxul-sdk-docs.pages.dev/docs/guides/error-handling) on `error` / thrown from `mutateAsync` — for example when `from` and `to` are the same endpoint, when the amount is not positive, when the source balance is insufficient (the error reports what was available versus requested), or when the mutation fires while `` is still bootstrapping. ## Client method [#client-method] This hook wraps [`client.subAccounts.transfer({ from, to, amount })`](https://capxul-sdk-docs.pages.dev/docs/client) on the core SDK.