TypeScript SDK

@mnd8t/sdk — a typed client for the mnd8t authority API. No runtime dependencies; receipt and artifact verification run on WebCrypto, so they work in Node, Deno, Bun, edge runtimes and the browser.

For the integration narrative, read the developer guide. This page is the method-by-method reference.


Install

pnpm add @mnd8t/sdk
import { MandateClient } from "@mnd8t/sdk";

const mandate = new MandateClient({
  apiKey: process.env.MANDATE_API_KEY!,
});

Client options

Option Type Default Notes
apiKey string Required. Throws if empty.
baseUrl string http://localhost:4000 Your API host. Trailing slash is trimmed.
timeoutMs number 15000 Per-request timeout.
maxRetries number 2 Applies to safe requests only — see retries.
fetch typeof fetch global Inject your own for tests or proxies.

Never put a live key in browser code. The SDK will happily run there for receipt verification, but an API key in a bundle is a key you have given away.


Decisions

The endpoint that matters. Call it before touching a wallet or provider.

decisions.authorize(request)

const decision = await mandate.decisions.authorize({
  agent_external_id: "procurement_agent_12",
  external_reference: "INV-2026-0042",
  action_type: "PAYMENT",
  amount: { asset: "USDC", asset_amount_minor: 84_000_000 },
  valuation: {
    policy_amount_minor: 50_000,
    policy_currency: "GBP",
    source: "CUSTOMER_SUPPLIED",
    rate_reference: "provider_quote_123",
  },
  counterparty_external_id: "supplier_acme_cloud",
  destination: "0x1111111111111111111111111111111111111111",
  purpose: "cloud_infrastructure",
});

Returns AuthorizeResponse:

{
  id: string;
  mode: "SHADOW" | "ENFORCE";
  decision: "APPROVE" | "REJECT" | "ESCALATE" | "OBSERVE";
  effective_decision: same;
  would_decide: "APPROVE" | "REJECT" | "ESCALATE";
  reason_codes: string[];
  mandate_id: string | null;
  mandate_version: number | null;
  approval: { id, status, expires_at, approval_url } | null;
  authorisation_artifact: { id, status, expires_at, download_url } | null;
  evidence_receipt_id: string | null;
}

An idempotency key is generated per call. Override it with idempotencyKey to make a retry collide deliberately — derive it from something stable in your domain:

await mandate.decisions.authorize({
  ...payload,
  idempotencyKey: `invoice-${invoice.id}`,
});

Amounts are integer minor units. 50_000 is £500.00. The flat form (amount_minor, policy_amount_minor, policy_currency, asset) is still accepted but deprecated; prefer amount + valuation.

decisions.confirm(intentId, body)

Reports an execution that you performed. mnd8t does not execute.

await mandate.decisions.confirm(decision.id, {
  provider: "MY_PROVIDER",
  external_reference: txRef,
  authorisation_artifact_id: artifact.id,   // required in enforce mode
  reported_by: "CUSTOMER_EXECUTOR",
  verification: { method: "PROVIDER_LOOKUP", verified: true },
});

Consumes the budget reservation once. Repeating with the same external_reference is idempotent and returns duplicate: true.

decisions.cancel(intentId, reason?)

Releases the reservation, expires any pending approval, cancels live artifacts.

decisions.get(intentId) · decisions.list(params?)

get returns the decision with its rule-by-rule evaluations trace, approvals, executions, artifacts and receipts. list accepts { status?, decision?, limit? }.


Authorisation artifacts

The signed, single-use token that lets your system execute an approved action.

artifacts.get(artifactId)

Returns the envelope, including the signed artifact object.

artifacts.verify(artifact, options?)

Verifies the Ed25519 signature locally. Do this before acting on an artifact — it is the entire point of the signature.

const envelope = await mandate.artifacts.get(artifact.id);
const { valid, reason } = await mandate.artifacts.verify(envelope.artifact);
if (!valid) throw new Error(`refusing to execute: ${reason}`);

The public key is fetched from the gateway's key discovery document and matched on signature.key_id. Pin it instead with { publicKeyPem } if you would rather not fetch.

artifacts.claim(artifactId, executorReference)

Atomic. If two executors race, exactly one succeeds and the other throws a 409. Claim before executing.

await mandate.artifacts.claim(artifact.id, "executor-instance-1");

artifacts.fail(artifactId, reasonCode)

Marks the artifact FAILED after a provider rejection.

artifacts.reissue(intentId)

Issues a fresh artifact after failure or expiry. Re-checks live mandate state — a revoked or expired mandate refuses, as does a released reservation.


Mandates

mandates.create(request)

const m = await mandate.mandates.create({
  agent_id: agent.id,
  name: "Cloud procurement mandate",
  mode: "SHADOW",
  policy: {
    currency: "GBP",
    allowed_assets: ["USDC"],
    per_transaction: { autonomous_limit: 75_000, absolute_limit: 250_000 },
    budgets: [{ period: "MONTH", limit: 500_000 }],
    approval: { new_counterparty_requires_approval: true },
    allowed_purposes: ["cloud_infrastructure"],
    effective_at: new Date().toISOString(),
    expires_at: new Date(Date.now() + 30 * 86_400_000).toISOString(),
  },
});

