Node & servers
Use the core client outside React — in scripts, servers, and agent backends — and hand a pre-built client to CapxulProvider.
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 and call its method
bundles directly.
Create the client in a script
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<CapxulResult<T>>— narrow onokinstead of wrapping calls intry/catch. See the result contract. - 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 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)
No account setup runs. Use it for tooling that only needs auth or profile reads and never touches money.
"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"
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:
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
polls in React).
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:
import { CapxulProvider } from "@capxul/sdk-react";
import type { CapxulClient } from "@capxul/sdk";
export function App({ client }: { client: CapxulClient }) {
return (
<CapxulProvider client={client}>
<Dashboard />
</CapxulProvider>
);
}Injected-client mode changes the provider contract:
- Ready immediately. There is no bootstrapping phase;
useCapxul()reportsreadyfrom 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
requirementorsignerprops. Those are inputs tocreateCapxulClient; you already made those choices when you built the client. Passing them alongsideclientis a type error. - Swapping the
clientprop 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
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, which exposes these operations as
tools.
CapxulClient
Reference for the core SDK client — createCapxulClient, the CapxulResult contract, and the method bundles the React hooks wrap.
MCP server
The @capxul/mcp server exposes Capxul financial operations — auth, account readiness, balances, testnet faucet, payments — as MCP tools over Streamable HTTP.