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.
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.
| Variable | Required | Default |
|---|---|---|
AFFIX_API_KEY | Yes | — |
AFFIX_API_BASE | No | https://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,
});
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.
Recommended. Credentials are normalised, witness-prepared server-side via POST /v1/witness/prepare, then proved.
Call buildWitness() or prepare offline, then pass witness to prove().
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
| Method | Description |
|---|---|
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). |
client | Low-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.
| Command | Purpose |
|---|---|
npx affix-sdk config | Show masked key and API base from envStatus() |
npx affix-sdk health | Call GET /api/health |
npx affix-sdk circuits | List 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 list | List locally stored proofs |
npx affix-sdk flush | Replay the offline queue |
API endpoints used by the SDK
Default base URL: https://api.affix-io.com. Request timeout default: 120 seconds.
| HTTP | Path | Purpose |
|---|---|---|
| GET | /api/health | Service health |
| GET | /v1/circuits | Circuit catalogue |
| POST | /v1/witness/prepare | Witness preparation |
| POST | /v1/circuits/:circuitId/prove | SNARK proof generation |
| POST | /v1/circuits/:circuitId/verify | Proof verification |
| GET | /v1/merkle/root | Current Merkle audit root |
| POST | /v1/merkle/audit | Anchor 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.
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.
