# Ardoq Webhooks

Webhooks from **Ardoq** (ardoq.com), the enterprise-architecture platform. An organization-wide webhook receives every change across the organization. This skill covers **signed** webhooks, which deliver component, reference and tag changes.

## When to Use This Skill

* How do I receive Ardoq webhooks?
* How do I verify the `X-Ardoq-Signature-256` header?
* Why is my Ardoq webhook signature verification failing?
* How do I handle Ardoq `component`, `reference` and `tag` events?
* Why did my Ardoq webhook stop sending report or dashboard events?

## Setup Note

Keep signing on for the Ardoq webhook (it is on by default) and paste its secret, which Ardoq shows only once. Ardoq enables webhooks per organization.

## Verification (core)

Ardoq signs each request with two headers:

```http
X-Ardoq-Signature-256: sha256=<hex digest>
X-Ardoq-Timestamp: <unix epoch seconds>

```

The digest is `HMAC-SHA256(secret, "<timestamp>.<raw body>")`, hex-encoded. The key is the secret's UTF-8 bytes, used as-is. Build the signed message as **bytes**: the timestamp header exactly as received, a literal `.`, then the raw body. Never decode and re-encode the body, and never `JSON.parse` / `JSON.stringify` before verifying.

```javascript
const crypto = require('crypto');

function verifyArdoqSignature(rawBody /* Buffer */, signatureHeader, timestampHeader, secret) {
  if (!secret || !signatureHeader || !timestampHeader) return false;

  // Accept the value with or without "sha256="; validate format BEFORE decoding
  const digest = signatureHeader.replace(/^sha256=/, '');
  if (!/^[0-9a-fA-F]{64}$/.test(digest)) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(Buffer.concat([Buffer.from(timestampHeader, 'latin1'), Buffer.from('.'), rawBody]))
    .digest();
  const provided = Buffer.from(digest.toLowerCase(), 'hex'); // hex is case-insensitive
  return provided.length === expected.length && crypto.timingSafeEqual(provided, expected);
}

```

Missing or empty signature or timestamp header, malformed value, or mismatch: respond **401**.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/ardoq-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/ardoq-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/ardoq-webhooks/examples/fastapi/).

There is **no official Ardoq SDK** for webhook verification. Ardoq's help article shows Python, Node.js and Java snippets; the examples here follow the same algorithm.

### Timestamp tolerance is opt-in

Ardoq's own snippets reject deliveries more than 5 minutes old. This skill does **not** by default: Hookdeck retries and replays resend the original headers and timestamp, and Hookdeck applies no tolerance itself, so a fixed window would reject legitimate retries. Set `ARDOQ_WEBHOOK_TOLERANCE_SECONDS` to opt in (`X-Ardoq-Timestamp` is Unix seconds; unset or `0` = off). Because the timestamp is signed, it cannot be swapped for a fresh one.

## Event Types

The body is `{resource-type, resource-id, event-type, organization, organization-label, data}`. Dispatch on `resource-type`, then `event-type`.

| `resource-type` | Triggered When | Notes |
| --- | --- | --- |
| `component` | A component changes | Signed webhooks only |
| `reference` | A reference (link between components) changes | Signed webhooks only |
| `tag` | A tag changes, e.g. one is added to a component | Signed webhooks only; `data.components` lists tagged components |

Ardoq's docs show `event-type: "create"` in the sample payload and do not list the other values, so log unknown ones instead of guessing. Signed webhooks do **not** deliver field, report, dashboard or survey changes (only unsigned webhooks do, and this skill does not cover them), and there are **no deletion events**.

## Important Details

* **Method:** `POST`. Hookdeck's Ardoq source manages the method as POST.
* **Scope:** the endpoint receives every change in the organization, unfiltered by workspace or permissions, including confidential fields. Only use an endpoint you trust.
* **HTTPS required** for signed webhooks; Ardoq verifies the endpoint's TLS certificate.
* **Signing is one-way.** To stop signing, delete the webhook and create an unsigned one.
* **Secret rotation:** "Rotate signing secret" invalidates the old secret immediately.
* **Retries:** Ardoq documents no retry or timeout policy. Respond quickly and process asynchronously.
* **No delivery ID header.** Build an idempotency key yourself, e.g. `resource-id` + `event-type` + `data._version` where present.

## Environment Variables

```bash
ARDOQ_WEBHOOK_SECRET=your_signing_secret      # shown once, when the webhook is created
ARDOQ_WEBHOOK_TOLERANCE_SECONDS=0             # optional; unset or 0 = no timestamp window (default)

```

## Local Development

```bash
# Forward Ardoq webhooks to your local server (no account required)
npx hookdeck-cli listen 3000 ardoq --path /webhooks/ardoq

```

Paste the resulting HTTPS URL as the destination URL when creating the webhook in Ardoq (**Preferences → Organization settings → Settings → Webhooks → Create Webhook**). Test by adding and removing a tag on a component.

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/ardoq-webhooks/references/overview.md) - Ardoq webhook concepts, resource types, payload fields
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/ardoq-webhooks/references/setup.md) - Create a signed webhook in Ardoq and get the secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/ardoq-webhooks/references/verification.md) - `X-Ardoq-Signature-256` verification details and gotchas