Chapter 7.5 — Public API
1. Purpose
travoBooks exposes a REST API that lets partners and approved third-party developers programmatically search offers, create bookings, manage customers/suppliers, retrieve financial data, and ingest webhooks. The same API powers the platform's own UI — the UI is the first consumer of the API. This chapter defines the API's structure, authentication, versioning, rate limiting, error contract, and operational guarantees.
2. Why it matters
The API is the platform's product surface. For developer-segment customers (one of travoBooks' four target segments per Chapter 0.1), it is the product. API design quality drives adoption directly: - Resource modelling clarity = onboarding speed. - Error contract consistency = production reliability. - Versioning discipline = long-term trust.
3. Design principles
- REST over HTTPS — verbs (
GET,POST,PATCH,DELETE); resource-oriented URLs. - JSON-only for request/response bodies in Phase 1 (XML support deferred).
- UTF-8 everywhere.
- ISO-8601 for dates and datetimes (always with timezone; never naïve).
- ISO-4217 for currency codes (3-letter).
- Decimal as string for monetary amounts to preserve precision (
"amount": "1234.56"). - snake_case for JSON keys (matches DB conventions).
- Idempotency-Key header supported on all non-idempotent endpoints.
- Cursor pagination for lists (no offset pagination).
- API-first — the UI uses the same API; no internal-only endpoints in Phase 1.
4. Authentication
Three authentication modes:
4.1 Personal Access Tokens (PATs)
Format: tgc_<env>_<random> where <env> is live or test.
Examples:
- tvb_live_K3p9...
- tvb_test_8sQv...
Stored hashed (Argon2id) in personal_access_tokens (Chapter 2.2). Sent as bearer token:
Authorization: Bearer tvb_live_K3p9...
Scoped to a single partner; tied to a user; carries the user's permissions.
4.2 OAuth 2.0 (Phase 2)
For third-party apps acting on behalf of partner users. Authorization-code flow with PKCE.
4.3 Session cookies
For the platform's own UI only — __Host-tvb_session cookie. Not used by external clients.
5. Versioning
URL-prefix versioning: /v1/..., /v2/....
- Within a major version, only backward-compatible changes are introduced (new optional fields, new endpoints).
- Breaking changes go in a new major version.
- Each major version is supported for at least 24 months after the next major launches.
- Deprecation announced ≥ 12 months ahead;
Sunsetheader included on responses for deprecated endpoints.
6. Resource taxonomy
Top-level resources (per partner scope):
| Resource | URL |
|---|---|
| Customers | /v1/customers |
| Suppliers | /v1/suppliers |
| Offers | /v1/offers |
| Bookings | /v1/bookings |
| Tickets | /v1/tickets |
| Invoices | /v1/invoices |
| Payments | /v1/payments |
| Refunds | /v1/refunds |
| Memos (ADM/ACM) | /v1/memos |
| Commission accruals | /v1/commission/accruals |
| Journal entries | /v1/ledger/journal-entries |
| Reports | /v1/reports/{report-name} |
| Webhooks | /v1/webhooks |
| Users | /v1/users |
Each resource supports standard verbs where semantically meaningful.
7. Request / response shape
Request example — Create booking
POST /v1/bookings HTTP/1.1
Authorization: Bearer tvb_live_...
Idempotency-Key: 8c1d4a02-...
Content-Type: application/json
{
"customer_id": 4521,
"offer_id": "offer_TG_2026_xyz",
"passengers": [
{"first_name": "Ahmed", "last_name": "Khan", "passport_number": "...", "date_of_birth": "1992-04-12"}
],
"remarks": "Window seat preferred"
}
Response shape — Standard envelope
{
"data": {
"booking_id": 1009287,
"booking_ref": "TVB-2026-000123",
"state": "HELD",
"pnr": "ABC123",
"hold_expires_at": "2026-05-26T19:30:00+06:00",
"amount": "65400.00",
"currency": "BDT",
"created_at": "2026-05-26T18:45:00+06:00"
},
"meta": {
"request_id": "req_4f2a..."
}
}
List response with pagination
{
"data": [ {...}, {...}, ... ],
"meta": {
"request_id": "req_...",
"pagination": {
"next_cursor": "Y3Vyc29yOjQ1Njc=",
"has_more": true
}
}
}
Pagination request: ?cursor=Y3Vyc29yOjQ1Njc=&limit=50.
8. Error contract
Standard error envelope:
{
"error": {
"code": "BOOKING_HOLD_EXPIRED",
"message": "The supplier hold has expired. Please re-search.",
"request_id": "req_4f2a...",
"field_errors": [
{"field": "passengers[0].passport_number", "code": "FIELD_REQUIRED", "message": "Passport required for international itinerary"}
],
"retry_advice": "non_retryable"
}
}
retry_advice values: non_retryable, retryable_after_short, retryable_after_long, requires_user_action.
| HTTP | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 202 | Accepted (async) |
| 400 | Validation error |
| 401 | Auth invalid |
| 403 | Permission denied |
| 404 | Not found |
| 409 | Conflict (idempotency conflict, state conflict) |
| 422 | Business rule violation |
| 429 | Rate limited |
| 500 | Server error |
| 503 | Supplier unavailable |
Full error code catalog: Chapter 14.2.
9. Rate limiting
Per-PAT and per-partner:
| Bucket | Limit |
|---|---|
| Search offers | 600/min per partner |
| Booking creation | 60/min per partner |
| Ticket issuance | 30/min per partner |
| Read-only endpoints | 1200/min per partner |
| Report generation | 10/min per partner (heavy operation) |
Exceeded → 429 Too Many Requests with Retry-After header.
Rate-limit headers on every response:
- X-RateLimit-Limit: 600
- X-RateLimit-Remaining: 587
- X-RateLimit-Reset: 1716729600
10. Idempotency
Endpoints that create or modify state accept Idempotency-Key header (UUID format recommended). Server caches the response keyed by (partner_id, endpoint, idempotency_key) for 24 hours.
Replay with same key + same body → cached response.
Replay with same key + different body → 409 IDEMPOTENCY_KEY_CONFLICT.
11. Test mode
Each PAT is either live or test. Test PATs hit a parallel sandbox:
- Mocked supplier responses (configurable per partner).
- Test data isolated from production.
- Full feature parity for booking creation, ticket issuance, refund.
- BSP / bank / gateway integrations stubbed.
12. Webhook subscriptions
Partners register webhook endpoints via:
POST /v1/webhooks
{
"url": "https://partner.example/travobooks-events",
"event_types": ["booking.issued", "refund.completed"],
"secret": "whsec_..."
}
travoBooks posts events with HMAC-SHA256 signature in travoBooks-Signature header. Detailed in Chapter 7.6.
13. Observability for API clients
Every response includes:
- X-Request-ID — for support correlation.
- X-travoBooks-Trace — internal trace (debug mode).
- Response time, partner ID, user ID logged on travoBooks side.
Partners can retrieve their last 24h of API requests via /v1/diagnostics/requests (rate-limited; for self-debugging).
14. SDKs
Phase 1: official PHP SDK; community-maintained JS/TS bindings. Phase 2: official Node, Python, Go SDKs.
SDKs encapsulate auth, retries, idempotency-key generation, and typed response models.
15. Backward-compatibility commitments
Within v1, travoBooks commits:
- Existing fields will not be removed.
- Field types will not change.
- Enum values will not be removed (new values may be added; clients should ignore unknown values).
- Endpoint URLs will not move.
- HTTP status semantics will not change.
Additions (new fields, endpoints, enum values, optional headers) may happen at any time and are not breaking. Clients must be tolerant of unknown fields.
16. Common pitfalls
- ⚠️ Treating 5xx as retryable always. Some 5xx are non-retryable (e.g., supplier responded with a permanent fail).
- ⚠️ Hard-coding enum values. Always handle the unknown case.
- ⚠️ Storing PATs in client-side code. Never. Server-to-server only.
- ⚠️ Skipping idempotency keys on retries. Causes double-bookings.
- 🔒 Logging full request bodies including PII / auth headers. Strip before logging.
- ⚠️ Polling for changes instead of using webhooks. Webhooks are authoritative; polling burns rate-limit.