CohesionXL

Webhooks

CohesionXL sends real-time HTTP POST notifications when key events occur. Use webhooks to keep external systems in sync with your planning data.

Supported Events

| Event | Description | |-------|-------------| | initiative.created | A new initiative was created | | initiative.updated | An initiative's fields were modified | | initiative.status_changed | An initiative moved to a new lifecycle stage | | scenario.created | A new scenario was created | | scenario.reconciled | A scenario was reconciled into the active plan | | approval.requested | An approval request was submitted | | approval.completed | An approval chain was fully resolved | | forecast.completed | A Monte Carlo simulation finished | | capacity.alert | A capacity constraint violation was detected |

Registering a Webhook

curl -X POST https://api.cohesionxl.com/v1/webhooks \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/cohesionxl",
    "events": ["initiative.status_changed", "approval.completed"],
    "secret": "your_webhook_secret"
  }'

Response:

{
  "id": "whk_abc123",
  "url": "https://your-app.com/webhooks/cohesionxl",
  "events": ["initiative.status_changed", "approval.completed"],
  "active": true,
  "created_at": "2025-01-15T10:00:00Z"
}

Payload Format

All webhook payloads follow the same envelope structure:

{
  "id": "evt_xyz789",
  "type": "initiative.status_changed",
  "timestamp": "2025-01-15T10:30:00Z",
  "organization_id": "org_123",
  "data": {
    "initiative_id": "init_456",
    "previous_status": "APPROVED",
    "new_status": "IN_PROGRESS",
    "changed_by": "user_789"
  }
}

Verifying Signatures

Every webhook request includes an X-CohesionXL-Signature header containing an HMAC-SHA256 signature of the request body, computed using your webhook secret.

import crypto from "crypto";

function verifyWebhook(body: string, signature: string, secret: string): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Retry Policy

Failed deliveries (non-2xx responses or timeouts) are retried with exponential backoff:

  • Attempt 1: Immediate
  • Attempt 2: 1 minute
  • Attempt 3: 5 minutes
  • Attempt 4: 30 minutes
  • Attempt 5: 2 hours

After 5 failed attempts, the webhook is marked as failing and an alert is sent to organization admins.