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
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 Class HTTP Status When It Occurs BadRequestError400 Malformed request AuthenticationError401 Invalid or missing API key ForbiddenError403 API key lacks required scope NotFoundError404 Event ID does not exist ConflictError409 Event is already sealed (cannot approve twice) ValidationError422 Missing required fields, invalid types RateLimitError429 Too many requests TimeoutError— Request exceeded timeout NetworkError— Connection 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
})
Option Required Default Description apiKeyYes — API key with proof:write or proof:read scope baseUrlYes — Proof API base URL timeoutNo 10000Request timeout in ms fetchNo globalThis.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.