Skip to main content
Every contract run produces exactly one BoundaryLogEvent. This is the shape that hits the network (or your custom write sink).
import type { BoundaryLogEvent } from "@withboundary/sdk";

Full shape

interface BoundaryLogEvent {
  // identity — always present
  contractName: string;
  environment?: string;
  timestamp: string;          // ISO 8601

  // run metadata — capture.metadata (default on)
  attempt: number;
  maxAttempts: number;
  ok: boolean;
  durationMs: number;
  model?: string;
  rulesCount?: number;

  // failure details — capture.errors (default on)
  category?: FailureCategory;
  issues?: string[];

  // repair context — capture.repairs (default on)
  repairs?: Array<{ role: string; content: string }>;

  // raw data — capture.inputs / capture.outputs (default off)
  input?: unknown;
  output?: unknown;

  // stamped by the SDK — always present
  sdk?: {
    name: string;
    version: string;
    runtime?: string;         // "node/22.12.0", "browser", or undefined
  };
}

Field-level reference

Identity

FieldSet byNotes
contractNameContractThe name field you passed to defineContract. Required.
environmentLoggerFrom createBoundaryLogger({ environment }). Buckets data on the dashboard.
timestampSDKStamped at emit time, not at run start.

Run metadata

FieldMeaning
attemptHow many attempts the run took. 1 means accepted first try.
maxAttemptsFrom the contract’s retry config at the time of the run.
okWhether the contract accepted the output.
durationMsTotal wall-clock time for the run including retries.
modelPer-call contract.accept(run, { model }) override, else the logger’s default model, else undefined.
rulesCountNumber of rules on the contract at runtime. Dashboard uses the latest seen.

Failure details

Only present when ok: false and capture.errors is on.
FieldMeaning
categoryEMPTY_RESPONSE, REFUSAL, NO_JSON, TRUNCATED, PARSE_ERROR, VALIDATION_ERROR, RULE_ERROR, or RUN_ERROR.
issuesSchema errors or rule violations from the last failed attempt.

Repair context

The repair messages generated from the last failed attempt. Present whenever the run ran more than one attempt and capture.repairs is on — even on successful runs (repairs from an earlier failed attempt that the repair loop fixed).
repairs: [
  { role: "user", content: "hot leads require score > 70, got 25" }
]

Raw data (opt-in)

FieldControlled by
inputcapture.inputs — off by default
outputcapture.outputs — off by default
Treat these as sensitive. See Capture policy and Redaction.

SDK attribution

Stamped automatically. Used for attribution on the dashboard and for debugging version-specific behavior.
sdk: {
  name: "@withboundary/sdk",
  version: "0.3.1",
  runtime: "node/22.12.0"
}
runtime is "node/<version>" on Node, "browser" where navigator.userAgent exists, or omitted on edge runtimes (Cloudflare Workers, Vercel Edge) where neither is available.

Lifecycle

Each hop runs before the event is visible on the next. If capture strips a field, it can’t be added back by beforeSend for that event.

Example success event

{
  "contractName": "invoice-extraction",
  "environment": "production",
  "timestamp": "2026-04-20T09:45:12.104Z",
  "attempt": 2,
  "maxAttempts": 3,
  "ok": true,
  "durationMs": 2104,
  "model": "gpt-4.1",
  "rulesCount": 2,
  "repairs": [
    {
      "role": "user",
      "content": "subtotal + tax != total (100 + 10 = 110, got 105)"
    }
  ],
  "sdk": {
    "name": "@withboundary/sdk",
    "version": "0.3.1",
    "runtime": "node/22.12.0"
  }
}

Example failure event

{
  "contractName": "agent-action-validation",
  "environment": "production",
  "timestamp": "2026-04-20T09:47:03.221Z",
  "attempt": 3,
  "maxAttempts": 3,
  "ok": false,
  "durationMs": 5829,
  "model": "claude-haiku",
  "rulesCount": 4,
  "category": "RULE_ERROR",
  "issues": [
    "cannot close a ticket with status needs_review"
  ],
  "repairs": [
    {
      "role": "user",
      "content": "cannot close a ticket with status needs_review"
    }
  ],
  "sdk": { "name": "@withboundary/sdk", "version": "0.3.1", "runtime": "node/22.12.0" }
}

See also

Capture policy

Which bucket controls which field

createBoundaryLogger

Full options reference