Skip to main content
Webhooks push booking lifecycle events to your server so you don’t have to poll. Register an HTTPS endpoint and Jinko sends a signed POST the moment a booking is confirmed or fails. The one-line version:
register endpoint → Jinko POSTs a signed event → you verify X-Jinko-Signature → you act

Prerequisites

  • A Jinko account and an API key (jnk_...). Get one.
  • An HTTPS endpoint that can receive a POST (must be https:// and publicly reachable).

1) Register an endpoint

Go to Dashboard → Webhooks, click Add webhook, paste your URL, pick the events, and Create. Copy the signing secret shown once — you’ll need it to verify deliveries.

2) Events

EventWhen it fires
booking.completedThe booking is fulfilled and paid (payment captured).
booking.failedThe booking could not be fulfilled.
More event types will be added over time — treat the event field as an open enum and ignore events you don’t handle.

3) Payload

Deliveries are intentionally thin — identifiers only, no traveler PII. Fetch full detail with get_booking using the booking_ref.
{
  "event": "booking.completed",
  "booking_ref": "JNK-A7B3X9",
  "status": "confirmed",
  "occurred_at": "2026-06-02T08:00:00Z",
  "event_id": "evt_01HX…",
  "livemode": true
}
Each request also carries these headers:
HeaderPurpose
X-Jinko-Signaturesha256=<hex> HMAC of the request (see below).
X-Jinko-TimestampUnix seconds; part of the signed payload (replay protection).
X-Jinko-Event-IdStable id for the business event — use it to dedupe.
X-Jinko-Delivery-IdId of this specific delivery attempt.

4) Verify the signature

Compute HMAC-SHA256(secret, "<X-Jinko-Timestamp>.<raw request body>") and compare it, in constant time, to the hex in X-Jinko-Signature (after the sha256= prefix). Use the raw request body — parsing and re-serializing the JSON will change the bytes and break the check.
import crypto from 'node:crypto';

// rawBody: the exact bytes Jinko sent (Buffer/string), NOT re-serialized JSON.
function verifyJinkoWebhook(rawBody, headers, secret) {
  const ts = headers['x-jinko-timestamp'];
  const sig = headers['x-jinko-signature']; // "sha256=<hex>"
  const expected =
    'sha256=' +
    crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}
Reject the request if the signature doesn’t match, or if X-Jinko-Timestamp is older than your tolerance (e.g. 5 minutes) to guard against replays. Respond 2xx once you’ve accepted the event.

5) Retries & idempotency

  • A non-2xx response (or a timeout) is retried with exponential backoff — up to 8 attempts over several hours.
  • Retries mean you may receive the same event more than once. Deduplicate on X-Jinko-Event-Id (a given business event always carries the same id).
  • Return 2xx as soon as you’ve durably recorded the event; do slow work asynchronously so you don’t trip the delivery timeout.

6) Test it

Use Send test in the dashboard, or:
curl -X POST https://api.gojinko.com/v1/webhooks/42/test \
  -H "X-API-Key: $JINKO_API_KEY"
This delivers a sample event with "livemode": false and booking_ref: "JNK-TEST00", so you can confirm your signature handling end-to-end without a real booking.