ErasureDocs
SDKs

SDKs

Browser SDK @erasurehq/anumati — install, init, methods, HTTP, and troubleshooting.

SDKs

The public client SDK is @erasurehq/anumati.

It loads published consent configuration, shows the notice UI, and posts receipts. It authenticates with a publishable key only—never an operator session.

Derived from product docs: app_docs/guide/sdk.md, app_docs/sdk/GettingStarted.md, app_docs/sdk/API.md.


When to use it

  • Marketing sites, SPAs, and multi-page apps that need a consent banner
  • After you have published consent and a publishable key

How it works

Anumati.init({ publicKey, apiBaseUrl? })

      ▼ GET /api/sdk/v1/config  (Bearer pk_live_…)
Published snapshot + ETag

      ▼ Banner / preferences UI

      ▼ POST /api/sdk/v1/consent
Receipt stored → optional project webhook (consent.updated)
RuleDetail
AuthPublishable key only—not operator session
DraftNever returned from config
StorageChoices in localStorage (package Storage docs)
PrivacyReceipts do not store IP or email

Path to live

Publish consent → Create publishable key → Install SDK → First receipt → Live
StepWhereDone when
1. PublishConsentImmutable version exists
2. KeyDeveloppk_live_… created
3. InstallYour appAnumati.init runs in the browser
4. ReceiptDevelop / ReceiptsFirst row appears

The first successful receipt is the integration milestone—not “SDK installed.”


Install

npm install @erasurehq/anumati
# pnpm add @erasurehq/anumati
# yarn add @erasurehq/anumati
# bun add @erasurehq/anumati

Environment

apiBaseUrl is the origin only—never include /api/....

Local

# .env
NEXT_PUBLIC_ANUMATI_PUBLIC_KEY=pk_live_…
NEXT_PUBLIC_ANUMATI_API_BASE_URL=http://localhost:3000

Production

# .env
NEXT_PUBLIC_ANUMATI_PUBLIC_KEY=pk_live_…
NEXT_PUBLIC_ANUMATI_API_BASE_URL=https://your-erasure-origin

Same-origin apps (site and Erasure on one host) may omit apiBaseUrl.

Product guide examples sometimes use the option name baseUrl; the package Getting Started path documents apiBaseUrl. Use the option name your installed package documents; the value is always the Erasure origin.


Initialize

Instance client (documented npm API)

import { Anumati } from "@erasurehq/anumati";

const client = await Anumati.init({
  publicKey: "pk_live_…",
  apiBaseUrl: "https://your-erasure-origin", // optional if same-origin
  onReady?: (client) => { /* … */ },
  onError?: (error) => { /* … */ },
});

Next.js (App Router — client only)

"use client";
import { useEffect } from "react";
import { Anumati } from "@erasurehq/anumati";

export function ConsentBootstrap() {
  useEffect(() => {
    void Anumati.init({
      publicKey: process.env.NEXT_PUBLIC_ANUMATI_PUBLIC_KEY!,
      apiBaseUrl: process.env.NEXT_PUBLIC_ANUMATI_API_BASE_URL,
    });
  }, []);
  return null;
}

React (Vite)

useEffect(() => {
  void Anumati.init({
    publicKey: import.meta.env.VITE_ANUMATI_PUBLIC_KEY!,
    apiBaseUrl: import.meta.env.VITE_ANUMATI_API_BASE_URL,
  });
}, []);

Plain HTML / CDN

<script type="module">
  import { Anumati } from "https://esm.sh/@erasurehq/anumati";
  await Anumati.init({
    publicKey: "pk_live_…",
    apiBaseUrl: "http://localhost:3000",
  });
</script>

Express / Fastify

The SDK does not run on the server. Serve the publishable key to the browser. Optionally verify webhooks with X-Anumati-Signature (HMAC-SHA256 of the raw body). Console Develop includes Express / Fastify tabs for examples.


Client surface (core runtime)

From the SDK API surface documented for the shipped core:

MemberBehaviour
init(options)Returns Promise<AnumatiClient>. Idempotent for the same publicKey.
client.hasConsent(purposeKey)true only if a current local decision exists for a matching consentHash and that purpose is granted. Unknown key → false. Before ready → false (fail closed).
client.getConsent()Returns choices, consentHash, versionNumber, needsConsent.
client.showPreferences()Opens preference modal (banner Manage + hosted preferences center).
client.destroy()Tear down the instance.
onReady / onErrorOptional callbacks on init.

