Webhooks
In this guide, we'll look at how to use webhooks for async job processing in the Schedule Intelligence APIs. For large schedules (>1,000 tasks), analyses are processed asynchronously with webhook callbacks when complete.
When to use webhooks
The Schedule Intelligence platform uses webhooks for async job processing:
- Sync processing: ≤1,000 tasks (immediate response)
- Async processing: >1,000 tasks (webhook callback)
For async jobs, you provide a callback_url in your request, and we'll POST the results when processing completes.
Async job workflow
curl -X POST "https://api.bellatorsi.com/api/v1/health/score/async" \
-H "Content-Type: application/json" \
-H "x-api-key: your-starter-tier-or-higher-key" \
-d '{
"tasks": [
{"id": "1", "duration_days": 5, "predecessors": []},
... // >1,000 tasks
],
"callback_url": "https://your-app.com/webhooks/schedule-analysis"
}'
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued",
"created_at": "2026-01-29T12:00:00Z"
}
Webhook payload
When processing completes, we POST the results to your callback_url. The payload wraps the analysis result in the standard BellatorResponse envelope:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"result": {
"status": "warning",
"score": 85.5,
"grade": "B",
"summary": "Schedule has moderate critical path density.",
"breakdown": {
"criticality": { "score": 82.0, "weight": 0.40, "label": "Critical Path Density" },
"float_erosion": { "score": 91.0, "weight": 0.35, "label": "Float Erosion" }
},
"findings": [
{
"code": "DCMA_CHECK_12_CRITICAL_DENSITY",
"severity": "medium",
"title": "Elevated Critical Path Density",
"description": "38% of tasks are on the critical path (threshold: 30%)."
}
],
"recommendations": [],
"data": { "critical_path_length": 120, "total_tasks": 1500 },
"metadata": {
"task_count": 1500,
"processing_time_ms": 4200,
"api_version": "0.2.0",
"endpoint": "/api/v1/health/score"
}
},
"created_at": "2026-01-29T12:00:00Z",
"completed_at": "2026-01-29T12:03:45Z"
}
Webhook security
Verify webhook signatures
All webhook requests include an X-Webhook-Signature header with an HMAC-SHA256 signature:
import hmac
import hashlib
def verify_webhook_signature(payload_body: bytes, signature: str, secret: str) -> bool:
"""Verify webhook signature."""
expected_sig = hmac.new(
secret.encode(),
payload_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_sig)
# In your webhook handler
@app.post("/webhooks/schedule-analysis")
async def handle_webhook(request: Request):
signature = request.headers.get("X-Webhook-Signature")
body = await request.body()
if not verify_webhook_signature(body, signature, WEBHOOK_SECRET):
raise HTTPException(status_code=401, detail="Invalid signature")
# Process webhook...
Retry logic
Webhooks are retried with exponential backoff:
- Max retries: 5 attempts
- Backoff: 1s, 2s, 4s, 8s, 16s
- Timeout: 30s per request
- Dead Letter Queue: Failed webhooks after 5 retries
Polling alternative
If webhooks aren't suitable, you can poll the job status endpoint:
curl "https://api.bellatorsi.com/api/v1/jobs/550e8400-e29b-41d4-a716-446655440000" \
-H "x-api-key: your-api-key"
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"result": {...},
"created_at": "2026-01-29T12:00:00Z",
"completed_at": "2026-01-29T12:03:45Z"
}
Job statuses
-
queued: Job accepted and waiting in the processing queue.
-
processing: Job is actively being analyzed.
-
completed: Job finished successfully. Results available via polling or webhook.
-
failed: Job failed due to a processing error. Check the error message in the job status response.
-
cancelled: Job was cancelled before completion via the cancel endpoint.
Webhook delivery requires Starter tier or above. Free tier analyses are synchronous only (up to 1,000 tasks) and do not support async job submission or webhooks.
Best practices
- Return 200 quickly: Acknowledge receipt immediately, process async
- Idempotency: Handle duplicate webhooks gracefully (check
job_id) - Verify signatures: Always validate
X-Webhook-Signature - Timeout handling: Respond within 30 seconds
- Error logging: Log failed webhooks for debugging