Webhooks and event catalog

Gripz sends a signed HTTPS request to your endpoint when something changes. Delivery is at least once, so use the event ID to ignore repeats.

Events

{
  "id": "evt_01J9ZK4T7Q",
  "topic": "conversation.created",
  "api_version": "2026-09-01",
  "created_at": "2026-10-02T10:42:11Z",
  "workspace_id": "tw_8f2k",
  "ordering_key": "cnv_7Hq2Lm",
  "sequence": 1,
  "data": {
    "id": "cnv_7Hq2Lm",
    "object": "conversation",
    "state": "open",
    "channel": "chat",
    "subject": "Invoice PDF shows the wrong tax rate",
    "contact": { "id": "ct_9d3m" }
  }
}

Verify the signature

Every delivery has an ID, a timestamp and an HMAC-SHA256 signature over the ID, the timestamp and the raw body. Reject anything older than five minutes.

import crypto from 'node:crypto';

export function verifyGripzWebhook(req, secret) {
  const id = req.headers['gripz-webhook-id'];
  const timestamp = req.headers['gripz-webhook-timestamp'];
  const signature = req.headers['gripz-webhook-signature'];

  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (age > 300) return false; // older than five minutes

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${id}.${timestamp}.${req.rawBody}`)
    .digest('hex');

  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

Retry policy

Any 2xx answer within 10 seconds counts as delivered. Anything else is retried on this schedule for 72 hours.

Retry schedule
AttemptSent
1Immediately
21 minute after the first attempt
35 minutes
430 minutes
52 hours
66 hours
7 and laterEvery 6 hours, until 72 hours after the first attempt

After 72 hours of failures the subscription is paused and admins are notified. Undelivered events wait 14 days in the dead-letter queue so you can replay them. A 410 Gone answer disables the subscription at once.