Audience: Travel agencies, tour operators, TMCs, banks, fintechs and travel apps integrating flights into their own platform
Version: Partner API v1
Base path: /api/v1/partner
1. Getting access
- Email support@getmytrip.com with the subject
[PARTNER API] Access request. Tell us your company name, website, and what you want to build. - Once approved, GetMyTrip sends you sandbox credentials and your base URL.
- Build and test against the sandbox. It uses live fares and simulated bookings, and is free for up to 60 days.
- When your integration passes the go-live checklist (section 9), GetMyTrip issues production credentials.
What you receive from GetMyTrip
| Item | Description |
|---|---|
| Base URL | Sent with your credentials, e.g. https://<base-url>/api/v1 |
client_id | Public partner identifier (e.g. gmt_sandbox_demo-bank_…) |
client_secret | Secret shown once at issuance. Store it in a vault |
| Environment | SANDBOX or PRODUCTION |
| Scopes | Usually: locations.search, flights.search, flights.price, flights.book, flights.read |
Sandbox note: With SANDBOX credentials, search and price-confirm return real fares from live airline/GDS inventory; they never reserve seats. Booking is simulated: your request is validated exactly as in production, then you receive a test PNR and orderId, but no airline reservation is made and no traveller email is sent. See section 4.7.
2. Authentication (OAuth 2.0 Client Credentials)
Step A: Get an access token
POST /api/v1/partner/oauth/token
Content-Type: application/json
{
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
Success (200):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "flights.search flights.price flights.book flights.read locations.search",
"partner": {
"id": "...",
"name": "Demo Bank",
"slug": "demo-bank",
"status": "SANDBOX",
"environment": "SANDBOX"
}
}
Step B: Call APIs with the token
Authorization: Bearer <access_token>
Tokens expire in about 1 hour. Cache and refresh by calling /oauth/token again. Never put client_secret in a browser or mobile app; call GetMyTrip from your backend only.
3. End-to-end booking flow
1. (Optional) Locations autocomplete
2. Search flights → save offerToken from chosen offer
3. Price confirm → new offerToken (use this for book)
4. Create order → orderId + PNR/recordLocator
5. Retrieve order → status / confirmation
Offers returned to partners are sanitized. Supplier internals are not exposed. You must pass offerToken into price-confirm and book (do not rebuild raw supplier payloads). Requests without offerToken are rejected.
Offers come from GDS and airline NDC content (source: GDS or NDC). The booking flow is the same for both.
offerToken is valid for about 30 minutes.
4. API reference
4.1 Airport / city search
GET /api/v1/partner/locations/search?keyword=LOS
Authorization: Bearer <token>
4.2 Flight search
GET /api/v1/partner/flights/search
?originLocationCode=LOS
&destinationLocationCode=LHR
&departureDate=2026-12-10
&adults=1
&cabinClass=ECONOMY
Authorization: Bearer <token>
| Query | Required | Notes |
|---|---|---|
originLocationCode | Yes* | IATA, e.g. LOS |
destinationLocationCode | Yes* | IATA, e.g. LHR |
departureDate | Yes* | YYYY-MM-DD |
returnDate | No | Round-trip |
adults | No | Default 1 |
children / infants | No | |
cabinClass | No | e.g. ECONOMY, BUSINESS |
nonStop | No | true / false |
currencyCode | No | Display preference when supported |
* Multi-city uses leg query params (same as the GetMyTrip website search).
Success shape:
{
"success": true,
"message": "Flights retrieved successfully",
"data": [
{
"id": "...",
"source": "GDS",
"price": { "currency": "NGN", "total": "902068.65", "base": "...", "grandTotal": "..." },
"itineraries": [ /* segments */ ],
"travelerPricings": [ /* ... */ ],
"offerToken": "uuid-cache-id",
"expiresAt": "2026-09-03T13:30:00.000Z"
}
],
"meta": { "totalOffers": 248, "currency": "NGN" }
}
Pick one offer and keep its offerToken.
4.3 Price confirm (mandatory before book)
POST /api/v1/partner/flights/price-confirm
Authorization: Bearer <token>
Content-Type: application/json
{
"offerToken": "uuid-from-search"
}
Success shape:
{
"success": true,
"message": "Amadeus flight price confirmed successfully",
"data": {
"offer": { "price": { "currency": "NGN", "grandTotal": "..." }, "offerToken": "...", "expiresAt": "..." },
"offerToken": "new-uuid-for-book",
"expiresAt": "...",
"priceChanged": false,
"totalPrice": "...",
"currency": "NGN"
}
}
Always use the new data.offerToken from this response for booking.
4.4 Book (create order)
POST /api/v1/partner/flights/orders
Authorization: Bearer <token>
Content-Type: application/json
{
"offerToken": "uuid-from-price-confirm",
"travelers": [
{
"id": "1",
"dateOfBirth": "1990-05-15",
"gender": "MALE",
"name": {
"firstName": "ADE",
"lastName": "OKAFOR"
},
"documents": [
{
"documentType": "PASSPORT",
"number": "A12345678",
"expiryDate": "2030-01-01",
"issuanceCountry": "NG",
"nationality": "NG",
"holder": true
}
]
}
],
"contacts": [
{
"addresseeName": {
"firstName": "ADE",
"lastName": "OKAFOR"
},
"emailAddress": "traveller@example.com",
"phones": [
{
"deviceType": "MOBILE",
"countryCallingCode": "234",
"number": "8012345678"
}
]
}
]
}
Traveler count must match the offer (adults/children/infants from search).
Booking rules (all airlines):
- Always book with the
offerTokenreturned by price-confirm. Search tokens are rejected for NDC airline offers. gender(MALE/FEMALE) anddateOfBirthare required for every traveler.- Optional
travelerType(ADULT,CHILD,HELD_INFANT) on each traveler. If omitted, it is worked out fromdateOfBirth. - Optional
title(MR,MRS,MS,MISS,MASTER) on each traveler. - Include
issuanceDateon passport documents so the passport is passed to every airline. - Optional
addressoncontacts[0](lines,cityName,postalCode,countryCode).
Success (201):
{
"success": true,
"message": "Flight booked successfully",
"data": {
"orderId": "...",
"recordLocator": "ABC123",
"pnr": "ABC123",
"status": "CONFIRMED",
"totalPrice": 902068.65,
"currency": "NGN",
"autoCancelAt": "2026-11-02T10:15:00.000Z"
}
}
Ticketing deadline (autoCancelAt): a booking is a reservation until GetMyTrip issues the ticket. If it is still unticketed at autoCancelAt, GetMyTrip cancels it with the airline and its status becomes EXPIRED. The deadline is the earlier of 24 hours after booking and 1 hour before the airline's own ticketing or payment deadline. autoCancelAt is null once the booking is ticketed, paid or closed.
4.5 Retrieve bookings
List:
GET /api/v1/partner/flights/orders
Authorization: Bearer <token>
One order (must belong to your partner account):
GET /api/v1/partner/flights/orders/{orderId}
Authorization: Bearer <token>
The single-order response includes autoCancelAt. This endpoint is the authoritative booking status; webhooks are sent once and are not retried.
| Status | Meaning |
|---|---|
CONFIRMED | Reserved with the airline (PNR issued), not yet ticketed |
PENDING | Reserved but the supplier has not returned a PNR yet |
TICKETED | Ticket issued |
EXPIRED | Cancelled automatically because it was not ticketed by autoCancelAt |
CANCELLED | Cancelled by GetMyTrip operations |
4.6 Webhooks
Send GetMyTrip your webhook URL during onboarding. To test it:
POST /api/v1/partner/webhooks/test
Authorization: Bearer <token>
GetMyTrip may POST these events to your webhook:
| Event | When | Key data fields |
|---|---|---|
booking.confirmed | Booking created | orderId, recordLocator, status, totalPrice, currency, autoCancelAt |
booking.cancelled | Unticketed booking cancelled at its deadline | orderId, recordLocator, status: "EXPIRED", reason: "TICKETING_DEADLINE_PASSED", autoCancelAt, cancelledAt |
ticket.issued | The deadline check found the airline had already ticketed | orderId, recordLocator, status: "TICKETED", ticketNumbers |
Every webhook carries the header:
X-GetMyTrip-Signature: sha256=<hmac-of-raw-body>
Verify this signature against the raw request body before trusting the payload.
4.7 Sandbox bookings
With SANDBOX credentials, POST /flights/orders does not contact the airline. It applies the same checks as production (offer token, traveller count and types, names, date of birth, gender, contact email and phone), then returns a simulated booking:
{
"success": true,
"message": "Flight booked successfully (sandbox simulation, no airline reservation was made)",
"data": {
"orderId": "…",
"recordLocator": "K7PQ2M",
"pnr": "K7PQ2M",
"status": "CONFIRMED",
"totalPrice": 902068.65,
"currency": "NGN",
"autoCancelAt": "2026-11-02T10:15:00.000Z",
"sandbox": true
}
}
- Sandbox responses and webhooks include
"sandbox": true. Production responses never do. Never show a sandbox PNR to a real traveller. GET /flights/ordersandGET /flights/orders/{orderId}with sandbox credentials return only sandbox orders. Production credentials never see them.- Sandbox orders are kept for 30 days.
autoCancelAtis always filled in for open sandbox orders, so you can build against it. Sandbox orders are never cancelled automatically; use the simulate endpoint below.
Test failure paths by sending an optional header on the book request:
X-GetMyTrip-Sandbox-Scenario | Result |
|---|---|
success (default) | 201, status CONFIRMED with a PNR |
pending | 201, status PENDING, pnr: null (the airline accepted the booking but has not returned a PNR yet) |
supplier_error | 502, the airline did not respond. Nothing is stored |
Simulate what happens after booking (sandbox credentials only, scope flights.book):
POST /api/v1/partner/sandbox/orders/{orderId}/simulate
Authorization: Bearer <token>
Content-Type: application/json
{ "event": "ticket.issued" }
event | Effect |
|---|---|
ticket.issued | Order becomes TICKETED with test ticket numbers; ticket.issued webhook is sent |
booking.cancelled | Order becomes EXPIRED, exactly as when a booking passes autoCancelAt; booking.cancelled webhook is sent |
The response returns the updated order plus a webhook object (delivered, responseCode, error) so you can confirm your endpoint received it. Orders that are already TICKETED or EXPIRED return 409.
5. cURL walkthrough
Replace BASE, CLIENT_ID, and CLIENT_SECRET with the values GetMyTrip sent you.
BASE=https://<base-url>/api/v1
CLIENT_ID=YOUR_CLIENT_ID
CLIENT_SECRET=YOUR_CLIENT_SECRET
# 1) Token
TOKEN=$(curl -s -X POST "$BASE/partner/oauth/token" \
-H "Content-Type: application/json" \
-d "{\"grant_type\":\"client_credentials\",\"client_id\":\"$CLIENT_ID\",\"client_secret\":\"$CLIENT_SECRET\"}" \
| jq -r .access_token)
# 2) Search
curl -s "$BASE/partner/flights/search?originLocationCode=LOS&destinationLocationCode=ACC&departureDate=2026-11-20&adults=1" \
-H "Authorization: Bearer $TOKEN" | jq '.data[0] | {id, price, offerToken}'
# 3) Price confirm (set OFFER_TOKEN from search)
curl -s -X POST "$BASE/partner/flights/price-confirm" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"offerToken\":\"$OFFER_TOKEN\"}" | jq '.data | {offerToken, totalPrice, currency}'
# 4) Book (set BOOK_TOKEN from price-confirm). Simulated with SANDBOX keys, a real reservation with PRODUCTION keys
curl -s -X POST "$BASE/partner/flights/orders" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"offerToken\": \"$BOOK_TOKEN\",
\"travelers\": [{
\"id\": \"1\",
\"dateOfBirth\": \"1990-05-15\",
\"gender\": \"MALE\",
\"name\": { \"firstName\": \"ADE\", \"lastName\": \"OKAFOR\" }
}],
\"contacts\": [{
\"addresseeName\": { \"firstName\": \"ADE\", \"lastName\": \"OKAFOR\" },
\"emailAddress\": \"partner-test@example.com\",
\"phones\": [{ \"deviceType\": \"MOBILE\", \"countryCallingCode\": \"234\", \"number\": \"8012345678\" }]
}]
}" | jq .
6. Recommended architecture
- Store secrets on your server (environment variables or a secrets manager), never in a mobile app or frontend.
- Your backend calls GetMyTrip for the token and for search, price-confirm and book.
- Your UI only talks to your backend and renders the sanitized offers (
price, itineraries,offerToken). - When the user clicks "Continue", your backend price-confirms; on "Pay/Book" it creates the order and shows
pnr/orderId. - Optionally receive
booking.confirmedwebhooks to update your ledger.
[Your App / Website]
│
▼
[Your Backend] --OAuth + Flight APIs--> [GetMyTrip Partner API]
│
▼
Airline / GDS inventory
7. Errors & limits
| HTTP | Meaning |
|---|---|
| 401 | Missing/invalid token or bad client credentials |
| 403 | Partner suspended, wrong scope, or production key before go-live |
| 400 | Validation / expired offerToken / price failure |
| 409 | The airline could not confirm the fare at price-confirm (search again), or a sandbox order is already closed |
| 429 | Rate limit (default ~60 requests/min per partner; the token endpoint allows 30 requests per 15 minutes) |
| 502 | The airline did not respond to a booking. Check GET /flights/orders before retrying |
Expired offerToken: search (and price-confirm) again.
Service health: GET /api/v1/partner/health (no token needed) returns 200 when the Partner API is healthy and 503 when it is not. Limit: 60 requests per minute per IP.
8. Not in Partner API v1 (yet)
- Cancel / modify / ticket issue
- PNR lookup by record locator
- Seat maps / branded fares
For these, contact GetMyTrip operations or request an API expansion.
9. Go-live checklist
- Token + search works from your staging environment
- Price-confirm handles
priceChanged - Book returns
orderId+pnr - Retrieve by
orderIdworks - Webhook (if used) verifies the HMAC signature
- Unticketed bookings past
autoCancelAt(statusEXPIRED) are handled in your ledger - In sandbox:
pendingandsupplier_errorscenarios handled;ticket.issuedandbooking.cancelledsimulated and received - Your code never shows orders marked
"sandbox": trueto real travellers - Commercial terms signed
- Production
client_id/client_secretreceived from GetMyTrip
10. Support
Email support@getmytrip.com with the subject [PARTNER API] followed by your company name. Include your client_id (never your client_secret), the endpoint, the time of the request and any orderId.