এই ভলিউমে · ভলিউম 07
ইন্টিগ্রেশন
GDS (Amadeus, Sabre) NDC পাবলিক API ওয়েবহুক
বাংলা সারসংক্ষেপ

ইভেন্ট ফরম্যাট: .। সিগনেচার: HMAC-SHA256 হেডার। 5-মিনিট replay উইন্ডো। ডেলিভারি: at-least-once; backoff (1m, 5m, 30m, 2h, 12h, 24h)—সর্বোচ্চ 8 চেষ্টা। কনজিউমার: signature ভেরিফাই, ১০ সেকেন্ডে রেসপন্স, idempotent প্রসেসিং।

Chapter 7.6 — Webhooks

1. Purpose

Webhooks are how travoBooks notifies partner systems of events without requiring them to poll. This chapter defines the event catalog, the delivery contract (signatures, retries, ordering, idempotency), and the operational tooling partners get to manage webhook configurations.

2. Why it matters

A reliable webhook system is the contract between travoBooks and partner-side systems. If a payment.succeeded event is silently dropped, the partner's CRM never knows the booking is paid. Reliability properties — at-least-once delivery, signature verification, replay protection, observable failure — turn webhooks from a nice-to-have into a production-grade integration.

3. Event taxonomy

Naming pattern: <resource>.<action> (e.g., booking.issued).

Bookings

  • booking.draft.created
  • booking.held
  • booking.pending_payment
  • booking.pending_approval
  • booking.issued
  • booking.cancelled_before_issue
  • booking.cancelled_after_issue
  • booking.partially_refunded
  • booking.completed
  • booking.expired
  • booking.schedule_changed

Tickets / EMDs

  • ticket.issued
  • ticket.voided
  • ticket.exchanged
  • ticket.refunded
  • emd.issued
  • emd.refunded

Customer / Supplier

  • customer.created
  • customer.updated
  • customer.credit_limit_changed
  • customer.credit_hold_placed
  • customer.credit_hold_released
  • supplier.created
  • supplier.bank_details_changed

Financial

  • invoice.generated
  • invoice.sent
  • invoice.paid
  • invoice.overdue
  • payment.received
  • payment.refunded
  • payment.failed
  • payout.initiated
  • payout.completed
  • refund.requested
  • refund.completed
  • refund.rejected
  • journal_entry.posted

Memos

  • memo.received (ADM or ACM)
  • memo.linked
  • memo.disputed
  • memo.settled

Operational

  • import_run.completed
  • import_run.failed
  • period.soft_closed
  • period.closed
  • period.reopened

(The full catalog is maintained in code; this list is illustrative.)

4. Event payload

{
  "id": "evt_8c2f9...",
  "type": "booking.issued",
  "created_at": "2026-05-26T18:45:32.123456+06:00",
  "partner_id": 42,
  "data": {
    "booking_id": 1009287,
    "booking_ref": "TVB-2026-000123",
    "state": "ISSUED",
    "amount": "65400.00",
    "currency": "BDT",
    "customer_id": 4521,
    "supplier_id": 17,
    "primary_ticket_number": "176-2400000123",
    "service_date_start": "2026-05-28",
    "issued_at": "2026-05-26T18:45:30.000000+06:00"
  },
  "meta": {
    "api_version": "v1",
    "delivery_attempt": 1
  }
}

data shape is the same as the corresponding API resource. Adding new fields to data is not breaking; consumers must tolerate unknown fields.

5. Delivery contract

Property Behaviour
Delivery semantics At-least-once
Ordering Not guaranteed. Consumers must tolerate out-of-order delivery (use created_at for ordering).
Timeout per attempt 10 seconds
Expected response HTTP 2xx
Retry on 4xx (excluding 408, 429) No (treated as permanent rejection)
Retry on 5xx, 408, 429 Yes with backoff
Backoff schedule 1m, 5m, 30m, 2h, 12h, 24h, 24h, 24h (≈4 days)
Maximum attempts 8
After max attempts Marked failed; alert to partner; not auto-retried

6. Signature & verification

Each delivery carries a HMAC-SHA256 signature:

travoBooks-Signature: t=1716729932,v1=8d4e2f...
travoBooks-Webhook-Id: whd_...
travoBooks-Event-Id: evt_...

Computation: 1. travoBooks computes signed_payload = "{timestamp}.{raw_body}". 2. HMAC-SHA256 with the partner's webhook secret as key. 3. Send as v1=<hex>.

