Travlix Travel API v1
Base URL https://api.travlix.com. Downloads:
OpenAPI 3 (YAML) ·
Postman collection
Quick start
- Create an account and verify your email.
- Create a sandbox key in API keys and copy the secret (shown once).
- Sign each request:
X-Signature = hex(HMAC-SHA256(secret, METHOD\nPATH\nQUERY\nTIMESTAMP\nNONCE\nhex(sha256(body)))), withX-Api-Key,X-TimestampandX-Nonce. - Flow: search → fare-quote → book (with
Idempotency-Key) → tickets issued → webhookticket.issued.
Signing samples
curl · download
#!/usr/bin/env bash
# Travlix API v1 - signed request with curl + openssl. Usage: TLX_KEY=... TLX_SECRET=... ./sign.sh
set -euo pipefail
BASE="${TLX_BASE:-https://api.travlix.com}"
tlx_call() { # method path [json-body]
local method="$1" path="$2" body="${3:-}" ts nonce bh sig q p
ts=$(date +%s); nonce=$(openssl rand -hex 16)
p="${path%%\?*}"; q=""; [[ "$path" == *\?* ]] && q="${path#*\?}"
bh=$(printf '%s' "$body" | openssl dgst -sha256 -hex | sed 's/^.* //')
sig=$(printf '%s\n%s\n%s\n%s\n%s\n%s' "$method" "$p" "$q" "$ts" "$nonce" "$bh" | openssl dgst -sha256 -hmac "$TLX_SECRET" -hex | sed 's/^.* //')
local args=(-sS -X "$method" "$BASE$path" -H 'Content-Type: application/json' -H "X-Api-Key: $TLX_KEY"
-H "X-Timestamp: $ts" -H "X-Nonce: $nonce" -H "X-Signature: $sig")
[ -n "$body" ] && args+=(--data-binary "$body")
curl "${args[@]}"; echo
}
tlx_call GET /v1/wallet/balance
tlx_call POST /v1/flight/search "{\"origin\":\"DEL\",\"destination\":\"BOM\",\"departure_date\":\"$(date -d '+30 days' +%F 2>/dev/null || date -v+30d +%F)\",\"adults\":1}"
PHP · download
<?php
// Travlix API v1 - signed request in PHP (curl). Usage: TLX_KEY=... TLX_SECRET=... php sign.php
$base = getenv('TLX_BASE') ?: 'https://api.travlix.com';
$key = getenv('TLX_KEY');
$secret = getenv('TLX_SECRET');
function tlx_call(string $method, string $path, ?array $body = null, array $headers = []): array
{
global $base, $key, $secret;
$raw = $body === null ? '' : json_encode($body, JSON_UNESCAPED_SLASHES);
$u = parse_url($path);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$sts = implode("\n", [strtoupper($method), $u['path'], $u['query'] ?? '', $ts, $nonce, hash('sha256', $raw)]);
$h = array_merge(['Content-Type: application/json', 'X-Api-Key: ' . $key, 'X-Timestamp: ' . $ts,
'X-Nonce: ' . $nonce, 'X-Signature: ' . hash_hmac('sha256', $sts, $secret)], $headers);
$ch = curl_init($base . $path);
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => $method, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $h, CURLOPT_TIMEOUT => 90]);
if ($method !== 'GET') curl_setopt($ch, CURLOPT_POSTFIELDS, $raw);
$res = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [$code, json_decode((string) $res, true)];
}
[$code, $r] = tlx_call('GET', '/v1/wallet/balance');
echo "balance $code: " . json_encode($r['data'] ?? $r['error']) . "\n";
[$code, $r] = tlx_call('POST', '/v1/flight/search', ['origin' => 'DEL', 'destination' => 'BOM',
'departure_date' => date('Y-m-d', strtotime('+30 days')), 'adults' => 1]);
echo "search $code: " . ($r['data']['count'] ?? 0) . " offers\n";
if (!empty($r['data']['offers'])) {
[$code, $q] = tlx_call('POST', '/v1/flight/fare-quote', ['offer_id' => $r['data']['offers'][0]['offer_id']]);
echo "fare-quote $code: total " . ($q['data']['price']['total'] ?? '?') . "\n";
// Book (uncomment): tlx_call('POST', '/v1/flight/book', [...], ['Idempotency-Key: ' . bin2hex(random_bytes(12))]);
}
Python · download
"""Travlix API v1 - signed request in Python 3 (requests). Usage: TLX_KEY=... TLX_SECRET=... python3 sign.py"""
import datetime, hashlib, hmac, json, os, secrets, time
from urllib.parse import urlsplit
import requests
BASE = os.environ.get("TLX_BASE", "https://api.travlix.com")
KEY, SECRET = os.environ["TLX_KEY"], os.environ["TLX_SECRET"]
def tlx_call(method, path, body=None, headers=None):
raw = b"" if body is None else json.dumps(body, separators=(",", ":")).encode()
u = urlsplit(path)
ts, nonce = str(int(time.time())), secrets.token_hex(16)
sts = "\n".join([method.upper(), u.path, u.query, ts, nonce, hashlib.sha256(raw).hexdigest()])
sig = hmac.new(SECRET.encode(), sts.encode(), hashlib.sha256).hexdigest()
h = {"Content-Type": "application/json", "X-Api-Key": KEY, "X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": sig}
h.update(headers or {})
r = requests.request(method, BASE + path, data=raw if method != "GET" else None, headers=h, timeout=90)
return r.status_code, r.json()
if __name__ == "__main__":
print("balance", *tlx_call("GET", "/v1/wallet/balance"))
date = (datetime.date.today() + datetime.timedelta(days=30)).isoformat()
code, r = tlx_call("POST", "/v1/flight/search", {"origin": "DEL", "destination": "BOM", "departure_date": date, "adults": 1})
print("search", code, r.get("data", {}).get("count"), "offers")
if r.get("data", {}).get("offers"):
code, q = tlx_call("POST", "/v1/flight/fare-quote", {"offer_id": r["data"]["offers"][0]["offer_id"]})
print("fare-quote", code, q.get("data", {}).get("price", {}).get("total"))
Node.js · download
// Travlix API v1 - signed request in Node.js 18+ (built-in fetch). Usage: TLX_KEY=... TLX_SECRET=... node sign.js
const crypto = require('crypto');
const BASE = process.env.TLX_BASE || 'https://api.travlix.com';
const KEY = process.env.TLX_KEY, SECRET = process.env.TLX_SECRET;
async function tlxCall(method, path, body, headers = {}) {
const raw = body === undefined ? '' : JSON.stringify(body);
const u = new URL(path, BASE);
const ts = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(16).toString('hex');
const sts = [method.toUpperCase(), u.pathname, u.search.replace(/^\?/, ''), ts, nonce,
crypto.createHash('sha256').update(raw).digest('hex')].join('\n');
const sig = crypto.createHmac('sha256', SECRET).update(sts).digest('hex');
const res = await fetch(u, { method, body: method === 'GET' ? undefined : raw, headers: {
'Content-Type': 'application/json', 'X-Api-Key': KEY, 'X-Timestamp': ts, 'X-Nonce': nonce, 'X-Signature': sig, ...headers } });
return [res.status, await res.json()];
}
(async () => {
console.log('balance', ...(await tlxCall('GET', '/v1/wallet/balance')));
const date = new Date(Date.now() + 30 * 864e5).toISOString().slice(0, 10);
const [code, r] = await tlxCall('POST', '/v1/flight/search', { origin: 'DEL', destination: 'BOM', departure_date: date, adults: 1 });
console.log('search', code, r.data && r.data.count, 'offers');
if (r.data && r.data.offers.length) {
const [c2, q] = await tlxCall('POST', '/v1/flight/fare-quote', { offer_id: r.data.offers[0].offer_id });
console.log('fare-quote', c2, q.data && q.data.price.total);
}
})();
Verifying webhooks
Header X-Travlix-Signature: t=<unix>,v1=<hex> where v1 = HMAC-SHA256(webhook secret, t + "." + raw body). Reject if older than 5 minutes; de-duplicate on X-Travlix-Delivery.
PHP
<?php
// Verify a Travlix webhook (plain PHP endpoint). Set TLX_WEBHOOK_SECRET.
$secret = getenv('TLX_WEBHOOK_SECRET');
$raw = file_get_contents('php://input');
$hdr = $_SERVER['HTTP_X_TRAVLIX_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $hdr), $p);
$t = (int) ($p['t'] ?? 0);
$expected = hash_hmac('sha256', $t . '.' . $raw, $secret);
if (!$t || abs(time() - $t) > 300 || !hash_equals($expected, (string) ($p['v1'] ?? ''))) {
http_response_code(400);
exit('bad signature');
}
$event = json_decode($raw, true);
// De-duplicate on $event['id'] (also in X-Travlix-Delivery), then handle $event['type'] / $event['data'].
http_response_code(200);
echo 'ok';
Python (Flask)
"""Verify a Travlix webhook (Flask). Set TLX_WEBHOOK_SECRET."""
import hashlib, hmac, os, time
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["TLX_WEBHOOK_SECRET"].encode()
@app.post("/travlix/webhook")
def hook():
raw = request.get_data()
parts = dict(p.split("=", 1) for p in request.headers.get("X-Travlix-Signature", "").split(",") if "=" in p)
t = parts.get("t", "")
expected = hmac.new(SECRET, t.encode() + b"." + raw, hashlib.sha256).hexdigest()
if not t.isdigit() or abs(time.time() - int(t)) > 300 or not hmac.compare_digest(expected, parts.get("v1", "")):
abort(400)
event = request.get_json()
# de-duplicate on event["id"], then handle event["type"]
return "ok"
Node.js (Express)
// Verify a Travlix webhook (Express). Set TLX_WEBHOOK_SECRET. Use the RAW body, not re-serialized JSON.
const crypto = require('crypto');
const express = require('express');
const app = express();
app.post('/travlix/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const parts = Object.fromEntries((req.get('X-Travlix-Signature') || '').split(',').map((p) => p.split('=')));
const t = parts.t || '';
const expected = crypto.createHmac('sha256', process.env.TLX_WEBHOOK_SECRET).update(t + '.' + req.body.toString('utf8')).digest('hex');
const ok = /^\d+$/.test(t) && Math.abs(Date.now() / 1000 - Number(t)) <= 300 && parts.v1 && parts.v1.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
if (!ok) return res.status(400).send('bad signature');
const event = JSON.parse(req.body.toString('utf8'));
// de-duplicate on event.id, then handle event.type
res.send('ok');
});
app.listen(3000);