# Egnyte Webhooks

Egnyte (file collaboration) pushes real-time events from your domain to an HTTPS endpoint you
register with the Webhooks API. Payloads are **not signed**. The only authentication is the
`authHeader` value you choose at registration, which Egnyte sends back as the `Authorization`
header on every delivery.

## When to Use This Skill

* How do I receive Egnyte webhooks?
* How do I verify the Egnyte `authHeader` / `Authorization` header on a webhook?
* Does Egnyte sign webhook payloads? (No: shared credential header only)
* Why does my Egnyte webhook endpoint return 401, or why was my webhook disabled?
* How do I handle `fs:add_file`, `fs:delete_file`, `fs:move_file` or `link:create` events?

## Verification (core)

Set `authHeader` when you register the webhook (it is optional in the API, but without it
Egnyte sends nothing you can check). Egnyte then sends it as the `Authorization` header. The
key is the **whole header value exactly as you set it**, including any `Bearer ` prefix. Don't
strip, parse or base64-decode it:

```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 verifyEgnyteRequest(headers, expected = process.env.EGNYTE_AUTHORIZATION) {
  if (!expected) return false;                // never accept when unconfigured
  const received = headers['authorization'];  // full value, e.g. "Bearer secret-token-12345"
  return Boolean(received) && safeEqual(received, expected);
}

```

Return `401` when this fails, `400` for a body that isn't `{"data": [...]}`, and exactly `200`
once handled. Read the body raw (`express.raw({ type: '*/*' })`) and parse it only after the
check. Python and Next.js equivalents are in [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/egnyte-webhooks/references/verification.md).

> **A static shared credential authenticates the sender, not the body, and gives no replay
> protection** (there is no signature and no timestamp to check). Use HTTPS only, and rotate
> the `authHeader` (update the webhook) if it leaks.

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

## Hookdeck Setup Note

In Hookdeck, create an **Egnyte** source (source type `EGNYTE`) and paste the credential as
the source's key:

> Paste the authHeader value exactly as set on the Egnyte webhook, including any "Bearer " prefix.

Hookdeck compares the whole `Authorization` header to that value, the same as the examples here.

## Events and Payload

Egnyte POSTs `application/json` with an object holding a `data` array. One request can carry
several events; loop over `data[]` and dispatch on `eventType`:

| `eventType` | Fires when |
| --- | --- |
| `fs:add_file` | A file or file version is added |
| `fs:delete_file` | A file is deleted |
| `fs:move_file` | A file is moved or renamed |
| `fs:add_folder` | A folder is created |
| `link:create` | A link is created |
| `comment:set_comment` | A comment is set on a file |
| `permission:permission_change` | A folder permission changes |
| `workflow:completed` | A workflow finishes |
| `group:add_members` | Members are added to a group |

Each item has `eventId` (unique per domain: your dedupe key), `domain`, `timestamp` (epoch
milliseconds), `user`, `actionSource`, `eventType`, `webhookId` and an event-specific `data`
object. See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/egnyte-webhooks/references/overview.md) for the full list.

## Response and Retries

Respond with **exactly HTTP 200** (not 202 or 204). Any other status is retried immediately,
then after 1 minute, 10 minutes and 1 hour (4 attempts). After that Egnyte **disables the
webhook registration**, so don't return `4xx`/`5xx` for events you merely don't handle.

## Environment Variables

```bash
EGNYTE_AUTHORIZATION=Bearer secret-token-12345   # full authHeader value, prefix included

```

## Local Development

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

```

No account required. Register the printed URL as the webhook `url` (Egnyte requires HTTPS with
a CA-signed certificate).

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/egnyte-webhooks/references/overview.md) - Payload shape, event types, retries
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/egnyte-webhooks/references/setup.md) - Registering a webhook with `authHeader`
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/egnyte-webhooks/references/verification.md) - Header check in Node, Python and Next.js, gotchas
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/egnyte-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests