AffixIOAFFIXIO
Contact

HomeDocs › SDK

Node.js integration

AffixIO SDK

The official Node.js client for prove, verify, witness preparation, Merkle audit calls, local proof storage, and an offline replay queue. Proving runs on the AffixIO API; your service sends credentials or witnesses and receives proof strings back.

Node.js 18+ ESM Zero dependencies CLI included
AffixIO logo
@affix-io/sdk npmjs.com/package/@affix-io/sdk Latest published: 2.0.2 · Apache-2.0
AffixIO verification infrastructure at the API boundary
Prove and verify beside your stack. API at api.affix-io.com.

Install

Add the package to any Node.js 18+ project. The published tarball ships pre-built dist/ output and the affix-sdk CLI binary.

npm install @affix-io/sdk

Source review or air-gapped installs from the public repository:

git clone https://github.com/AffixIO/SDK.git
cd SDK
cp .env.example .env
npm install
npm run build
npx affix-sdk health

Do not copy node_modules between machines. Run npm install && npm run build on each deployment host.

Download: https://www.npmjs.com/package/@affix-io/sdk · Registry install is the supported path for production services and CI images.

Authentication and configuration

The SDK requires an API key. Resolution order: constructor apiKey, then AFFIX_API_KEY from a package-root .env file (loaded by loadEnv()), then an error if still unset.

VariableRequiredDefault
AFFIX_API_KEYYes
AFFIX_API_BASENohttps://api.affix-io.com

Every HTTP request sends Authorization: Bearer <key> and X-API-Key: <key>. Accepted key prefixes on the API include aio_, demo_, and affix_, plus minted demo keys. Obtain production keys via contact or your evaluation agreement.

# .env at package root (copy from .env.example)
AFFIX_API_KEY=aio_your_key_here
AFFIX_API_BASE=https://api.affix-io.com
import { AffixSDK } from "@affix-io/sdk";

// Reads AFFIX_API_KEY from .env
const sdk = new AffixSDK();

// Or pass explicitly
const sdk = new AffixSDK({
  apiKey: process.env.AFFIX_API_KEY!,
  apiBase: "https://api.affix-io.com",
  requestAttestation: true,
  timeoutMs: 120_000,
});
Server-side only. Install the SDK in the service that holds secrets (API route, worker, MCP server). Do not embed API keys in browser bundles or client-side LLM prompts.

Quick start

Prove an attested boolean credential, then inspect the result. This matches the package README and the default integration test pattern.

import { AffixSDK, defaultContext } from "@affix-io/sdk";

const sdk = new AffixSDK();

const result = await sdk.prove({
  circuitId: "attested_boolean",
  credential: {
    schema_id: "eligibility_v1",
    issuer_id: "issuer_abc",
    issuer_pubkey_hash: "0x...",
    credential_id: "0x...",
    claim_value: "approved",
    valid_from: 1700000000,
    valid_until: 1800000000,
  },
  context: defaultContext({ required_claim_hash: "approved" }),
});

console.log(result.valid, result.decision, result.proof_id);

Verify health and list circuits before your first prove:

npx affix-sdk config
npx affix-sdk health
npx affix-sdk circuits

Prove input modes

prove() accepts exactly one of: a pre-built witness, a credential with context, or raw circuit fields. Otherwise it throws prove_requires_witness_or_credential_or_fields.

1. Credential + context

Recommended. Credentials are normalised, witness-prepared server-side via POST /v1/witness/prepare, then proved.

2. Pre-built witness

Call buildWitness() or prepare offline, then pass witness to prove().

3. Raw fields

For Merkle batch proofs and custom Noir inputs. Pass a fields object keyed by circuit input names.

Credential + context

await sdk.prove({
  circuitId: "ticket_local_resident",
  credential: { /* AffixCredential */ },
  context: defaultContext({ region_hash: "region-north", as_of_timestamp: 1700000000 }),
});

Pre-built witness

const witness = await sdk.buildWitness(circuitId, credential, context);
const result = await sdk.prove({ circuitId, witness });

Raw fields (Merkle batch)

await sdk.prove({
  circuitId: "merkle_batch",
  fields: {
    leaf: "0x...",
    sibling0: "0x...",
    sibling1: "0x...",
    sibling2: "0x...",
    index: "0",
    expected_root: "0x...",
  },
});

Prove and verify in one call

const { prove, verify } = await sdk.proveAndVerify({
  circuitId: "attested_boolean",
  credential,
  context: defaultContext({ required_claim_hash: "approved" }),
});
// verify.valid, verify.decision ("yes" | "no")

SDK methods