Full field semantics: policy model.

mandates.publish(mandateId, options)

Freezes the draft. Requires a delegator attestation — whoever publishes asserts they hold the authority being delegated:

await mandate.mandates.publish(m.id, {
  delegation: {
    delegatorRole: "Finance Director",
    authoritySource: "CORPORATE_POLICY",
    sourceReference: "DoA-2026-v4-section-8",
  },
  changeNote: "Initial cloud procurement authority",
});

mnd8t records the assertion in the evidence chain. It does not verify that the delegator legally held the authority.

mandates.simulate(mandateId, body)

Dry run — nothing persisted, no budget moved.

const { would_decide, reason_codes } = await mandate.mandates.simulate(m.id, {
  amount_minor: 120_000,
  asset: "USDC",
  counterparty_external_id: "supplier_acme_cloud",
  purpose: "software",
  at: "2026-08-04T14:00:00Z",   // test schedule rules
});

mandates.revoke(mandateId)

Immediate kill switch: new decisions reject, pending approvals expire, unconsumed reservations release, live artifacts are revoked.

mandates.get(id) · mandates.list()


Agents and counterparties

const agent = await mandate.agents.create({
  name: "Procurement Agent",
  external_id: "procurement_agent_12",     // what you pass on every decision
  purpose: "Purchase approved software",
  principal_type: "ORGANISATION",
  principal_reference: "acme_ltd",
});

await mandate.counterparties.create({
  external_id: "supplier_acme_cloud",
  display_name: "Acme Cloud",
  authority_status: "CUSTOMER_APPROVED",   // your assertion, not a screening result
  destination: "0x1111111111111111111111111111111111111111",
});

Also: agents.list(), agents.get(id), counterparties.list().

external_id must be unique within your organisation and matches [a-zA-Z0-9_.-]+.


Receipts

receipts.get(receiptId)

receipts.verify(receipt, options?)

Local Ed25519 verification — the one that matters, because it does not require trusting the API that issued the receipt.

const envelope = await mandate.receipts.get(receiptId);
const { valid } = await mandate.receipts.verify(envelope.receipt);

receipts.verifyRemote(receiptId)

Server-side check. Convenience only — it asks the issuer to vouch for itself.


Errors

Every non-2xx response throws a typed subclass of MandateError, so you can branch on the class rather than parsing strings:

import {
  MandateError,
  MandateAuthenticationError,
  MandateValidationError,
  MandateIdempotencyConflictError,
  MandateRateLimitError,
  MandateNotFoundError,
} from "@mnd8t/sdk";

try {
  await mandate.decisions.authorize(payload);
} catch (err) {
  if (err instanceof MandateRateLimitError) return backOffAndRetry();
  if (err instanceof MandateValidationError) return report(err.details);
  if (err instanceof MandateError) log(err.status, err.code, err.message);
  throw err;
}
Class Status
MandateAuthenticationError 401
MandateNotFoundError 404
MandateIdempotencyConflictError 409 (IDEMPOTENCY_CONFLICT)
MandateValidationError 422
MandateRateLimitError 429
MandateError anything else

Every instance carries status, code, message and details.


Retries and idempotency

GETs retry automatically. Writes retry only when an idempotency key makes the replay safe — which decisions.authorize always sets, so it is safe by default. Retries fire on network failures and 5xx, up to maxRetries, and never on 4xx.

If you supply your own idempotencyKey, the guarantees are the API's: identical payload replays the original response; a different payload with the same key throws MandateIdempotencyConflictError. Keys are retained 7 days.


A complete flow

import { MandateClient } from "@mnd8t/sdk";

const mandate = new MandateClient({ apiKey: process.env.MANDATE_API_KEY! });

const decision = await mandate.decisions.authorize({
  agent_external_id: "procurement_agent_12",
  action_type: "PAYMENT",
  amount: { asset: "USDC", asset_amount_minor: 84_000_000 },
  valuation: { policy_amount_minor: 50_000, policy_currency: "GBP" },
  counterparty_external_id: "supplier_acme_cloud",
  purpose: "cloud_infrastructure",
});

if (decision.effective_decision === "ESCALATE") {
  await notifyApprover(decision.approval!.approval_url);
  return;
}
if (decision.effective_decision !== "APPROVE") return;

const artifact = decision.authorisation_artifact!;
const envelope = await mandate.artifacts.get(artifact.id);

const { valid } = await mandate.artifacts.verify(envelope.artifact);
if (!valid) throw new Error("artifact failed verification");

await mandate.artifacts.claim(artifact.id, "executor-1");

const txRef = await myWallet.pay(envelope.artifact);   // your credentials

await mandate.decisions.confirm(decision.id, {
  provider: "MY_PROVIDER",
  external_reference: txRef,
  authorisation_artifact_id: artifact.id,
  reported_by: "CUSTOMER_EXECUTOR",
  verification: { method: "PROVIDER_LOOKUP", verified: true },
});
15 min read · Need something that isn't here? Browse all documentation