Webhooks & Events
Receive real-time notifications when jobs complete, fail, or require review.
Setting up a webhook
Register an HTTPS endpoint to receive event notifications. The API returns a signing secret you will use to verify payloads.
register.py
python
webhook = agent.webhooks.create(url="https://yourapp.example.com/webhooks/curvestone",events=["job.completed", "job.failed", "review.required"],)print(f"Secret: {webhook.secret}") # whsec_a1b2c3d4...
Available events
| Event | Description |
|---|---|
job.completed | A job finished processing successfully |
job.failed | A job encountered an unrecoverable error |
review.required | An amber or red triage requires human review |
monitor.triggered | A monitor detected a change in its target |
Payload format
Every webhook delivery is a POST request with a JSON body. The top-level shape is consistent across all event types.
job.completed payload
json
{"id": "evt_abc123","type": "job.completed","created_at": "2026-02-21T14:32:27Z","data": {"id": "job_7kTx9mNpQ2","type": "check","status": "completed","triage": "amber","reference": "CASE-2026-00451","skills_executed": 7,"processing_time": "147s"}}
Verification
Every delivery includes a Curvestone-Signature header containing an HMAC-SHA256 digest of the raw request body. Verify it against the webhook secret returned during registration.
verify.py
python
import hmac, hashlibdef verify_webhook(payload: bytes, signature: str, secret: str) -> bool:expected = hmac.new(secret.encode(),payload,hashlib.sha256,).hexdigest()return hmac.compare_digest(f"sha256={expected}", signature)
Handler example
A minimal Express.js handler that verifies the signature and routes events.
server.ts
typescript
import express from 'express';app.post('/webhooks/curvestone', express.raw({ type: 'application/json' }), (req, res) => {const signature = req.headers['curvestone-signature'] as string;if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET!)) {return res.status(401).json({ error: 'Invalid signature' });}const event = JSON.parse(req.body);switch (event.type) {case 'job.completed':console.log(`Job ${event.data.id} completed: ${event.data.triage}`);break;case 'review.required':console.log(`Job ${event.data.id} needs review`);break;}res.json({ received: true });});