Skip to main content

Proof SDK

The Cronozen Proof SDK provides a lightweight, type-safe client for the Decision Proof API. Record AI decisions, add human approvals, and export audit-ready evidence.
Package: cronozen · Size: 11.2KB · Dependencies: Zero · Formats: CJS + ESM + TypeScript

Installation

npm install cronozen

Quick Start

import { Cronozen } from "cronozen"

const cz = new Cronozen({
  apiKey: process.env.CZ_KEY!,
  baseUrl: "https://cronozen.com/api/v1",
})

// 1. Record a decision
const event = await cz.decision.record({
  type: "agent_execution",
  actor: { id: "support_agent", type: "ai_agent" },
  action: {
    type: "refund_approved",
    input: { orderId: "ORD-1234", amount: 45000 },
  },
  aiContext: { model: "gpt-4", confidence: 0.87 },
})

// 2. Add human approval (seals with SHA-256)
const approval = await cz.decision.approve(event.id, {
  approver: { id: "team_lead", type: "human", name: "Team Lead" },
  result: "approved",
  reason: "Policy-compliant refund under $100K threshold",
})

console.log(approval.sealedHash)
// → "sha256:9943798c6313e9dd2cffa71686176d4125a65777..."

// 3. Export audit-ready evidence
const report = await cz.evidence.export(event.id)
// → JSON-LD v2 document at schema.cronozen.com/decision-proof/v2

API Reference

cz.decision.record(params)

Create a new decision event with structured metadata.
const event = await cz.decision.record({
  type: "settlement_created",            // Event type identifier
  actor: {
    id: "finance_bot",                   // Who/what made the decision
    type: "ai_agent",                    // "human" | "ai_agent" | "system" | "service"
    name: "Finance Bot",                 // Optional display name
  },
  action: {
    type: "approve_settlement",          // What action was taken
    description: "Auto-approved based on amount threshold",
    input: {                             // Action inputs (any JSON)
      settlementId: "STL-5678",
      amount: 150000,
      currency: "KRW",
    },
    output: {                            // Action outputs (any JSON)
      priority: "high",
    },
  },
  aiContext: {                           // Optional: AI involvement details
    model: "claude-sonnet-4-5-20250514",
    provider: "anthropic",
    confidence: 0.92,
    reasoning: "Amount within auto-approval threshold",
  },
  tags: ["settlement", "finance", "auto"],  // Optional: filterable tags
  idempotencyKey: "settle-STL-5678",        // Optional: prevent duplicates
})
Returns: DecisionEventResponse
{
  id: string
  decisionId: string
  type: DecisionEventType
  status: "recorded"           // Initially "recorded"
  actor: DecisionActor
  action: DecisionAction
  occurredAt: string
  aiContext?: AIContext
  tags: string[]
  evidence?: {
    id: string
    status: "pending"
    chainHash?: string
    chainIndex?: number
  }
  createdAt: string
  updatedAt: string
}
Event types: agent_execution | workflow_step | human_approval | ai_recommendation | automated_action | policy_decision | escalation | custom

Idempotency

Pass idempotencyKey to safely retry without creating duplicates. If the same key is sent again, the original event is returned instead of creating a new one.
// Safe to retry — same key returns same event
const event = await cz.decision.record({
  type: "settlement_created",
  actor: { id: "cron_job", type: "system" },
  action: {
    type: "daily_settlement",
    input: { date: "2026-03-12" },
  },
  idempotencyKey: "daily-settle-2026-03-12",
})
Use idempotency keys for any event triggered by webhooks, queue consumers, or cron jobs where retries are expected.

cz.decision.approve(id, params)

Add human approval and seal the decision with SHA-256.
const approval = await cz.decision.approve(event.id, {
  approver: {
    id: "center_director",
    type: "human",                      // "human" | "system"
    name: "Center Director",
  },
  result: "approved",                   // "approved" | "rejected"
  reason: "Reviewed and approved per settlement policy v2.1",
})

// approval.sealedHash   → "sha256:..." (only if approved)
// approval.sealedAt     → "2026-03-12T13:05:00Z"
// approval.evidenceLevel → "AUDIT_READY"
Once approved, the event is sealed — its hash becomes part of the immutable chain.
Calling approve() on an already-sealed event throws ConflictError (409). This is by design — sealed events cannot be modified.

cz.decision.get(id)

Retrieve a single decision event with full metadata.
const event = await cz.decision.get("cmmnhiobs0002bfi9mlu8eof4")
// → { id, decisionId, type, status, actor, action, evidence, ... }

cz.decision.list(params?)

List decision events with optional filters.
const { data, pagination } = await cz.decision.list({
  type: "settlement_created",    // Filter by event type
  status: "sealed",              // "recorded" | "pending_approval" | "approved" | "rejected" | "sealed"
  tag: "settlement",             // Filter by single tag
  limit: 50,                     // 1-100 (default: 20)
  offset: 0,                     // Pagination offset
})
Returns: DecisionEventListResponse
{
  data: DecisionEventResponse[]
  pagination: {
    total: number
    limit: number
    offset: number
    hasMore: boolean
  }
}

cz.evidence.get(id)

Retrieve sealed evidence with full hash chain verification data. Returns the complete event payload including input data, AI context, approval chain, and chain integrity status.
const evidence = await cz.evidence.get("cmmnhiobs0002bfi9mlu8eof4")

