# MICEtribe Webhooks

## When to Use This Skill

* How do I receive MICEtribe webhooks?
* How do I verify the MICEtribe `X-Signature` header?
* Why is my MICEtribe webhook signature verification failing?
* How do I handle `registration.created` or `registration.attended` events?
* How do I dedupe MICEtribe webhook deliveries?

## Setup note

> MICEtribe signs webhooks only when the webhook has a signing secret. Set one on the webhook and paste it here.

Without a signing secret, deliveries carry no `X-Signature` header and there is nothing to verify.

## Verification (core)

`X-Signature` is the bare lowercase **hex** HMAC-SHA256 (no prefix) of `X-Timestamp` + `"."` + the **exact raw request body**, keyed with the webhook's signing secret as UTF-8 bytes. Build the signed message as bytes (the `X-Timestamp` string as latin1, a literal `.`, the body bytes untouched), validate the signature is exactly 64 hex characters, then compare in constant time. Missing/empty headers, a malformed value or a mismatch is a **401**.

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

function verify(rawBody, signature, timestamp, secret) { // rawBody: Buffer
  if (!signature || !timestamp || !/^[0-9a-fA-F]{64}$/.test(signature)) return false;
  const signed = Buffer.concat([Buffer.from(timestamp, 'latin1'), Buffer.from('.'), rawBody]);
  const expected = crypto.createHmac('sha256', Buffer.from(secret, 'utf8')).update(signed).digest();
  const received = Buffer.from(signature.toLowerCase(), 'hex');
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

```

```python
import hmac, hashlib, re

def verify(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
    if not signature or not timestamp or not re.fullmatch(r"[0-9a-fA-F]{64}", signature):
        return False
    signed = b"".join([timestamp.encode("latin-1"), b".", raw_body])
    expected = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).digest()
    return hmac.compare_digest(bytes.fromhex(signature), expected)

```

**Timestamp window (opt-in):** `X-Timestamp` is Unix seconds. MICEtribe lists rejecting old timestamps as optional. The examples do **not** reject old timestamps by default, because Hookdeck retries and replays resend the original headers and timestamp, and Hookdeck itself applies no tolerance. Set `MICETRIBE_WEBHOOK_TOLERANCE_SECONDS` (unset or `0` = off) to opt in.

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

MICEtribe has no webhook verification SDK, so verification is manual. Read the body as raw bytes (`express.raw({ type: '*/*' })`, `await request.arrayBuffer()`, `await request.body()`) and parse JSON only after the check. See [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/micetribe-webhooks/references/verification.md).

## Common Event Types

Dispatch on the body's `meta.trigger` field.

| `meta.trigger` | Description |
| --- | --- |
| `registration.created` | A new registration is created |
| `registration.updated` | An existing registration is updated |
| `registration.deleted` | A registration is deleted |
| `registration.attended` | Attendance is recorded |
| `registration.printed` | A badge/registration is printed |

> **For the full reference**, see [MICEtribe's webhook documentation](https://developer.micetribe.com/webhooks-10150906f0).

## Important Headers

| Header | Description |
| --- | --- |
| `X-Signature` | Hex HMAC-SHA256 of `X-Timestamp` + `.` + raw body (only sent when a signing secret is set) |
| `X-Timestamp` | Unix timestamp in seconds used when signing |
| `X-Request-Id` | Unique UUID for this delivery attempt |

## Delivery and Idempotency

The request is a `POST` with a JSON body. A `2xx` response is success; `3xx` and above, network errors and timeouts (10 seconds) fail. MICEtribe retries up to 5 times, 15 min, 30 min, 3 h, 6 h and 12 h apart (a dispatcher runs every 15 minutes). Process idempotently on `registration_id` + `meta.trigger` + `X-Request-Id`.

## Environment Variables

```bash
MICETRIBE_WEBHOOK_SECRET=your_webhook_signing_secret   # The signing secret set on the webhook (used as UTF-8)
MICETRIBE_WEBHOOK_TOLERANCE_SECONDS=0                  # Optional; a positive value turns on the X-Timestamp check

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 micetribe --path /webhooks/micetribe

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/micetribe-webhooks/references/overview.md) - MICEtribe webhook concepts, events, payload shape
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/micetribe-webhooks/references/setup.md) - Creating a webhook and setting the signing secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/micetribe-webhooks/references/verification.md) - Signature verification details and gotchas