Curvestone

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

EventDescription
job.completedA job finished processing successfully
job.failedA job encountered an unrecoverable error
review.requiredAn amber or red triage requires human review
monitor.triggeredA 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, hashlib
def 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 });
});