# Auvik Webhooks

Auvik's Webhook integration posts a JSON alert notification to your URL when a network
monitoring alert is triggered or cleared. Requests are **not signed**. Choose **Header
authentication** and Auvik sends a custom HTTP header (a Key and a Value that you pick) on every
request; your endpoint authenticates the sender by comparing that header with the value you
stored.

> **Choose Header authentication on the Auvik webhook, not OAuth.** Basic Authentication and
> OAuth 2.0 are not covered by this skill or by Hookdeck's Auvik source.

## When to Use This Skill

* How do I receive Auvik webhooks?
* How do I authenticate an Auvik webhook with a custom header?
* Does Auvik sign webhook payloads? (No: use the shared header value)
* How do I handle `Triggered` and `Cleared` Auvik alerts?
* Why does my Auvik webhook endpoint return 401?

## Verification (core)

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

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyAuvikRequest(headers,
    name = process.env.AUVIK_WEBHOOK_HEADER || 'Authorization',
    secret = process.env.AUVIK_WEBHOOK_SECRET) {
  if (!secret) return false;                  // never accept when unconfigured
  const received = headers[name.toLowerCase()]; // Node lowercases header names
  return typeof received === 'string' && received !== '' && safeEqual(received, secret);
}

```

* The header **name** is whatever Key you typed in Auvik, so read it from `AUVIK_WEBHOOK_HEADER`
  (default `Authorization`) and look it up case-insensitively.
* Compare the **whole** header value with `AUVIK_WEBHOOK_SECRET`, including any `Bearer ` prefix
  you typed in Auvik. Do not strip or parse a prefix.
* Missing header or mismatch: return `401`. Invalid JSON or a non-object body: `400`. Handled: `200`.
* Read the body raw (`express.raw({ type: '*/*' })`) so any content type works, and parse it only
  after the credential check.
* Python: encode both sides to bytes before `hmac.compare_digest` (a non-ASCII `str` raises).

> **A static shared credential authenticates the sender, not the body.** There is no signature
> and no timestamp, so it gives no replay protection and no integrity check. Use HTTPS, and
> rotate the credential (change it in Auvik and in `AUVIK_WEBHOOK_SECRET`) if it leaks.

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

## Alert Payload

The default body is a JSON object; dispatch on `alertStatusString`.

| Field | Meaning |
| --- | --- |
| `alertId` | Alert identifier. Differs between a trigger and its clear in Auvik's examples |
| `correlationId` | Identifier of the alert trigger, shared by a trigger and its clear in Auvik's examples |
| `alertName` | Name of the alert |
| `alertStatus` | Numeric status: `0` Triggered, `1` Cleared |
| `alertStatusString` | `Triggered` or `Cleared` |
| `alertDescription` | Description of the alert |
| `entityName` | Asset that triggered the alert |
| `companyName` | Site the asset is located in |
| `date` | When the occurrence was detected, in UTC |
| `link` | Link to the alert in Auvik |

More fields may be present. Test Connection sends `{"my-api-test-field":"my-api-test-value"}`, so return `200` for a body with no alert fields. An optional JSONata expression on the integration can reshape the
body, in which case these field names will differ.

| Event (`alertStatusString`) | Sent When |
| --- | --- |
| `Triggered` | An alert fires |
| `Cleared` | An alert clears |

## Environment Variables

```bash
AUVIK_WEBHOOK_HEADER=Authorization       # the Key you entered in Auvik (default Authorization)
AUVIK_WEBHOOK_SECRET=your-header-value   # the exact, whole Value you entered in Auvik

```

## Local Development

Auvik can't reach `localhost`. Tunnel with the Hookdeck CLI (no account required):

```bash
npx hookdeck-cli listen 3000 auvik --path /webhooks/auvik

```

Use the printed URL as the webhook URL in Auvik.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/auvik-webhooks/references/overview.md) - What Auvik webhooks are and the alert payload
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/auvik-webhooks/references/setup.md) - Add the Webhook integration with Header authentication
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/auvik-webhooks/references/verification.md) - Header check details, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/auvik-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests