Channel API guide
Events and webhooks
Every change that concerns you is an event. Riverdesk sends each one to your webhook endpoint, and you can also read them all from the event feed.
Webhooks
The company registers your endpoint and gives you its signing secret. Each event is a POST with:
X-VS-Event-Id: <uuid>X-VS-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>
Answer 2xx within 10 seconds. A failed delivery is retried with growing pauses for 24 hours. The company can see every attempt and send one again.
Verify every delivery before trusting it:
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(
secret: string,
rawBody: string,
header: string,
now = Math.floor(Date.now() / 1000),
) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=', 2)));
const t = Number(parts.t);
if (!Number.isInteger(t) || Math.abs(now - t) > 300) return false; // older than 5 minutes: reject
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
const given = Buffer.from(parts.v1 ?? '', 'hex');
return given.length === expected.length && timingSafeEqual(given, expected);
}
Order and duplicates
Events about one resource (one booking, one hold) arrive in order: a later one waits until the earlier is delivered. Delivery is at least once, so you may see an event twice. Keep the highest resource_version you applied per resource_id, and ignore anything not newer.
Event types
| Event | Means |
|---|---|
availability.changed |
Free cabins changed on a leg and category. Re-read availability for those dates |
sailing.changed |
A departure was cancelled, moved or changed port |
rate.changed |
Prices of a plan you sell changed. Existing quotes and bookings keep theirs |
hold.expired |
One of your holds ran out |
booking.confirmed, booking.amended, booking.cancelled |
Your booking changed. Read it for details |
booking.paid_unavailable |
Paid after its hold expired and the cabins were gone. The company will offer another cabin or refund |
request.decided |
A decision on your request (see Requests and offers) |
offer.created |
An alternative was offered for your request |
payment.recorded, payment.overdue |
Money received, or a balance overdue, on your booking |
Catching up: the event feed
After downtime, read the feed from your last cursor:
curl -s "$API/v1/events?limit=500" -H "$H"
curl -s "$API/v1/events?after=$NEXT_CURSOR&limit=500" -H "$H"
Store next_cursor after each page. An empty page means you are up to date. The feed returns events only once they are fully committed, in commit order, so a cursor never skips one.