# Guide to MICEtribe Webhooks: Features and Best Practices

MICEtribe webhooks tell your systems when something happens to a registration at your event: an attendee registers, updates their details, is deleted, checks in, or has their badge printed. MICEtribe handles event registration, badge printing and check-in, and webhooks let you sync attendees to a CRM, analytics or access control system as it happens.

This guide covers how MICEtribe webhooks are created and signed, the triggers and payload, the retry schedule, and the practices that keep a MICEtribe receiver reliable.

## What are MICEtribe webhooks?

A MICEtribe webhook belongs to one event. You create it through the Integrations management API, point it at your HTTPS URL, and enable one or more registration triggers. MICEtribe sends a `POST` only when the trigger is enabled on an active webhook for the same `event_id`.

Signing is optional. When you set a signing secret on the webhook, every delivery includes an `X-Signature` header: the hex HMAC-SHA256 of the `X-Timestamp` header, a `.`, and the raw body. Without a secret, the header isn't sent.

## MICEtribe webhook features

| Feature | Details |
| --- | --- |
| Configuration | Integrations management API (v1), per event |
| Base URL | `https://api.micetribe.com/integrations/v1` (staging: `https://api.staging.micetribe.com/integrations/v1`) |
| Subscription model | One or more triggers per webhook |
| Transport | HTTPS `POST`, `application/json` |
| Signature header | `X-Signature`, bare hex. Only when a signing secret is set |
| Timestamp header | `X-Timestamp`, Unix seconds |
| Scheme | HMAC-SHA256 |
| Signed content | `<X-Timestamp>.<raw body>` |
| Other headers | `X-Request-Id`, a UUID for each delivery attempt |
| Timeout | 10 seconds per attempt |
| Success | `2xx`. `3xx` and above, network errors and timeouts fail |
| Retries | Up to 5: after 15 minutes, 30 minutes, 3 hours, 6 hours and 12 hours |
| Source IPs | Not published |

## Common events

| Trigger | Fires when |
| --- | --- |
| `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 or registration is printed |

Every delivery has the same structure. Here is MICEtribe's example:

```json
{
  "registration_id": "6a08287fa219e11ac60d2a10",
  "data": {
    "registration_data": {
      "email": "attendee@example.com",
      "phone": "+97400000000",
      "fullname": "Jane Doe",
      "first_name": "Jane",
      "last_name": "Doe"
    },
    "entry_status": "Approved",
    "persona": "attendee",
    "barcode": "ABC123XY",
    "source": "online",
    "mirror": "",
    "print": { "print_count": 0, "last_printed_at": "", "last_printed_by": "" },
    "checkin": { "attended": false, "checkin_at": "" },
    "payment": { "total_payment": "10.00", "currency": "QAR", "method": "SkipCash" }
  },
  "meta": {
    "event_id": 7337,
    "event_name": "My Event",
    "event_slug": "my-event",
    "webhook_name": "crm-sync",
    "occurred_at": "2026-06-01T12:29:11+00:00",
    "created_at": "2026-05-18T09:27:25+00:00",
    "updated_at": "2026-06-01T08:37:39+00:00",
    "trigger": "registration.updated",
    "webhook_url": "https://your-server.com/webhooks/micetribe",
    "request_method": "post"
  }
}

```

Route on `meta.trigger`. `data.registration_data` holds the attendee's form answers, `data.entry_status` is a readable status such as `Approved`, `Declined`, `Pending Payment` or `Request Revision`, and `data.source` is `online`, `onsite`, `integrations` or `import`. The `print`, `checkin`, `payment`, `parent` and `product` blocks are left out when they're empty, so check for them before reading.

## Setting up MICEtribe webhooks

1. Get management API access for your workspace from the MICEtribe integrations team, which also handles webhook enablement per workspace or event.
2. Create a webhook for your event with the Integrations API: your HTTPS URL, `active: true`, at least one trigger, and a signing secret.
3. Store the same signing secret on your server, for example as `MICETRIBE_WEBHOOK_SECRET`. MICEtribe stores it encrypted.
4. Create or update a registration on a test event and confirm the request arrives with `X-Signature` and `X-Timestamp`.

**With Hookdeck.** MICEtribe is a supported source type in Hookdeck. MICEtribe signs webhooks only when the webhook has a signing secret, so set one on the webhook and paste it into the Hookdeck source. Use the Hookdeck source URL as the webhook URL. The source accepts `POST` only and checks `X-Signature` against `<X-Timestamp>.<raw body>`. A request that fails the check, including an unsigned one, still gets a `200` from Hookdeck, but Hookdeck marks it as failed verification and doesn't deliver it to your service.

## Securing MICEtribe webhooks

Join the `X-Timestamp` header, a literal `.`, and the raw body bytes. Compute an HMAC-SHA256 with the signing secret as a UTF-8 string and compare the hex digests in constant time.

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

function verifyMicetribeSignature(rawBody, signature, timestamp, secret) {
  if (!signature || !timestamp || !secret) return false; // unsigned deliveries fail
  if (!/^[0-9a-f]{64}$/i.test(signature)) return false; // validate before decoding

  const signed = Buffer.concat([Buffer.from(`${timestamp}.`, 'latin1'), rawBody]);
  const expected = crypto.createHmac('sha256', secret).update(signed).digest();
  const received = Buffer.from(signature, 'hex');
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

// Optional replay check, off by default (0). Hookdeck retries and replays resend the original timestamp.
const TOLERANCE_SECONDS = Number(process.env.MICETRIBE_WEBHOOK_TOLERANCE_SECONDS || 0);

const app = express();

app.post('/webhooks/micetribe', express.raw({ type: '*/*' }), (req, res) => {
  const secret = process.env.MICETRIBE_WEBHOOK_SECRET;
  if (!secret) return res.sendStatus(500); // fail closed

  const timestamp = req.get('x-timestamp');
  if (!verifyMicetribeSignature(req.body, req.get('x-signature'), timestamp, secret)) {
    return res.sendStatus(401);
  }
  if (TOLERANCE_SECONDS > 0 && Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  // Route on event.meta.trigger, then process asynchronously.
  res.sendStatus(200);
});

```

