Webhooks
Webhooks send HTTP POST requests to your URL when events occur in your workspace. Use them with n8n, Zapier, Make, or custom servers.
Prerequisites
Section titled “Prerequisites”- Settings → Developers access (capability Webhooks or team admin)
- A publicly reachable HTTPS endpoint (localhost works only with a tunnel like ngrok)
- Server env:
PUBLISHING_ENCRYPTION_KEYmust be set (see Installation)
Create a webhook
Section titled “Create a webhook”- Go to Settings → Developers → Webhooks
- Click Add webhook
- Enter a name and URL (HTTPS recommended)
- Select events to subscribe to (or all)
- Save — the signing secret is shown once. Store it securely.
Event types
Section titled “Event types”| Event | Fires when |
|---|---|
document.created | New document |
document.updated | Document content or metadata changed |
document.deleted | Document removed |
document.published | Document published to a channel |
collection.created | New collection |
collection.updated | Collection renamed or moved |
collection.deleted | Collection removed |
notification.created | In-app notification created (per recipient) |
Payload format
Section titled “Payload format”Each delivery is JSON:
{ "id": "delivery-uuid", "createdAt": "2026-05-22T10:00:00.000Z", "event": "document.updated", "payload": { "id": "document-uuid", "model": {} }}The model object contains a sanitized snapshot of the affected resource.
Verify signatures
Section titled “Verify signatures”Every request includes:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Orimora-Event | Event name (e.g. document.updated) |
X-Orimora-Signature | t=<unix_seconds>,v=<hex_hmac> |
User-Agent | Orimora-Webhooks/1.0 |
The signature is HMAC-SHA256 over the string "<timestamp>.<raw_body>" using your webhook secret.
Verification steps:
- Read the raw request body (before JSON parsing)
- Parse
X-Orimora-Signature— extracttandv - Reject if
tis too old (replay protection — e.g. ±5 minutes) - Compute
HMAC-SHA256(secret, t + "." + body)as hex - Compare with
vusing a timing-safe comparison
import { createHmac, timingSafeEqual } from 'crypto';
function verifyOrimoraWebhook(secret, signatureHeader, rawBody, maxAgeSec = 300) { const parts = Object.fromEntries(signatureHeader.split(',').map((p) => p.split('='))); const t = Number(parts.t); const v = parts.v; if (!t || !v || Math.abs(Date.now() / 1000 - t) > maxAgeSec) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
try { return timingSafeEqual(Buffer.from(v, 'hex'), Buffer.from(expected, 'hex')); } catch { return false; }}import hashlibimport hmacimport time
def verify_orimora_webhook(secret: str, signature_header: str, raw_body: bytes, max_age_sec: int = 300) -> bool: parts = dict(part.split("=", 1) for part in signature_header.split(",")) t = int(parts.get("t", 0)) v = parts.get("v", "")
if not t or not v or abs(time.time() - t) > max_age_sec: return False
expected = hmac.new( secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256, ).hexdigest()
return hmac.compare_digest(v, expected)function verifyOrimoraWebhook(string $secret, string $signatureHeader, string $rawBody, int $maxAgeSec = 300): bool { $parts = []; foreach (explode(',', $signatureHeader) as $part) { [$k, $v] = explode('=', $part, 2); $parts[$k] = $v; }
$t = (int) ($parts['t'] ?? 0); $v = $parts['v'] ?? '';
if (!$t || !$v || abs(time() - $t) > $maxAgeSec) { return false; }
$expected = hash_hmac('sha256', "{$t}.{$rawBody}", $secret); return hash_equals($expected, $v);}SECRET="your_webhook_secret"BODY='{"event":"document.updated","data":{}}'T=$(date +%s)SIG=$(echo -n "${T}.${BODY}" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -X POST https://your-endpoint.example.com/webhook \ -H "Content-Type: application/json" \ -H "X-Orimora-Signature: t=${T},v=${SIG}" \ -H "X-Orimora-Event: document.updated" \ -d "$BODY"Delivery behaviour
Section titled “Delivery behaviour”- Deliveries run asynchronously via a background queue
- Failed deliveries are retried with backoff
- Webhooks with high failure rates are auto-disabled (configurable via
WEBHOOK_FAILURE_*env vars) - Delivery logs are retained for debugging (see Settings UI)
URLs must not point to private IPs or cloud metadata endpoints (SSRF protection).
Test with n8n
Section titled “Test with n8n”Orimora ships an n8n community node (packages/n8n-nodes-orimora). Alternatively, use an n8n Webhook trigger node and paste the test URL when creating the Orimora webhook.
See also
Section titled “See also”- Configuration —
WEBHOOK_FAILURE_*thresholds - Publishing channels — different outbound sync model
- Permissions & groups — Webhooks capability