Skip to main content

Install

npm install @withboundary/contract @withboundary/sdk

Set the API key

Get an API key from the Boundary dashboard. The SDK reads BOUNDARY_API_KEY from the environment by default.
export BOUNDARY_API_KEY=bnd_live_...

Wire a logger to your contract

import { z } from "zod";
import { defineContract } from "@withboundary/contract";
import { createBoundaryLogger } from "@withboundary/sdk";

const logger = createBoundaryLogger({
  environment: "production",  // "production" | "staging" | "development"
});

const contract = defineContract({
  name: "lead-scoring",       // appears on every event
  schema: z.object({
    tier: z.enum(["hot", "warm", "cold"]),
    score: z.number().min(0).max(100),
  }),
  rules: [
    (d) => d.tier !== "hot" || d.score > 70
      || `hot leads require score > 70, got ${d.score}`,
  ],
  logger,
});

Run a contract

const result = await contract.accept(async (attempt) => {
  const res = await openai.responses.create({
    model: "gpt-4.1",
    input: ["Score this lead", ...attempt.repairs],
  });
  return res.output_text;
});
Every accept() produces one event at the end of the run. The event is queued, batched, and shipped to Boundary’s ingest endpoint.

What gets sent

The SDK’s default capture policy is conservative. On a successful run you’ll see roughly:
{
  "contractName": "lead-scoring",
  "environment": "production",
  "timestamp": "2026-04-20T09:45:12.104Z",
  "attempt": 2,
  "maxAttempts": 3,
  "ok": true,
  "durationMs": 1843,
  "repairs": [
    { "role": "user", "content": "hot leads require score > 70, got 25" }
  ],
  "rulesCount": 1,
  "sdk": { "name": "@withboundary/sdk", "version": "0.3.1", "runtime": "node/22.12.0" }
}
Raw LLM input and output are not included unless you opt in — see Capture policy.

Dev-safe by default

If BOUNDARY_API_KEY is not set and you don’t pass a custom write, createBoundaryLogger returns null. Passing null to defineContract is a no-op — the contract runs normally, no events sent, no errors.
// Works in prod. No-op locally.
const logger = createBoundaryLogger();

Next steps

Capture policy

Turn raw inputs/outputs on or off

Redaction

Scrub PII before events leave the process

Shutdown

Drain the queue on serverless / edge / workers

createBoundaryLogger

Every option, with defaults