Consumer verification: 1. Read t and v1 from header. 2. Reject if |now - t| > 300s (replay protection). 3. Recompute HMAC over t.raw_body and constant-time compare to v1. 4. Reject on mismatch.

Multiple signatures (v1=...,v1=...) allowed during secret rotation. Consumers must accept any.

7. Idempotency

Every delivery has a unique webhook_delivery_id (in header travoBooks-Webhook-Id) and a unique evt_... id. Consumers should dedupe by evt_id — multiple delivery attempts of the same event share the same evt_id.

Consumer handler pattern:

on_webhook(request):
    verify_signature(request)
    event = parse(request.body)
    if already_processed(event.id):  # idempotency
        return 200
    process(event)
    mark_processed(event.id)
    return 200

8. Subscriptions

Per partner, subscriptions defined via API:

POST /v1/webhooks
{
  "url": "https://partner.example/hooks/travobooks",
  "description": "CRM sync",
  "event_types": ["booking.*", "refund.completed"],
  "active": true
}

Wildcards: booking.* matches all booking.<x>.

Multiple subscriptions per partner allowed; same event may be delivered to multiple URLs.

9. Secret management

On creation, a secret is generated (whsec_...); shown once in the response and never returned again. Stored hashed on travoBooks side; revealed plaintext only in initial response.

Rotation: - Partner generates new secret via API. - Returns both old and new secret active simultaneously for a 30-day window. - travoBooks signs with new; can also include legacy signature for verification compatibility. - Old secret revoked after 30 days.

10. Failure handling & resurrection

If 8 attempts fail: - Subscription auto-paused if 100% failure over 30 minutes (protect partner from flood when their endpoint is down). - Email alert to partner_admin. - "Resurrect" API: partner can request redelivery of all failed events in a time window after fixing their endpoint:

POST /v1/webhooks/{id}/redeliver
{ "since": "2026-05-26T00:00:00Z" }

11. Observability for partners

Per subscription, travoBooks exposes: - Last 100 deliveries with status, attempt count, response code, response body (truncated). - Aggregate success rate (7-day, 30-day). - Time-to-first-success and time-to-final-failure distributions.

Surfaced both via UI and API.

12. Sample event flow

sequenceDiagram autonumber participant Core as travoBooks Core participant Q as Webhook Queue participant W as Webhook Worker participant DB as MySQL participant Partner as Partner Endpoint Core->>DB: Business event committed (booking.issued) Core->>Q: Enqueue webhook job W->>DB: Fetch subscription(s) matching event loop For each subscription W->>W: Build payload + signature W->>Partner: POST /hooks (10s timeout) alt 2xx response W->>DB: Mark delivery success else 5xx / timeout W->>DB: Mark attempt failed W->>Q: Schedule retry with backoff else 4xx (non-retryable) W->>DB: Mark delivery failed (no retry) W->>Partner: Notify by email if first end end

13. Database tables touched

Table Role
webhook_endpoints Subscriptions
webhook_secrets Hashed secrets + rotation
webhook_deliveries Per-event-per-endpoint record
webhook_delivery_attempts Per attempt detail
audit_logs Subscription create/update/delete

14. Security considerations

  • 🔒 Endpoint URLs must be HTTPS. HTTP rejected at subscription creation.
  • 🔒 Domain validation: subscriptions to obvious internal/localhost URLs blocked.
  • 🔒 Signature is the primary authn; partners must verify on every event.
  • 🔒 Replay window: 5 minutes. Outside that → reject.
  • 🔒 travoBooks never includes secrets in payloads.

15. Common pitfalls

  • ⚠️ Trusting payloads without signature verification. Anyone could POST to the endpoint.
  • ⚠️ Processing events out-of-order assuming order. Use timestamps; treat as eventually-consistent.
  • ⚠️ Long-running handlers. 10s timeout. Acknowledge fast; do work async.
  • ⚠️ Returning 200 before processing reliably. Risk losing the event. Acknowledge after persisting the event for later processing.
  • ⚠️ Not deduplicating. At-least-once means duplicates happen.
  • ⚠️ Ignoring 4xx from partner endpoint. A persistent 4xx means consumer-side bug; travoBooks stops retrying — partner must fix and redeliver.
  • 🔒 Storing webhook secrets in version control. Use partner's secret manager.