Webhooks
Webhooks let your systems react to ORKSTRA events without polling. Subscribe to specific event types, receive HMAC-signed POSTs to your callback URL, and process them idempotently.
Subscribing
Admin → API → Webhooks → New subscription (UI), or via the API:
curl -X POST https://api.orkstra.com/v1/webhooks \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"url": "https://your-app.example.com/orkstra-webhook",
"events": ["ipc.certified", "vo.agreed", "ncr.created"]
}'
Response includes a secret — store this securely; you need it to verify signatures.
Signature verification
Every POST carries:
X-Orkstra-Signature: <hex>— HMAC-SHA256 over the raw body using your subscription secret.X-Orkstra-Event-Id: <uuid>— unique event ID for dedupe.X-Orkstra-Event-Type: <type>— e.g.,ipc.certified.X-Orkstra-Timestamp: <unix>— issuance timestamp.
Python verification:
import hmac
import hashlib
def verify(body_bytes: bytes, sig_header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body_bytes, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig_header)
Always compute the HMAC against the raw bytes before JSON parsing — middleware that re-serializes can change byte-level whitespace and break the signature.
Node.js verification:
import crypto from "crypto";
function verify(bodyBuf, sigHeader, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(bodyBuf)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(sigHeader)
);
}
Idempotency
Retries can deliver the same event twice. Dedupe using X-Orkstra-Event-Id — track seen IDs in your store (e.g., Redis with 7-day TTL).
Event types (highlights)
| Event | Payload |
|---|---|
ipc.created | IPC ID, project, draft amount |
ipc.submitted | IPC ID, submitter, amount |
ipc.certified | IPC ID, certified amount, certificate URL |
ipc.paid | IPC ID, payment ref |
vo.created | VO ID, project, description |
vo.priced | VO ID, total cost impact |
vo.agreed | VO ID, final amount, time impact |
vo.absorbed | VO ID, new BOQ revision |
ncr.created | NCR ID, project, severity |
ncr.closed | NCR ID, closure date |
hse.incident.reported | Incident ID, classification |
permit.issued | Permit ID, type, validity |
permit.expiring | Permit ID, hours until expiry |
dms.transmittal.sent | Transmittal ID, recipients, doc count |
Full list: docs.orkstra.com/api/events.
Retry policy
If your endpoint doesn't return 2xx within 10 seconds:
- Retry at 1m, 5m, 30m, 2h, 6h, 24h.
- After 24h with no success, the delivery is marked permanently failed.
- Permanently failed deliveries appear in Admin → API → Webhooks → Deliveries.
- Replay manually any time.
Best practices
- Verify the signature first. Reject unsigned or wrong-signature requests with 401.
- Return 2xx immediately. Queue the work and process async. ORKSTRA will retry if you exceed 10s.
- Dedupe on
X-Orkstra-Event-Id. - Handle out-of-order delivery. Retries can deliver out of order; use the payload's
event_timestampto handle. - Treat secrets as secrets. Never log them. Rotate annually.
Testing
- Ping test: every new subscription receives a
webhook.pingevent. Use it to validate your endpoint. - Manual fire: Admin → API → Webhooks → [sub] → Fire test event.
- Local dev: use ngrok or Cloudflare Tunnel to expose your local server.
See also: API reference, API & Webhooks module.