For agent operators

Agent SDK — typed access to gated content

Subscribe, mint a bearer, drop client.fetch() in for any gated request — typed errors, no edge-bundle gymnastics. The SDK is a thin Node client that auto-decodes 403 + x-cryptolect-gated responses transparently, so your agent code reads gated publishers the same way it reads anything else.

Why a separate SDK?

The agent SDK is not the EmDash plugin. It serves a different audience and lives on a different side of the request: publishers install the EmDash plugin to gate their content; agent operators install this SDK to read it. If you landed here looking for the publisher integration, that's where to go.

Install

pnpm add @cryptolect/sdk
# or
npm install @cryptolect/sdk

Get a bearer token

  1. Subscribe at /pricing. Pick a tier (Developer is fine for evaluation).
  2. From the Cryptolect dashboard, mint a token via POST /v1/subscriptions/tokens.
  3. Tokens look like crypt_developer_K3RFyZkN… — store as a secret.

Use it

import { CryptolectClient } from "@cryptolect/sdk";

const client = new CryptolectClient({
  token: process.env.CRYPTOLECT_TOKEN!,
  // Optional: identify the source publisher site for the agent's path
  // (used in audit logs; doesn't affect access control).
  sourceSite: "https://my-research-agent.example",
});

// 1) Drop-in fetch — auto-decodes any 403 + x-cryptolect-gated: 1 response.
const res = await client.fetch("https://publisher.example/posts/agent-only");
console.log(res.status); // 200 (synthesized)
console.log(res.headers.get("x-cryptolect-decoded")); // "1"
const text = await res.text(); // ← plaintext

// 2) Decode a specific cipher payload directly (when you already
//    pulled the gated block out of an EmDash render response).
const { plaintext } = await client.decode({
  cipher_code: "ABC1XYZ9",
  emojitext: "🐼🐰🐮🐮🐵💎🐭🐦🐴🐔",
});

// 3) List the ciphers your subscription can decode and current quota.
const { ciphers, quota } = await client.listAccessibleCiphers();
console.log(`${quota.monthly_used}/${quota.monthly_quota} decodes used this month`);

Typed errors

Every wire error surfaces as a CryptolectError with a stable code field. Two SDK-only codes (gated, network_error) cover the cases the wire never sees.

import {
  CryptolectClient,
  CryptolectError,
  CryptolectGatedError,
} from "@cryptolect/sdk";

try {
  const text = await client.fetch(url).then((r) => r.text());
  // …
} catch (err) {
  if (err instanceof CryptolectGatedError) {
    // Auto-decode tried but couldn't, e.g. cipher_not_granted or quota_exceeded.
    // err.envelope contains the publisher's acquire_options array — surface
    // those to the agent operator.
    console.error(err.envelope.acquire_options);
  } else if (err instanceof CryptolectError && err.code === "quota_exceeded") {
    // Slow down or surface to the human operator.
  } else {
    throw err;
  }
}

How auto-decode works

  1. client.fetch() calls fetch() normally with the bearer attached as Authorization.
  2. If the response is 2xx, it's returned unchanged.
  3. If the response is 403 with x-cryptolect-gated: 1, the SDK parses the envelope, posts {cipher_code, emojitext} to /v1/decode, and returns a synthesized 200 with the plaintext body and x-cryptolect-decoded: 1.
  4. If the gated 403's body is malformed, the original Response passes through unchanged so you can diagnose the misbehaving publisher.

Caller-set Authorization is preserved (escape hatch for multi-tenant agent runners). The auto-decode call is metered against the bearer's monthly quota; failed decodes do not burn quota.

Discovery via MCP

If your agent runtime speaks MCP, you don't have to wire the SDK manually — pull the tool bundle and let the runtime register it:

const r = await fetch("https://cryptolect.dev/.well-known/mcp-tools.json");
const { tools } = await r.json();
// hand 'tools' to your MCP-tool registry

The tool surface is documented in detail at /docs/mcp-tools.

Reference

Cross-cutting reference for the agent-discovery surface: