> ## Documentation Index
> Fetch the complete documentation index at: https://docs.enfinitos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK integration

> How the auditor SDK maps onto the sandbox HTTP API — code snippets for @enfinitos/sdk-auditor and @enfinitos/cli pointed at the sandbox. Other SDKs ship at the April 2027 production launch.

The sandbox HTTP API is a strict subset of the production API
shape. The auditor SDK (`@enfinitos/sdk-auditor`) is published
today and integrates with the sandbox by pointing its base URL at
`https://sandbox.api.enfinitos.com`. Other SDKs (`sdk-operator-web`,
`sdk-brand`) ship at the April 2027 production launch — code
snippets below show the expected integration shape for planning
purposes.

This page is the **integration map**: which SDK surface maps to
which sandbox endpoint, with copy-pasteable code snippets.

## @enfinitos/sdk-auditor

Verify any sandbox-issued pack offline. The sandbox publishes the
same `runtime-keys` directory shape as production, with the
sandbox-only key. This SDK is **published today**.

```ts theme={null}
import { verifyAll } from "@enfinitos/sdk-auditor";

const packResponse = await fetch(
  "https://sandbox.api.enfinitos.com/api/sandbox/proof-packs/PAK_ID",
  { credentials: "include" },
);
const { data } = await packResponse.json();

const report = await verifyAll(
  {
    pack: data.pack,
    metering: data.pack.metering,
    settlement: data.pack.settlement,
    verificationKeys: [data.verificationKey],
  },
  { verificationKeySource: "local" },
);

console.log(report.status); // "VALID"
```

The auditor SDK is **fully browser-compatible** as of v0.0.2 —
the previous version required `node:crypto`; the current version
uses `@noble/ed25519` + `@noble/hashes` + Web APIs only, so this
exact snippet runs in a browser tab, a Cloudflare Worker, Node,
Deno, or Bun.

<Note>
  Use auditor SDK **v0.0.4 or later**. The platform seals
  `settlement.v2` packs (3-field content-hash settlement idemKey);
  earlier published SDK versions reconstruct the legacy 2-field key
  and report `SETTLEMENT_IDEM_KEY_MISMATCH` on every line. v0.0.4 is
  version-aware: it verifies both `settlement.v2` and historical
  `settlement.v1` packs.
</Note>

## @enfinitos/sdk-brand

<Note>
  `@enfinitos/sdk-brand` ships at the April 2027 production launch. The
  integration shape below is for planning purposes.
</Note>

The Brand SDK is read-only — it queries delivery proof, metering,
and settlement records scoped to a brand's campaigns. The sandbox
runs the brand from the operator side (we sign offers from one
sandbox org to another), so the brand surface is presented through
the same `/api/sandbox/proof-packs` and `/api/sandbox/inspect`
endpoints.

```ts theme={null}
import { BrandClient } from "@enfinitos/sdk-brand";

const client = new BrandClient({
  apiKey: "sandbox-cookie-equivalent",
  baseUrl: "https://sandbox.api.enfinitos.com/api/sandbox",
  // The sandbox auths via cookie; the SDK supports cookie-mode
  // when constructed with `auth: { mode: "cookie" }`.
  auth: { mode: "cookie" },
});

const packs = await client.proof.list();
```

## @enfinitos/sdk-operator-web

<Note>
  `@enfinitos/sdk-operator-web` ships at the April 2027 production launch. The
  integration shape below is for planning purposes.
</Note>

The Operator Web SDK is a React component library. Wire it
against the sandbox by configuring its `OperatorProvider` with
the sandbox base URL:

```tsx theme={null}
import { OperatorProvider, RightsRegistry, ProofCentre } from "@enfinitos/sdk-operator-web";

export function SandboxDashboard() {
  return (
    <OperatorProvider
      config={{
        apiBaseUrl: "https://sandbox.api.enfinitos.com/api/sandbox",
        authMode: "cookie",
        environment: "sandbox",
      }}
    >
      <RightsRegistry />
      <ProofCentre />
    </OperatorProvider>
  );
}
```

<Note>
  The sandbox API surfaces a subset of the production endpoints:
  rights, offers, challenges, proof, runtime-keys. Components that
  hit endpoints outside that subset (billing, pacing, pilot
  programmes) currently render an empty state when pointed at the
  sandbox. Wiring the remaining surfaces is on the post-launch
  roadmap.
</Note>

## @enfinitos/cli