The same check in Python:

```python
import hashlib
import hmac
import re

HEX_64 = re.compile(r"[0-9a-fA-F]{64}")

def verify_micetribe_signature(raw_body: bytes, signature, timestamp, secret: str) -> bool:
    if not signature or not timestamp or not secret:
        return False
    if not HEX_64.fullmatch(signature):
        return False
    signed = 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)

```

MICEtribe's PHP sample does the same with `hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret)` and `hash_equals`. Its security recommendations list rejecting timestamps older than 5 minutes as optional. Hookdeck applies no timestamp tolerance, and its retries and replays resend the original headers, so leave the window off behind Hookdeck unless it's wider than your retry schedule.

> **Make MICEtribe webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) verifies the `X-Signature` header, stores every registration, check-in and badge print event, and delivers it to your services with retries you control.

## MICEtribe webhook limitations and pain points

### Unsigned unless you set a secret

**The Problem:** Requests arrive without `X-Signature`, and anyone who knows the URL could send the same thing.

**Why It Happens:** MICEtribe signs only when the webhook has a signing secret.

**Workarounds:**

* Always set a signing secret when you create a webhook, and reject requests that lack the header.

**How Hookdeck Can Help:** Hookdeck's MICEtribe source requires the signature, so unsigned requests are recorded as failed verification and never reach your service.

### Slow, sparse retries

**The Problem:** A failed delivery can take hours to come back, or not come back at all.

**Why It Happens:** Retries are picked up by a job that runs every 15 minutes, and the five retries are spread over about 22 hours. A response slower than 10 seconds counts as a failure.

**Workarounds:**

* Return `2xx` within a few seconds and queue heavy work, as MICEtribe recommends.
* After an outage, re-sync registrations for the event from the API.

**How Hookdeck Can Help:** Hookdeck answers MICEtribe straight away and stores each delivery, then delivers to your service with [automatic retries](/docs/retries) on a schedule you set.

### Deduplication needs care

**The Problem:** Retries repeat the same payload, but there's no single event ID.

**Why It Happens:** MICEtribe suggests deduplicating on `registration_id`, `meta.trigger` and `X-Request-Id`, but it also describes `X-Request-Id` as unique for each delivery attempt. If that's the case, a retry carries a new value and that key won't catch it.

**Workarounds:**

* Build a key from `registration_id`, `meta.trigger` and `meta.occurred_at`, which describes the underlying event rather than the attempt.

**How Hookdeck Can Help:** [Deduplication](/docs/deduplication) can drop repeated requests based on the body fields you choose.

## Best practices

### Verify against the raw body

MICEtribe signs the "exact JSON bytes in the POST body (no re-encoding)". Use `express.raw()` in Express, `await request.arrayBuffer()` in a Next.js route handler, and `await request.body()` in FastAPI.

### Handle missing blocks

Optional blocks are omitted when empty, so a `registration.updated` event may have no `checkin` or `payment`. Use optional access rather than assuming a fixed shape.

### Keep attendee data safe

Payloads carry names, emails, phone numbers and form answers. Store only what you need, and never log the signing secret.

### Acknowledge fast and process asynchronously

Verify, store the event, return `200`, then do the work. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

## Conclusion

MICEtribe signs webhooks with `X-Signature`, a hex HMAC-SHA256 of `<X-Timestamp>.<raw body>`, but only when the webhook has a signing secret. Always set one, verify against the raw bytes, route on `meta.trigger`, respond within 10 seconds, and deduplicate on fields that describe the event rather than the attempt.

[Hookdeck Event Gateway](https://hookdeck.com) sits in front of your receiver, checks every MICEtribe signature, and holds each registration event until your service has processed it.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle MICEtribe webhooks reliably in minutes.