Skip to content

Concepts

Webhooks

STRYK sends an HTTPS POST to your endpoint when bookings, guests, availability, rates or payments change. v1 has no invoice event; reconcile GET /invoices with updated_since. Each delivery is signed, retried on failure and visible in the delivery log.

Set up an endpoint

  1. Create the endpoint

    In STRYK, open Channels → API, tab Webhooks, choose Create webhook, enter an HTTPS URL and choose the events.

  2. Store the signing secret

    STRYK shows the secret (whsec_…) once. Keep it on your server, like an API key.

  3. Send a test event

    Use Send test event. STRYK delivers a webhook.test event so you can check your signature verification.

The URL must use HTTPS and reach a public address. STRYK does not follow redirects and rejects private or local targets, both when you save the endpoint and on every delivery.

Events

EventSent when
booking.createdA new booking exists in STRYK.
booking.updatedA booking changed: status, dates, room, guest assignment, notes. data.changes names the areas (status, rooms, guest, details, deleted).
booking.cancelledA booking was cancelled.
guest.updatedGuest contact data changed.
availability.changedFree units or restrictions changed for room types and a date range. Changes are combined per room type and period.
rate.changedDaily prices changed for room types and a date range, combined like availability.
payment.updatedThe paid amount of a booking changed; data.bookingId names the booking.
webhook.testYou requested a test delivery in STRYK.
unit.updatedunit.updated

GET /webhook-event-types returns the current list with descriptions.

Payload

Events are thin: they contain the type, the affected object and its ID, but not the full object. Fetch the current state from the API. This keeps personal data out of logs and queues, and you never act on a stale copy.

{
  "id": "evt_1f6c9e2a-3b4d-4e5f-8a7b-6c5d4e3f2a1b",
  "type": "booking.updated",
  "apiVersion": "v1",
  "createdAt": "2026-11-04T08:20:31.000Z",
  "hotelId": "hotel-alpenblick",
  "data": { "object": "booking", "id": "9d3c1b2a-7e6f-4a5b-8c9d-0e1f2a3b4c5d", "changes": ["status"] }
}

Headers

HTTP
POST /webhooks/stryk HTTP/1.1
Content-Type: application/json
User-Agent: STRYK-Webhooks/1.0
STRYK-Event-Id: evt_1f6c9e2a-3b4d-4e5f-8a7b-6c5d4e3f2a1b
STRYK-Event-Type: booking.updated
STRYK-Delivery-Id: 2b1a0f9e-8d7c-4b6a-a594-837261504f3e
STRYK-Delivery-Attempt: 1
STRYK-Signature: t=1793780431,v1=5f2c8a1e9b0d…
HeaderContent
STRYK-Event-IdEvent ID, identical to id in the body. The same event keeps its ID on every attempt.
STRYK-Event-TypeEvent type, e.g. booking.updated.
STRYK-Delivery-IdID of this delivery to this endpoint; shown in the delivery log.
STRYK-Delivery-AttemptAttempt number, starting at 1.
STRYK-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256>; during a secret rotation two v1 values.

Verifying the signature

STRYK computes HMAC-SHA256(secret, "<t>.<rawBody>") with your signing secret as the key, the full value as shown, including whsec_, as UTF-8 bytes, and sends it hex-encoded as v1. Verify every request before you use it:

  1. Read the raw body

    Use the exact bytes you received. Parsing and re-serialising JSON changes spacing and order, and the signature no longer matches.

  2. Check the timestamp

    Reject requests whose t is more than 300 seconds away from your clock. This blocks replays of recorded requests.

  3. Compare in constant time

    Compute the HMAC over t, a dot and the raw body, and compare it with each v1 value using a constant-time comparison.

  4. Accept one match

    After you rotate the secret, STRYK signs with the old and new secret for 24 hours. The header then has two v1 values; one valid match is enough.

webhook.js
import crypto from 'node:crypto';
import express from 'express';

export function verifyStrykSignature(rawBody, header, secret, toleranceSeconds = 300) {
  if (!header) return false;
  let timestamp = null;
  const signatures = [];
  for (const part of header.split(',')) {
    const [key, value] = part.trim().split('=', 2);
    if (key === 't') timestamp = Number(value);
    if (key === 'v1' && value) signatures.push(value);
  }
  if (!Number.isInteger(timestamp) || signatures.length === 0) return false;
  if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > toleranceSeconds) return false;

  const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest();
  // During a secret rotation the header carries two v1 values; one match is enough.
  return signatures.some((signature) => {
    const received = Buffer.from(signature, 'hex');
    return received.length === expected.length && crypto.timingSafeEqual(received, expected);
  });
}

const app = express();

// express.raw keeps the exact bytes. Parsing JSON first would break the signature.
app.post('/webhooks/stryk', express.raw({ type: 'application/json', limit: '1mb' }), (req, res) => {
  if (!verifyStrykSignature(req.body, req.get('STRYK-Signature'), process.env.STRYK_WEBHOOK_SECRET)) {
    return res.status(400).send('Invalid signature');
  }
  const event = JSON.parse(req.body.toString('utf8'));
  res.sendStatus(204);   // acknowledge within seconds …
  queue.add(event);      // … and process asynchronously, deduplicated by event.id
});

Responding and processing

  • Answer with any 2xx status within 10 seconds. STRYK discards the response body.
  • Acknowledge first, then process in a queue or background job. Slow handlers cause timeouts and duplicate deliveries.
  • Make processing idempotent by event id: store processed IDs and skip repeats.
  • Do not rely on order. Compare updatedAt of the fetched object with what you have.

Retries and automatic disabling

A delivery fails on a timeout, a network error or any non-2xx status. STRYK retries with increasing delays:

AttemptDelay after the previous attempt
1immediately
21 minute
35 minutes
430 minutes
52 hours
66 hours
712 hours
824 hours

After the eighth failed attempt the delivery is marked as failed for good. If an endpoint fails 20 times in a row over at least 24 hours without a single success, STRYK disables it automatically. Fix the endpoint, then enable it again in STRYK.

Delivery log and manual retry

In Channels → API → Webhooks, the Deliveries section of a webhook shows each delivery with time, event, status, attempts, HTTP status, next attempt and delivery time. Send again retries a single delivery, for example after a fix. Rotate secret rotates the signing secret in the same place.