Webhook Security & Idempotency
This page covers two related topics for production-hardening your integration: signing payloads with HMAC so Orqestra can reject tampered requests, and using idempotency keys to prevent duplicate notifications.
HMAC payload signing
For production environments, configure a webhook secret for your tenant. Once set, the engine rejects any request without a valid signature.
How to sign your payload
- Compute an HMAC-SHA256 hash of your raw JSON request body using your webhook secret.
- Send the hex-encoded result in the
X-Orqestra-Signatureheader.
Always compute the signature over the raw JSON string you're sending as the body — not over a parsed and re-serialized object. Key ordering in JSON serialization can differ between languages and would produce a different signature.
Node.js / TypeScript
import { createHmac } from 'crypto';
const body = JSON.stringify(eventPayload);
const signature = createHmac('sha256', process.env.ORQESTRA_WEBHOOK_SECRET!)
.update(body)
.digest('hex');
await fetch(orqestraURL + '/api/v1/events/trigger', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.ORQESTRA_API_KEY!,
'X-Orqestra-Signature': signature,
},
body,
});
Python
import hmac, hashlib, json, os
body = json.dumps(event_payload, separators=(',', ':'))
signature = hmac.new(
os.environ['ORQESTRA_WEBHOOK_SECRET'].encode(),
body.encode(),
hashlib.sha256
).hexdigest()
requests.post(url, data=body, headers={
'Content-Type': 'application/json',
'x-api-key': os.environ['ORQESTRA_API_KEY'],
'X-Orqestra-Signature': signature,
})
Go
mac := hmac.New(sha256.New, []byte(os.Getenv("ORQESTRA_WEBHOOK_SECRET")))
mac.Write(bodyBytes)
signature := hex.EncodeToString(mac.Sum(nil))
req.Header.Set("X-Orqestra-Signature", signature)
Configuring a webhook secret
Ask your platform operator to set a webhook secret on your tenant. Once configured:
- Requests without
X-Orqestra-Signaturereturn401 Unauthorized. - Requests with an invalid signature return
401 Unauthorized.
Idempotency
The engine automatically deduplicates events using a composite key of tenantId + eventType + payload hash. If your backend retries a failed HTTP call before getting a response, the second identical request won't produce a duplicate notification.
Using a custom idempotency key
To override automatic deduplication — for example, to send a genuine reminder that uses the same event type and payload — provide a unique X-Idempotency-Key header:
curl -X POST https://your-orqestra-host/api/v1/events/trigger \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_KEY" \
-H "X-Idempotency-Key: reminder-2026-04-22-user-001" \
-d '{ "eventType": "global.warning", "payload": { ... } }'
If Orqestra receives a second request with the same X-Idempotency-Key within 24 hours, it returns 200 OK (duplicate acknowledged) without dispatching again.
Idempotency entries expire after 24 hours (configurable via IDEMPOTENCY_TTL_HOURS).
Best practices
- Generate idempotency keys from a stable combination of your entity ID + event type + date:
order-confirmed-${orderId}-${date}. - Don't reuse keys across different logical events — a retry and a genuine resend should have different keys.
- If your system doesn't retry automatically, you can skip this header entirely.