The CLI has dedicated sandbox subcommands — see
[the CLI page](/sandbox/cli) for the full reference. The CLI itself ships at
the April 2027 launch; today, use the published auditor SDK and the
[integration playground](https://enfinitos.com/developers/playground).

```sh theme={null}
# Available at launch:
npm install -g @enfinitos/cli
enfinitos sandbox provision
enfinitos sandbox demo --count 12
enfinitos sandbox list
enfinitos sandbox get pak_xyz --save pack.json
enfinitos sandbox verify pack.json
```

## Direct HTTP

Every SDK is a thin client over the same HTTP API. If you'd
rather not adopt a client, the API is fully documented in the
[API reference](/sandbox/api-reference) and the snippets above
translate to direct `fetch` calls one-to-one.

## Webhooks

The key-authed `/v1` surface (the same credentials as the
[quickstart](/quickstart)) can also push events to you. Subscribe an
HTTPS endpoint and the platform POSTs a signed payload for every
matching tenant event — `right.issued`, `offer.accepted`,
`challenge.opened`, `delivery.observed`, and the rest of the event
taxonomy. `["*"]` subscribes to everything; a few kinds are defined
but don't fire on the sandbox yet (`right.resumed`, `right.expired`,
`challenge.withdrawn`, `proof_pack.sealed`, the `key.*` kinds) —
subscribing to them is safe and forward-compatible.

| Method + path                                                       | Purpose                                                                                                                     |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `POST /v1/webhooks`                                                 | Subscribe: `{ url, events, label }`. `url` must be absolute `https`. Returns the `whsec_…` signing secret **exactly once**. |
| `GET /v1/webhooks`                                                  | List your subscriptions.                                                                                                    |
| `GET /v1/webhooks/{subscriptionId}`                                 | Fetch one subscription.                                                                                                     |
| `DELETE /v1/webhooks/{subscriptionId}`                              | Permanently remove a subscription.                                                                                          |
| `POST /v1/webhooks/{subscriptionId}/active`                         | Pause / resume (`{ active: boolean }`) without rotating the secret.                                                         |
| `POST /v1/webhooks/{subscriptionId}/rotate-secret`                  | Issue a fresh secret, returned exactly once. The old secret dies immediately.                                               |
| `GET /v1/webhooks/{subscriptionId}/deliveries`                      | List delivery attempts, newest first (`status` filter + cursor pagination).                                                 |
| `GET /v1/webhooks/{subscriptionId}/deliveries/{deliveryId}`         | One delivery attempt with full payload and response excerpt.                                                                |
| `POST /v1/webhooks/{subscriptionId}/deliveries/{deliveryId}/replay` | Re-fire a delivery, signed with the *current* secret.                                                                       |
| `POST /v1/webhooks/deliveries/process-due`                          | Process your due retries now (operator / external-cron hook).                                                               |

Reads carry `audit:read`; the active toggle and replay additionally
carry `delivery:write`; creating, deleting, rotating the secret, and
`process-due` require a key carrying the wildcard `*` scope.

Every delivery is signed with one header:

```
Enfinitos-Signature: t=<unix-seconds>,v1=<hex hmac-sha256>
```

where `v1` is HMAC-SHA256 over `` `${t}.${body}` `` — the timestamp,
a dot, the raw request body — keyed with your subscription's
`whsec_…` secret. Verify by recomputing the HMAC over exactly that
string with your subscription secret and comparing; reject stale
timestamps (say, older than five minutes) to block replayed
captures:

```ts theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

function verifySignature(header: string, rawBody: string, secret: string) {
  const t = header.match(/t=(\d+)/)?.[1];
  const v1 = header.match(/v1=([0-9a-f]+)/)?.[1];
  if (!t || !v1) return false;
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // stale
  const expected = createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  return timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
```

Delivery is asynchronous and **at-least-once** — make your receiver
idempotent and return 2xx quickly (each attempt has a 10-second
timeout). Failed attempts retry on an exponential backoff with
jitter — roughly **30 s, 2 m, 10 m, 1 h** — and after **5 attempts**
the delivery is marked `dead_lettered`: automatic retries stop, but
the replay endpoint can re-fire it manually at any time.

## What's the same vs. production integration

| Surface           | Sandbox                                          | Production                                         |
| ----------------- | ------------------------------------------------ | -------------------------------------------------- |
| Auth              | Cookie (`enf_sandbox_tenant`)                    | Bearer token + tenant scope                        |
| Base URL          | `https://sandbox.api.enfinitos.com`              | `https://api.enfinitos.com` (at April 2027 launch) |
| Rate limit        | 10 req/10s + 200/hour per IP                     | Per-tenant configurable                            |
| Idempotency keys  | Not enforced                                     | Required on mutating endpoints                     |
| Webhook backplane | Live — subscribe via [`/v1/webhooks`](#webhooks) | Same contract at launch                            |
| OAuth flow        | Not used                                         | Required for human reviewers                       |

A buyer's integration team can write their auditor SDK wiring
against the sandbox today. Other SDKs ship at the April 2027
production launch — type definitions, response shapes, error codes,
and state-machine semantics will be identical.
