Security
How to keep your FX-Port integration secure — key handling, booking authorization, and general API security hygiene.
An FX-Port API key with Read+Write permission can create bookings and trigger real financial debits from your agency balance. Treat it with the same care as a payment credential.
Always keep keys server-side
Never expose your FX-Port API key in:
- Browser JavaScript (React client components, Vue, Angular, plain JS)
- Mobile apps (React Native, Expo, Flutter, native iOS/Android)
- Public repositories, commit history, or CI/CD environment variables visible in build logs
- Client-side
.envfiles shipped with a compiled app
Your app's backend (Node.js, Python, Go, etc.) should be the only code that ever reads and uses the FX-Port API key. If your front-end needs to trigger a booking, it should call your own authenticated backend, which then calls FX-Port on the user's behalf.
Browser/App → Your backend → FX-Port API
↑
API key lives here onlyMinimal-permission keys
Use a Read-only key for integrations that only display search results, balances, or status. Reserve the Read+Write key for the backend service that actually creates bookings. See Authentication for what each permission level allows.
Key rotation
Rotate keys immediately if you suspect a leak. The dashboard lets you invalidate and replace a key without downtime. Rotate on a routine schedule too (e.g. every 90 days) rather than only after an incident — routine rotation limits how long a silently-leaked key stays useful to an attacker.
Enforce booking ownership in your own backend
FX-Port scopes every response to the agency that owns the API key — one agency's key can never return another agency's bookings, balances, or account data. But that only protects the FX-Port API boundary. If your own application lets multiple end-clients share one FX-Port key (a common pattern), you are responsible for authorization inside your own backend so one client cannot view, modify, or cancel another client's booking.
A booking reference, order ID, ICCID, or PNR is not a secret — it can be guessed, enumerated, logged, or leaked through a shared link. Never treat "the caller supplied a valid booking ID" as proof that the caller is entitled to see it. Concretely:
- Store which of your own end-users/clients created or is entitled to each booking (a
client_id/user_idcolumn alongside the FX-Portbooking_idyou save). - On every read or mutation your backend exposes (get booking, get ticket, cancel, pay, request a refund, view usage, etc.), check that the authenticated caller in your system owns that booking before forwarding the request to FX-Port — do not rely on the booking ID alone as an access-control token.
- Apply the same rule to balances, transaction/financial history, and account data: scope every query by the authenticated caller's own identity, never by an ID taken directly from the request path or query string.
- If you use a database with row-level security (Supabase, Postgres RLS, etc.), enforce this at the
database layer as well as in application code — defense in depth. A backend bug that forgets an
owner_idfilter should not be enough, on its own, to expose another client's data. - Test this explicitly: as client A, try to fetch/cancel a booking that belongs to client B by ID.
It should fail with
403/404, not succeed. Treat this as a required test case for every booking, balance, and account endpoint you expose — for flights, hotels, eSIMs, and any other product — not an edge case to defer.
This is Broken Object Level Authorization (OWASP API Security Top 10, API1:2023) — the single most common vulnerability class in booking/travel APIs. It is entirely preventable with a per-request ownership check and is far cheaper to build in from day one than to retrofit after a data leak.
Validate and sanitize webhook/callback payloads
If you build a webhook receiver or callback endpoint for booking status updates, treat every incoming payload as untrusted input, not as fact:
- Verify a signature/secret on every callback before acting on it, rather than trusting the sender because the request "looks right".
- Re-fetch authoritative state from the FX-Port API before making a financial or booking decision based on a webhook payload, instead of trusting fields inside the webhook body directly.
- Make callback handlers idempotent — a retried or duplicated webhook delivery should not double-book, double-refund, or double-charge.
Rate limiting and abuse protection
Search and pricing endpoints are inexpensive to call but not free to abuse:
- Apply your own rate limiting in front of any public-facing feature that triggers an FX-Port call on user input (a search box, a price checker), so a single abusive user or bot can't exhaust your key's rate limit or run up unnecessary supplier calls.
- Cache read-only, non-personalized responses (e.g. suppliers or airports) client-side or at your edge where appropriate, instead of re-querying on every page load.
Logging and data retention
- Never log full API keys, even in debug/error logs — log a short prefix (
fxp_live_ab12…) if you need to identify which key made a call. - Avoid logging full traveler PII (passport numbers, full card/payment details) in plaintext application logs. If you must retain it for support, encrypt it at rest and restrict access.
- Apply a retention policy to logs and cached search/pricing responses containing personal data, rather than keeping it indefinitely by default.
Broader security hygiene
These rules apply to every sensitive credential in your application stack, not just FX-Port keys.
- Supabase / database API keys — never expose the
service_rolekey in a browser or mobile app. Only theanonkey can be used client-side, and even that should be behind Row-Level Security policies. - Third-party vendor keys (Stripe, Twilio, etc.) — always consumed server-side. A leaked Stripe secret key or Twilio auth token can cause direct financial loss.
- WordPress and open-source CMS plugins — themes and plugins are a frequent attack vector. Credentials stored in a WordPress database are regularly exfiltrated through plugin vulnerabilities. If you run a CMS alongside your booking integration, keep credentials in separate, isolated infrastructure and audit installed plugins regularly.
- Dependency CVEs — keep your runtime and framework versions current. Subscribe to security advisories for your stack (nodejs.org/en/security, github.com/advisories) and apply patches promptly.
- Least privilege everywhere — the same "minimal-permission key" principle above applies to cloud IAM roles, CI/CD service accounts, and internal admin tooling. A credential should only be able to do what its specific job requires.
Questions?
Found a vulnerability or want a security review of your integration? Email [email protected].