Navan — Travel Booking & Management
Overview
This skill provides the complete travel booking workflow through the Navan REST API. Navan has no public SDK — all access is via raw HTTP calls using OAuth 2.0 bearer tokens. This skill covers trip retrieval for both user and admin scopes, itinerary PDF downloads, invoice access, and trip filtering by date range and status. Every booking is keyed by a UUID primary key that must be tracked for deduplication and updates.
Prerequisites
- Navan account with API credentials (client_id + client_secret)
- Credentials created in Admin > Travel admin > Settings > Integrations > Navan API Credentials
- OAuth 2.0 token obtained via POST
/ta-auth/oauth/token (see navan-install-auth)
- Node.js 18+ with
node-fetch or Python 3.8+ with requests
- Environment variables:
NAVAN_CLIENT_ID, NAVAN_CLIENT_SECRET, NAVAN_BASE_URL
NAVAN_BASE_URL should be set to https://api.navan.com
Instructions
Step 1: Authenticate and Obtain Bearer Token
const tokenRes = await fetch(`${process.env.NAVAN_BASE_URL}/ta-auth/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.NAVAN_CLIENT_ID!,
client_secret: process.env.NAVAN_CLIENT_SECRET!,
}),
});
const { access_token } = await tokenRes.json();
const headers = { Authorization: `Bearer ${access_token}` };
Step 2: Retrieve Bookings
const bookingsRes = await fetch(
`${process.env.NAVAN_BASE_URL}/v1/bookings?page=0&size=50`,
{ headers }
);
const { data: bookings } = await bookingsRes.json();
bookings.forEach((booking: any) => {
console.log(`UUID: ${booking.uuid}`);
console.log(` Route: ${booking.origin} -> ${booking.destination}`);
console.log(` Status: ${booking.status}`);
console.log(` Dates: ${booking.start_date} to ${booking.end_date}`);
});
Step 3: Retrieve Bookings with Date Filtering
const filteredRes = await fetch(
`${process.env.NAVAN_BASE_URL}/v1/bookings?createdFrom=2026-01-01&createdTo=2026-03-31&page=0&size=50`,
{ headers }
);
const { data: filteredBookings } = await filteredRes.json();
console.log(`Total bookings in range: ${filteredBookings.length}`);
Step 4: Paginate Through All Bookings
async function getAllBookings(): Promise<any[]> {
const allBookings: any[] = [];
let page = 0;
const size = 50;
while (true) {
const res = await fetch(
`${process.env.NAVAN_BASE_URL}/v1/bookings?page=${page}&size=${size}`,
{ headers }
);
const { data } = await res.json();
if (!data || data.length === 0) break;
allBookings.push(...data);
if (data.length < size) break;
page++;
}
return allBookings;
}
const allBookings = await getAllBookings();
console.log(`Total bookings: ${allBookings.length}`);
Step 5: Token Refresh
const refreshRes = await fetch(`${process.env.NAVAN_BASE_URL}/ta-auth/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.NAVAN_CLIENT_ID!,
client_secret: process.env.NAVAN_CLIENT_SECRET!,
}),
});
const refreshed = await refreshRes.json();
console.log('Token refreshed successfully');
Output
Successful execution returns:
- Booking objects in
.data array with UUID primary keys, origin/destination, dates, and status
- Paginated results using
page + size query params
- Incremental filtering via
createdFrom / createdTo params
Error Handling
| Error | HTTP Code | Cause | Solution |
|---|
| Unauthorized | 401 | Expired or invalid bearer token | Re-authenticate via POST /ta-auth/oauth/token |
| Forbidden | 403 | Insufficient API scope | Verify credentials have correct permissions |
| Not Found | 404 | Invalid endpoint or UUID | Confirm endpoint path starts with /v1/ |
| Rate Limited | 429 | Too many requests | Implement exponential backoff (start at 1s) |
| Server Error | 500 | Navan platform issue | Retry with backoff; check Navan status page |
Examples
Python — Retrieve trips with date filtering:
import requests
import os
base_url = os.environ.get('NAVAN_BASE_URL', 'https://api.navan.com')
auth = requests.post(f'{base_url}/ta-auth/oauth/token', data={
'grant_type': 'client_credentials',
'client_id': os.environ['NAVAN_CLIENT_ID'],
'client_secret': os.environ['NAVAN_CLIENT_SECRET'],
})
token = auth.json()['access_token']
headers = {'Authorization': f'Bearer {token}'}
resp = requests.get(
f'{base_url}/v1/bookings',
params={'createdFrom': '2026-01-01', 'createdTo': '2026-03-31', 'page': 0, 'size': 50},
headers=headers,
).json()
for booking in resp['data']:
print(f"{booking['uuid']}: {booking.get('origin')} -> {booking.get('destination')}")
Resources
Next Steps
After retrieving trip data, proceed to navan-core-workflow-b for expense management or navan-data-handling for bulk data extraction patterns.