# Proxyclick Webhooks

Proxyclick (now **Eptura Visitor**, visitor management) POSTs a JSON notification to a URL you
choose when a visitor or visit changes. Payloads are **not signed**. The only security option
is HTTP Basic authentication (a username and password you enter on the webhook).

## When to Use This Skill

* How do I receive Proxyclick (Eptura Visitor) webhooks?
* How do I verify the Proxyclick Basic auth credentials on a webhook?
* Does Proxyclick sign webhook payloads? (No: Basic auth only)
* Why does my Proxyclick webhook endpoint return 401?
* How do I handle visitor check-in, check-out and visit created, updated or deleted events?

## Verification (core)

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

const sha = (s) => crypto.createHash('sha256').update(String(s)).digest();
const safeEqual = (a, b) => crypto.timingSafeEqual(sha(a), sha(b)); // equal-length digests

function verifyProxyclickRequest(headers, env = process.env) {
  const m = /^basic +([A-Za-z0-9+/]+={0,2}) *$/i.exec(headers['authorization'] || '');
  if (!m || !env.PROXYCLICK_WEBHOOK_USERNAME || !env.PROXYCLICK_WEBHOOK_PASSWORD) return false;
  const decoded = Buffer.from(m[1], 'base64').toString('utf8');
  const i = decoded.indexOf(':');                 // split on the FIRST colon
  if (i < 0) return false;
  const userOk = safeEqual(decoded.slice(0, i), env.PROXYCLICK_WEBHOOK_USERNAME);
  const passOk = safeEqual(decoded.slice(i + 1), env.PROXYCLICK_WEBHOOK_PASSWORD);
  return userOk && passOk;                        // both evaluated, no short-circuit
}

```

Return `401` when this fails (missing or malformed header, wrong username or password), `400`
for a body that isn't a JSON object, and `200` once handled. Read the body raw
(`express.raw({ type: '*/*' })`) and parse JSON only after the credential check.

**Security notes**

* A static shared credential authenticates the **sender, not the body**, and gives **no replay
  protection** (no signature, no timestamp). Use an HTTPS destination URL (Proxyclick also allows
  plain HTTP) and rotate the credential if it leaks.
* The Proxyclick docs don't say whether Basic credentials are sent preemptively, so the
  credentials must arrive on the **first** request. A `401` + `WWW-Authenticate` challenge flow
  won't work, and it won't work through Hookdeck either, which verifies the request as received.
* Compare in constant time. In Python use `hmac.compare_digest` on **bytes** (encode both
  sides); passing `str` raises on non-ASCII input.

Python and Next.js equivalents are in [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/proxyclick-webhooks/references/verification.md).

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

## Webhook Triggers

Proxyclick's docs name six trigger events, only as labels in the webhook settings UI. They
document **no event-type field** in the payload, and the
[attribute reference](https://api.proxyclick.com/v1/docs/) shows API objects rather than a webhook
body, so the examples dispatch on nothing. Their handlers use a clearly labelled **illustrative**
body.

| Trigger (UI label) | Fires when |
| --- | --- |
| Visitor on-site | A visitor is on site |
| Visitor check-in | A visitor checks in |
| Visitor check-out | A visitor checks out |
| Visit created | A visit is created |
| Visit updated | A visit is updated |
| Visit deleted | A visit is deleted |

Proxyclick only guarantees backward compatibility for attributes in its public documentation.
Log a real delivery before you rely on any key.

## Environment Variables

```bash
PROXYCLICK_WEBHOOK_USERNAME=proxyclick
PROXYCLICK_WEBHOOK_PASSWORD=replace-with-a-long-random-password

```

## Local Development

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

```

No account required. Paste the printed URL into the webhook's destination URL in Proxyclick,
and enter the same username and password there.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/proxyclick-webhooks/references/overview.md) - The six triggers, payload caveats, delivery
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/proxyclick-webhooks/references/setup.md) - Add a webhook with Basic auth in Eptura Visitor
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/proxyclick-webhooks/references/verification.md) - Basic auth check in Node, Python and Next.js, gotchas
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/proxyclick-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests