Capxul Docs
Tutorials

Create your first SDK client

Learn the Core SDK by bootstrapping a client, completing email OTP, and reading an Account.

This tutorial builds a small Node script with the Core SDK client. You will bootstrap from a publishable key, complete email OTP, and read the signed-in user's logical Account.

Before you start

You need Node.js, a Capxul publishable key, and access to the email address used for the tutorial. Create an empty project, then install the current alpha as an exact dependency:

npm init -y
npm install --save-exact @capxul/sdk@alpha tsx

Set CAPXUL_PUBLISHABLE_KEY and CAPXUL_TUTORIAL_EMAIL in the environment that will run the script. The publishable key is safe to use in a client, but it is locked to the application origins configured when it was created.

1. Create the client

first-client.ts
import { createCapxulClient } from "@capxul/sdk";

const created = await createCapxulClient({
  publishableKey: process.env.CAPXUL_PUBLISHABLE_KEY!,
  requirement: "counterfactual",
});

if (!created.ok) {
  console.error(created.error.code, created.error.message);
  process.exit(1);
}

const client = created.value;

The publishable key identifies the application and resolves platform configuration. counterfactual asks the client to prepare a receivable Account after sign-in without requiring full deployed activation.

2. Request an OTP

const email = process.env.CAPXUL_TUTORIAL_EMAIL!;
const code = process.argv[2];

if (!code) {
  const requested = await client.auth.signIn({ email });
  if (!requested.ok) throw requested.error;

  console.log("Check your email, then run this script again with the one-time code.");
  process.exit(0);
}

The result is a value. Expected failures such as invalid input or rate limiting do not need try/catch; narrow on ok.

3. Verify the code

When the script receives a code argument, verify it without requesting a new code:

const verified = await client.auth.verifyOtp({ email, code });
if (!verified.ok) throw verified.error;

console.log(`Signed in until ${new Date(verified.value.expiresAt).toISOString()}`);

Successful verification stores the session for this process and starts the configured Account readiness lane in the background.

4. Wait for Account readiness

OTP verification does not wait for Account setup. Poll the public lifecycle until the requested readiness is available, and surface setup failure explicitly:

const readinessDeadline = Date.now() + 60_000;

while (true) {
  const lifecycle = await client.account.getLifecycle();
  if (!lifecycle.ok) throw lifecycle.error;

  if (lifecycle.value.status === "ready") break;
  if (lifecycle.value.status === "failed") throw lifecycle.value.error;
  if (Date.now() >= readinessDeadline) throw new Error("Account setup timed out");

  await new Promise((resolve) => setTimeout(resolve, 500));
}

5. Read the Account

const account = await client.accounts.read();
if (!account.ok) throw account.error;

console.log({
  accountId: account.value.id,
  balance: account.value.balance.value,
  currency: account.value.balance.currency,
  available: account.value.available.value,
});

The public result uses Money, not chain units. Your application does not need an RPC URL, address, or chain ID for this read.

6. Close owned resources

The public factory owns its production resources. When a short-lived process is done, close the client if a close hook is present:

await client._internal.close?.();

_internal is lifecycle substrate, not a domain API. Do not build product behavior on other members under that namespace.

Run the script once to request the OTP, then run it again with the code as the first argument:

npx tsx first-client.ts
npx tsx first-client.ts 123456

What you learned

You used the SDK independently of React, treated failures as typed values, and worked in product vocabulary. Continue with the OTP guide, readiness guide, or client reference.

On this page