Skip to main content
Local logs are enough while one developer is tuning a contract. The SDK is for production: shared traces, acceptance rates, top failing rules, retries, and alerts across real traffic. @withboundary/sdk captures each contract run as a structured event, batches events in memory, and sends them to the Boundary dashboard when you configure an API key or custom sink.
import { defineContract } from "@withboundary/contract";
import { createBoundaryLogger } from "@withboundary/sdk";

const logger = createBoundaryLogger({
  apiKey: process.env.BOUNDARY_API_KEY,
  environment: "production",
});

const contract = defineContract({
  name: "lead-scoring",
  schema,
  rules,
  logger,
});
Every contract.accept(run) call now produces a BoundaryLogEvent with the outcome, attempt count, duration, failure category, repair messages, and rule attribution.

What you get on the dashboard

  • Acceptance rate per contract, per environment, per model — track regressions when you swap prompts or models.
  • Top failing rules — the named rules driving most retries, with example messages.
  • Repair patterns — what the model needed to be told to fix on the second pass.
  • Latency breakdown — wall-clock per run including retries, broken down by attempt count.
  • Failure category mixRULE_ERROR vs PARSE_ERROR vs REFUSAL, so you know whether to tune your schema, your rules, or your prompt.
  • SDK + runtime attribution — version + runtime stamped on every event for debugging.

Why a contract logger and not a generic tracer

Generic observability shows you that an HTTP request was slow. Boundary shows you which rule rejected the output and what the repair was. Because the SDK is wired into the contract loop, it knows about attempts, repairs, and rule attribution that a generic OpenTelemetry span never sees. Use both — see Custom sinks for fanning events to OTel, Datadog, or your own collector alongside the Boundary dashboard.

What happens after the event leaves your process

The SDK queues events in memory and flushes them in batches — by default whenever the queue hits 20 events or 5 seconds elapse. One flush is one HTTPS POST to https://api.withboundary.com/v1/ingest. The dashboard typically reflects the run within a few seconds of flush. If the network or the backend hiccups, the transport retries with exponential backoff and a circuit breaker so a temporary outage doesn’t turn into a retry storm. See Resilience for the full failure model. For short-lived runtimes (Lambda, Edge, Workers, browser), drain the queue before the process exits so events don’t sit in a dead container. The SDK wires up the right lifecycle hooks per runtime — see Shutdown and the runtime platform pages for the per-runtime patterns.

Safe by default

Two things make it safe to leave the SDK wired up in every environment:
  1. Null logger in dev. When neither apiKey nor write is configured, createBoundaryLogger returns null. Passing null to defineContract({ logger }) is a no-op — the contract runs normally, no events sent, no errors.
  2. Conservative capture policy. Raw LLM inputs and outputs are off by default. Only the run shape, repair messages, and failure attribution leave the process. Opt into raw data explicitly — see Capture policy and Security & data handling.

Where to go next

Quickstart

Install and see your first event

Production observability

Decide what to capture and how to roll it out

Capture policy

Control what leaves the process

Redaction

Scrub PII before events are sent

Contract library docs

Looking for the correctness engine itself? See Contracts and Rules in Core Concepts.