# Currents Webhooks

Currents (currents.dev) reports Playwright test runs. Its **HTTP Webhook**
integration sends a JSON `POST` when a run starts, finishes, times out or is canceled.
Webhooks are **not signed**. You authenticate them with a secret header that you add to the
integration's **Headers** field; your endpoint compares that header with the value you stored.

The header name is your choice, so the examples read it from `CURRENTS_WEBHOOK_HEADER`
(default `Authorization`) and the expected value from `CURRENTS_WEBHOOK_SECRET`.

## When to Use This Skill

* How do I receive Currents webhooks?
* How do I authenticate a Currents HTTP webhook? (No signature: use a secret custom header)
* How do I handle `RUN_START`, `RUN_FINISH`, `RUN_TIMEOUT` or `RUN_CANCELED` events?
* Why does my Currents webhook endpoint return 401?
* What is the `x-timestamp` header on Currents webhooks?

## 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 verifyCurrentsRequest(
  headers,
  secret = process.env.CURRENTS_WEBHOOK_SECRET,
  headerName = process.env.CURRENTS_WEBHOOK_HEADER || 'Authorization'
) {
  if (!secret) return false;                          // never accept when unconfigured
  const received = headers[headerName.toLowerCase()]; // Node lowercases header names
  return Boolean(received) && safeEqual(received, secret); // WHOLE value, no prefix parsing
}

```

* Compare the **whole** header value. If you typed `Bearer abc123` into Currents, then
  `CURRENTS_WEBHOOK_SECRET` is `Bearer abc123`. Don't strip or parse the prefix.
* Look the header up case-insensitively. Return `401` on a missing header or a mismatch.
* Read the body raw (`express.raw({ type: '*/*' })`) so any content type works, and parse the
  JSON only after the check passes.
* **A static shared credential authenticates the sender, not the body, and gives no replay
  protection** (no signature, and the only timestamp is unsigned). Use HTTPS and rotate the
  credential if it leaks.
* Currents also sends an `x-timestamp` header with the system's epoch timestamp, which its docs
  offer to help you prevent replay attacks. It is not signed, so anyone holding the secret header
  can set a fresh value: treat it as a freshness hint at most, and handle events idempotently.

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

## Event Types

The payload's `event` field is one of these (exact strings, upper case):

| Event | Sent When |
| --- | --- |
| `RUN_START` | A new run starts |
| `RUN_FINISH` | A run finishes |
| `RUN_TIMEOUT` | A run times out |
| `RUN_CANCELED` | A run is canceled |

Runs with several groups (multiple Playwright projects under one `ci-build-id`) send one
request per group, each with its own `groupId`.

## Environment Variables

```bash
CURRENTS_WEBHOOK_HEADER=Authorization       # header name you added in the integration's Headers
CURRENTS_WEBHOOK_SECRET=your-secret-value   # exact, whole header value

```

## Local Development

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

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

```

Use the printed URL as the webhook URL in Currents.

## Resources

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