Skip to main content

Webhook validation

Every webhook request includes an X-Orq-Signature header containing an HMAC-SHA256 signature. You should verify this signature to confirm the request is authentic.

How the signature is computed

The signature is computed over a JSON string containing only three fields from the event body, in this exact order:
{"id":"<event_id>","created":"<event_created>","type":"<event_type>"}
This payload is signed using your webhook secret with HMAC-SHA256, producing a hex-encoded digest.

Headers included in every request

HeaderDescription
X-Orq-SignatureHMAC-SHA256 hex digest of the signature payload
X-Orq-Hook-IDThe event ID
X-Orq-EventThe event type
User-AgentOrq-Webhook/

Key points

  • Signature payload: The signature covers only {"id", "created", "type"} — not the full request body.
  • Signature Header: The signature is retrieved from the X-Orq-Signature header.
  • Timing-safe comparison: Always use constant-time string comparison to prevent timing attacks.
  • JSON key order matters: The payload must be serialized as {"id":...,"created":...,"type":...} with no spaces.

Verification examples

import express, { Request, Response } from 'express';
import crypto from 'crypto';

const app = express();
const port = 3000;

const webhookSecret = 'orq_wk_...';

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

app.post('/webhooks', express.json(), (req: Request, res: Response) => {
  const signature = req.headers['x-orq-signature'] as string;
  const { id, created, type } = req.body;

  const signaturePayload = JSON.stringify({ id, created, type });

  if (!verifySignature(signaturePayload, signature, webhookSecret)) {
    return res.status(400).send('Invalid signature');
  }

  switch (type) {
    case 'deployment.invoked':
      console.log('Deployment invoked:', req.body);
      break;
  }

  res.json({ received: true });
});

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});
By following these steps, you can effectively secure your webhooks and ensure that only authentic requests from orq.ai are processed.