@getsesame/sdk
v0.1.2
Published
Server-side TypeScript SDK for the Sesame human-in-the-loop approval API.
Readme
@getsesame/sdk
Server-side TypeScript SDK for the Sesame human-in-the-loop approval API. Trigger an approval, wait for a human to approve or deny it, gate sensitive operations behind it, and verify the webhooks Sesame sends you.
Server-side only. This package authenticates with a secret API key (
sk_live_...) and must never be bundled into browser/client code. Use it from your backend, a serverless function, or a worker — never the frontend.
Install
npm install @getsesame/sdk
# or: pnpm add @getsesame/sdkRequires Node 18+ (uses the global fetch and node:crypto). Zero runtime
dependencies.
Configuration
The SDK needs one thing — your API key. Create one in the
Sesame dashboard (API Keys), then set
SESAME_API_KEY (or pass apiKey) and you're done; it reaches the hosted Sesame
broker automatically.
import { Sesame } from "@getsesame/sdk";
const sesame = new Sesame(); // reads SESAME_API_KEY
const sesame = new Sesame({ apiKey: process.env.MY_KEY }); // ...or pass it explicitlyOnly if you host Sesame yourself do you need a URL. Override the default
(https://getsesame.dev) via the SESAME_BROKER_URL env var or the baseUrl
option:
const sesame = new Sesame({ apiKey: "sk_live_...", baseUrl: "http://localhost:8000" });Quickstart: trigger + wait
const approval = await sesame.approvals.trigger({
action: "refund:order-123", // operation class + coalescing key
summary: "Refund $50 to [email protected]",
reason: "Support ticket #4471",
context: { orderId: "order-123", amount: 50 },
});
await approval.wait(); // long-polls until terminal (throws ApprovalTimeout)
if (approval.approved) {
// do the sensitive thing
}approval.wait({ timeoutMs, pollIntervalMs }) resolves with the updated
Approval once it reaches a terminal state (approved / denied / expired),
or throws ApprovalTimeout. Inspect approval.status / approval.approved.
requireApproval — wrap a function
import { requireApproval } from "@getsesame/sdk";
const issueRefund = requireApproval(
{
action: "refund",
summary: (orderId: string, amount: number) => `Refund $${amount} on ${orderId}`,
},
async (orderId: string, amount: number) => {
return paymentProvider.refund(orderId, amount);
},
);
// Triggers + waits first. Throws ApprovalDenied / ApprovalTimeout, else runs:
await issueRefund("order-123", 50);gate — Express / Connect middleware
import express from "express";
import { gate } from "@getsesame/sdk";
const app = express();
app.post(
"/admin/wipe",
gate({
action: (req) => `wipe:${req.params.tenant}`,
summary: (req) => `Wipe tenant ${req.params.tenant}`,
}),
(req, res) => {
// only runs after a human approves
res.json({ ok: true });
},
);On approval the middleware calls next(). On denial it responds 403, on
timeout 504, each with a JSON error body.
Webhook receiver
Sesame POSTs to your callback_url when an approval reaches a terminal state.
Verify the X-Sesame-Signature (HMAC-SHA256 hex of the exact raw body) with
your API key's webhook secret. You must read the raw body — verifying a
re-serialized JSON object will fail.
import express from "express";
import { verifyWebhook, WebhookVerificationError } from "@getsesame/sdk";
const app = express();
app.post(
"/webhooks/sesame",
express.raw({ type: "application/json" }), // gives us the raw Buffer
(req, res) => {
try {
const event = verifyWebhook(
req.headers,
req.body, // Buffer
process.env.SESAME_WEBHOOK_SECRET!,
);
console.log(`${event.action} -> ${event.status} by ${event.requester_label}`);
res.sendStatus(204);
} catch (err) {
if (err instanceof WebhookVerificationError) {
res.sendStatus(400);
return;
}
throw err;
}
},
);verifyWebhook(headers, rawBody, secret, { toleranceSec = 300 }) does a
constant-time signature comparison and rejects timestamps older than the
tolerance. It returns the parsed WebhookPayload or throws
WebhookVerificationError.
Errors
| Class | When |
| --------------------------- | --------------------------------------------- |
| ApprovalError | Base class for all SDK errors |
| ApprovalDenied | Approval reached denied / expired |
| ApprovalTimeout | wait() exceeded its timeout |
| WebhookVerificationError | Bad signature, stale/missing timestamp |
| SesameAuthError | Broker returned 401 |
Scripts
pnpm build # tsup -> ESM + .d.ts in dist/
pnpm typecheck # tsc --noEmit
pnpm test # vitest runLicense
MIT