Init options (documented):

OptionNotes
publicKeyRequired. pk_live_…
apiBaseUrlOptional API origin
onReady / onErrorOptional hooks

Visitor identity (v1): the SDK auto-generates an installationId in localStorage. There is no public API to set or read it. Receipts and webhooks receive it automatically. There is no public subjectId option in v1.

Deferred / not to treat as guaranteed in core API design docs: additional managed-UI helpers such as a separate show() / hide() / clearLocal() surface and some callback variants may appear in package docs or later slices—rely on methods present in your installed package version.


Behavioral principles (from SDK design)

  1. One initialization path per project key.
  2. One SDK instance manages one project (one publishable key → one projectId).
  3. Instance client is the supported npm API (not a bag of mutable fields on a global).
  4. init is idempotent for the same key.
  5. Framework agnostic (vanilla TypeScript/JS).
  6. No hidden background work after ready—no silent polling or re-POST of receipts without user action or a new init.
  7. Optional purposes default to not granted until accepted.
  8. Re-prompt decisions use consentHash, not wall-clock alone.

HTTP used by the SDK

GET /api/sdk/v1/config

Auth: Authorization: Bearer pk_live_…
Compatibility only (not recommended): ?key= may be accepted by the server. The SDK package does not send query keys.

Caching: ETag from content hash; Cache-Control: public, max-age=60, stale-while-revalidate=600; If-None-Match304.

200 (shape):

{
  "projectId": "…",
  "version": {
    "id": "…",
    "number": 3,
    "publishedAt": "ISO-8601",
    "contentHash": "…",
    "consentHash": "…"
  },
  "config": {
    "purposes": [
      {
        "id": "…",
        "key": "analytics",
        "name": "…",
        "description": "…",
        "required": false,
        "categoryId": "…",
        "sortOrder": 0
      }
    ],
    "categories": [{ "id": "…", "name": "Essential", "sortOrder": 0 }],
    "notice": {
      "title": "…",
      "description": "…",
      "acceptLabel": "…",
      "rejectLabel": "…",
      "manageLabel": "…",
      "companyName": "…",
      "logoUrl": "…",
      "primaryColor": "…"
    }
  }
}
codeHTTP
invalid_key401
revoked_key403
no_published_version404
rate_limited429

POST /api/sdk/v1/consent

Auth: Bearer publishable key.
Purpose: append-only consent receipt.

Body:

{
  "versionId": "…",
  "consentHash": "…",
  "choices": { "analytics": true, "marketing": false },
  "timestamp": "2026-01-01T00:00:00.000Z",
  "sdkVersion": "0.1.0"
}
  • timestamp may be accepted but is not stored; server createdAt is authoritative.
  • Do not send IP, email, fingerprint, or session identifiers.
  • consentHash must match the version’s computed consent surface.
  • Receipts are append-only (never update/delete).

201: { "ok": true, "id": "…", "createdAt": "…" }

codeHTTP
invalid_key401
revoked_key403
invalid_body400
invalid_version400
consent_hash_mismatch400

Publishable key management (console session)

MethodPath
POST/api/platform/projects/:projectId/api-keys
GET/api/platform/projects/:projectId/api-keys
DELETE/api/platform/projects/:projectId/api-keys/:keyId

Full secret shown once on create; thereafter prefix only. Soft revoke via revokedAt.


Hosted preferences

Product docs reference a hosted preferences surface: /preferences?key=pk_live_… (see SDK Getting Started “what happens next”).


Error handling

SituationGuidance
401/403 invalid or revoked keyRotate key in Develop; update env
404 no published versionPublish consent first
Network / CORSSDK endpoints allow CORS *; check origin / apiBaseUrl
Rate limited 429Back off; honor Retry-After

Best practices

  1. Publish before shipping the key to production.
  2. One key per environment if possible.
  3. Prefer Bearer; avoid query-string keys.
  4. Treat first receipt as go-live, not “script loaded”.
  5. Init only in the browser (useEffect / 'use client').

Troubleshooting

SymptomCheck
No bannerInit errors; publish exists; key not revoked
No receiptsNetwork tab POST consent; versionId/hash match; rate limits
Stale configETag/cache; republish and hard-refresh
no_published_versionPublish in Consent
Init on serverMove to client-only code

Publish consent guide · API · SDK · Publishable keys · Webhooks