Events

The Events resource gives you access to everything that happens in Clockwork as an ordered, immutable log. You can stream events in real time, replay past events for recovery, or subscribe a webhook endpoint to receive push notifications.

When to use client.events

  • Building a real-time dashboard that reacts to task completions and delays
  • Triggering downstream systems when a workflow reaches a milestone
  • Replaying events to rebuild state after an outage
  • Auditing workflow history for compliance

Methods

MethodDescription
list(opts?)Fetch recent events, optionally filtered
stream()Open a real-time SSE stream
replay(input?)Replay events from a sequence cursor
subscribe(input)Register a webhook endpoint
listSubscriptions()List active subscriptions
unsubscribe(id)Deactivate a subscription

events.stream()

Returns an async generator that yields events as they occur. The connection is a Server-Sent Events (SSE) stream.

typescript
for await (const event of client.events.stream()) {
  console.log(event.type, event.data, event.ts)
 
  if (event.type === 'task.completed') {
    await triggerDownstreamSystem(event.data.taskId)
  }
  if (event.type === 'workflow.completed') {
    await sendCompletionEmail(event.data.instanceId)
  }
}

Stream event types

TypeFired when
task.createdA task is created
task.updatedA task is updated
task.completedA task is marked complete
workflow.startedA workflow instance starts
workflow.completedAll tasks in an instance complete
workflow.failedAn instance fails
artifact.createdAn artifact is attached
constraint.violatedA constraint is breached (conflict detected)

events.subscribe(input)

Registers a webhook endpoint to receive signed POST requests when events occur.

Parameters

ParameterTypeRequiredDescription
urlstringyesHTTPS endpoint that will receive events
eventsWebhookEvent[]yesList of event types to subscribe to
typescript
const { data: sub } = await client.events.subscribe({
  url: 'https://yourapp.com/webhooks/clockwork',
  events: ['task_completed', 'task_delayed', 'conflict_detected', 'workflow_completed'],
})
 
// Store the secret — it's only shown once
console.log('Webhook secret:', sub.secret)

Available webhook events

EventDescription
task_readyA task became ready (all predecessors completed)
task_completedA task was marked complete
task_delayedA task was delayed
conflict_detectedA scheduling conflict was detected
workflow_completedA workflow instance completed
*All events

Verifying webhook signatures

Every webhook request includes X-Clockwork-Signature: sha256=<hex>. Always verify this before processing:

typescript
import { createHmac, timingSafeEqual } from 'crypto'
 
function verifyWebhook(signature: string, secret: string, rawBody: string): boolean {
  const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex')
  return timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
}
 
// Express example
app.post('/webhooks/clockwork', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-clockwork-signature'] as string
  if (!verifyWebhook(sig, process.env.WEBHOOK_SECRET!, req.body.toString())) {
    return res.status(401).send('Invalid signature')
  }
 
  const payload = JSON.parse(req.body.toString())
  console.log(payload.event, payload.task_id, payload.timestamp)
  res.status(200).send('OK')
})

events.list(opts?)

Fetches recent events. Newest first.

typescript
const { data: events } = await client.events.list({
  workflowInstanceId: 'wi_abc123',
  type: 'task.completed',
  limit: 50,
})

events.replay(input?)

Replays the full event log from a sequence cursor. Useful for rebuilding state after an outage.

typescript
const { data: events } = await client.events.replay({
  fromSequence: 0,
  limit: 500,
})

Type reference

typescript
interface Event {
  id: string
  sequence: number
  projectId: string | null
  type: string
  subjectType: string | null
  subjectId: string | null
  payload: Record<string, unknown> | null
  correlationId: string | null
  createdAt: string
}
 
interface EventSubscription {
  id: string
  url: string
  events: WebhookEvent[]
  isActive: boolean
  createdAt: string
  secret?: string // Only on creation
}