Skip to content

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

  1. Return 200 quickly: Acknowledge receipt immediately, process async
  2. Idempotency: Handle duplicate webhooks gracefully (check job_id)
  3. Verify signatures: Always validate X-Webhook-Signature
  4. Timeout handling: Respond within 30 seconds
  5. Error logging: Log failed webhooks for debugging

Next steps

Was this page helpful?

Your feedback helps us improve docs, reference pages, and Playground flows.

Topic: Webhooks · Page: /docs/webhooks

TermsPrivacyContact

Schedule analysis guidance. Not legal advice. You remain responsible for contract and agency compliance.