MethodDescription
prove(input)Generate a proof for a circuit. Stores result locally when successful.
verify(circuitId, proof, requestAttestation?)Verify a proof string against a circuit.
proveAndVerify(input)Prove then verify in one call.
buildWitness(circuitId, credential, context?)Server-side witness preparation (POST /v1/witness/prepare).
isOnline()Returns true when GET /api/health reports status: ok.
flushOfflineQueue()Replay queued prove jobs after connectivity returns.
listQueuedJobs()Inspect the local offline queue.
listStoredProofs()Read the local proof store (last 500 entries).
clientLow-level AffixApiClient for health, circuits, Merkle root, and audit calls.

Exported helpers: defaultContext, prepareWitness, witnessFromInputs, loadEnv, envStatus, maskApiKey.

Set requestAttestation: true on prove or verify bodies to request ML-DSA-65 attestation fields where the API supports them. Default in config is true.

CLI reference

The affix-sdk binary ships with the package. Use npx affix-sdk or npm start -- from a cloned SDK repo.

CommandPurpose
npx affix-sdk configShow masked key and API base from envStatus()
npx affix-sdk healthCall GET /api/health
npx affix-sdk circuitsList circuits from GET /v1/circuits
npx affix-sdk prove [--circuit <id>] [--claim <value>]Run a prove with demo defaults
npx affix-sdk verify <proof-id-or-path>Verify a stored or pasted proof
npx affix-sdk listList locally stored proofs
npx affix-sdk flushReplay the offline queue

API endpoints used by the SDK

Default base URL: https://api.affix-io.com. Request timeout default: 120 seconds.

HTTPPathPurpose
GET/api/healthService health
GET/v1/circuitsCircuit catalogue
POST/v1/witness/prepareWitness preparation
POST/v1/circuits/:circuitId/proveSNARK proof generation
POST/v1/circuits/:circuitId/verifyProof verification
GET/v1/merkle/rootCurrent Merkle audit root
POST/v1/merkle/auditAnchor proof to audit log

Full HTTP semantics, rate limits, and error codes: errors and rate limits. OpenAPI specification: /openapi.json.

Offline queue and local storage

When prove() fails and queueOnFailure is not false (default is true), the SDK enqueues the job for later replay.

  • .affix/offline-queue.json — queued prove bodies (credential payloads redacted to witness-only before disk write).
  • .affix/proofs.json — last 500 successful proof records.
const online = await sdk.isOnline();
if (online) {
  const replayed = await sdk.flushOfflineQueue();
  console.log(`Replayed ${replayed.length} queued jobs`);
}

Circuit templates supported for local witness building during queue replay include boolean, range, membership, Merkle, and health-age patterns defined in the SDK. Unsupported circuits throw unsupported_witness_circuit.

Where the SDK fits in your stack

Typical integration points: Express or Fastify middleware before a sensitive route, BullMQ workers for batch eligibility, Next.js API routes, or an MCP tool that returns a proof_id instead of raw credentials to a model context.

AffixIO deployment beside regulated workflows
Run the SDK in trusted backend services beside CRM, identity, and workflow systems.
AffixIO example QR credential linking to affix-io.com/sdk
Example signed QR from the ticket sandbox. Scan resolves to this SDK guide.

Agent and API gate pattern

import { AffixSDK, defaultContext } from "@affix-io/sdk";

const sdk = new AffixSDK();

export async function beforeToolRun(userCredential: Record<string, unknown>) {
  const { valid, proof_id } = await sdk.prove({
    circuitId: "attested_boolean",
    credential: userCredential,
    context: defaultContext({ required_claim_hash: "approved" }),
  });
  if (!valid) throw new Error("tool_access_denied");
  return proof_id; // log or Merkle audit — not the credential
}

For Claude and MCP clients, see AI integration and MCP connect. For REST-only paths, see API quickstart.

Frequently asked questions

Does the SDK run in the browser?

No. It targets Node.js 18+ with ESM imports and Node built-in HTTP and filesystem modules. Use a backend route or worker to call AffixIO.

How do I list available circuits?

Run npx affix-sdk circuits or call sdk.client.listCircuits(). The live catalogue is also documented at /docs/circuits.

What licence applies?

Apache License 2.0. Source and notices are in the AffixIO/SDK repository on GitHub.

Where do I get an API key?

Copy .env.example to .env in your project or SDK clone. Production and higher-throughput keys: contact or your evaluation agreement. Sandbox keys are available via /sandbox/.

How does this relate to the REST API?

The SDK is a typed wrapper around the same endpoints documented in OpenAPI. You can integrate without the SDK using direct HTTPS calls if you prefer.