console.log(evidence.status)              // "sealed"
console.log(evidence.evidenceLevel)       // "AUDIT_READY"
console.log(evidence.chain.hash)          // "sha256:9e2b..."
console.log(evidence.chain.previousHash)  // "sha256:7f3a..." or null (genesis)
console.log(evidence.chain.index)         // 42
console.log(evidence.chain.domain)        // "proof"
console.log(evidence.approval)            // { approver, result, reason, approvedAt }
Use evidence.get() for chain integrity verification and compliance checks. Use evidence.export() when you need a portable audit document.
Throws NotFoundError if the decision hasn’t been sealed yet.

cz.evidence.export(id)

Export audit-ready document in JSON-LD v2 format.
const report = await cz.evidence.export("cmmnhiobs0002bfi9mlu8eof4")

// report["@context"]                   → "https://schema.cronozen.com/decision-proof/v2"
// report["@type"]                      → "DecisionProof"
// report.verification.hashAlgorithm    → "SHA-256"
// report.verification.verifyUrl        → "https://cronozen.com/verify/..."
Export includes:
  • Structured decision record (actor, action, input, AI context)
  • Approval chain with timestamps
  • SHA-256 hash chain link and position
  • Evidence level and verification metadata

Error Handling

The SDK provides 9 typed error classes for precise error handling:
Error ClassHTTP StatusWhen It Occurs
BadRequestError400Malformed request
AuthenticationError401Invalid or missing API key
ForbiddenError403API key lacks required scope
NotFoundError404Event ID does not exist
ConflictError409Event is already sealed (cannot approve twice)
ValidationError422Missing required fields, invalid types
RateLimitError429Too many requests
TimeoutErrorRequest exceeded timeout
NetworkErrorConnection failed
import {
  Cronozen,
  CronozenError,
  AuthenticationError,
  ForbiddenError,
  NotFoundError,
  ConflictError,
  ValidationError,
  BadRequestError,
  RateLimitError,
  TimeoutError,
  NetworkError,
} from "cronozen"

try {
  await cz.decision.approve(id, payload)
} catch (e) {
  if (e instanceof ConflictError) {
    // 409 — Event already sealed. This is safe to ignore on retries.
    const existing = await cz.evidence.get(id)
    console.log("Already sealed at:", existing.sealedAt)
  }
  if (e instanceof ForbiddenError) {
    // 403 — API key scope insufficient
    console.error("Need proof:write scope")
  }
  if (e instanceof NotFoundError) {
    // 404 — Event does not exist
  }
  if (e instanceof ValidationError) {
    // 422 — Invalid request body
    console.error("Validation errors:", e.details)
  }
  if (e instanceof CronozenError) {
    // Any other API error
    console.error(e.code, e.status, e.details)
  }
}

Handling 409 Conflict (Sealed Events)

The most common non-trivial error. Occurs when two approvers act simultaneously, or retry logic re-sends an already-successful approval.
async function safeApprove(eventId: string, payload: ApproveDecisionRequest) {
  try {
    return await cz.decision.approve(eventId, payload)
  } catch (e) {
    if (e instanceof ConflictError) {
      // Already sealed — not an error, just return the existing evidence
      return await cz.evidence.get(eventId)
    }
    throw e
  }
}
Combine idempotencyKey on decision.record() with ConflictError handling on decision.approve() for fully retry-safe integrations.

Non-Blocking Integration

For production use, wrap Proof calls so they never block your business logic:
function fireProof(
  cz: Cronozen,
  fn: (cz: Cronozen) => Promise<unknown>,
  label: string,
): void {
  fn(cz).catch((error) => {
    console.error(`[Proof] ${label} failed (non-blocking):`, error)
  })
}

// In your settlement handler:
const settlement = await createSettlement(data)

// Non-blocking evidence recording
fireProof(cz, (cz) =>
  cz.decision.record({
    type: "automated_action",
    actor: { id: actorId, type: "system" },
    action: {
      type: "settlement_created",
      input: { settlementId: settlement.id, amount: settlement.amount },
    },
    tags: ["settlement", vertical],
    idempotencyKey: `settlement-created-${settlement.id}`,
  }),
  `settlement-record-${settlement.id}`,
)
If CZ_KEY environment variable is not set, you can make the SDK client silently disable itself. This means you can add Proof integration to your codebase and activate it per-environment by simply setting the key.

Integration Patterns

Settlement Flow

Settlement Created    →  cz.decision.record()   →  Event recorded
Director Approves     →  cz.decision.approve()  →  SHA-256 sealed
Audit Request         →  cz.evidence.export()   →  JSON-LD report

AI Agent Workflow

Agent Decides         →  cz.decision.record({ aiContext })
Human Reviews         →  cz.decision.approve()
Compliance Check      →  cz.evidence.get() + verify chain
External Audit        →  cz.evidence.export()

Webhook (Coming Soon)

Event Sealed          →  Webhook fires to your endpoint
                      →  { event, chainHash, approval }

Configuration

const cz = new Cronozen({
  apiKey: process.env.CZ_KEY!,              // Required: API key
  baseUrl: "https://cronozen.com/api/v1",   // Required: API endpoint
  timeout: 10000,                            // Optional: request timeout (ms)
  fetch: customFetch,                        // Optional: custom fetch implementation
})
OptionRequiredDefaultDescription
apiKeyYesAPI key with proof:write or proof:read scope
baseUrlYesProof API base URL
timeoutNo10000Request timeout in ms
fetchNoglobalThis.fetchCustom fetch implementation

What’s Next

  • Proof Admin Console — Visual decision timeline and audit dashboard
  • Multi-approval policies — Route approvals by amount, risk level, or decision type
  • Webhook notifications — Real-time alerts on specific event types
  • Batch recording — Record multiple decisions in a single API call

Get API Key

Sign up and generate your Proof API key to get started.