Skip to main content

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

  1. Compute an HMAC-SHA256 hash of your raw JSON request body using your webhook secret.
  2. Send the hex-encoded result in the X-Orqestra-Signature header.
caution

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-Signature return 401 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.