Claude-code-plugins navan-hello-world

install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/navan-pack/skills/navan-hello-world" ~/.claude/skills/jeremylongshore-claude-code-plugins-navan-hello-world && rm -rf "$T"
manifest: plugins/saas-packs/navan-pack/skills/navan-hello-world/SKILL.md
source content

Navan Hello World

Overview

Execute a first API call against the Navan REST API to retrieve trip data. All examples use raw REST calls — Navan has no public SDK.

Purpose: Confirm end-to-end integration by retrieving real trip data and parsing

uuid
primary keys.

Prerequisites

  • Completed
    navan-install-auth
    with working OAuth 2.0 credentials
  • .env
    file with
    NAVAN_CLIENT_ID
    ,
    NAVAN_CLIENT_SECRET
    , and
    NAVAN_BASE_URL
  • Node.js 18+ (for TypeScript) or Python 3.8+ (for Python)
  • At least one trip or user in your Navan organization

Instructions

Step 1: Acquire a Bearer Token

Reuse the token exchange from

navan-install-auth
:

import 'dotenv/config';

async function getNavanToken(): Promise<string> {
  const response = 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!,
    }),
  });
  if (!response.ok) throw new Error(`Auth failed: ${response.status}`);
  const data = await response.json();
  return data.access_token;
}

Step 2: Retrieve Bookings (TypeScript)

Call

GET /v1/bookings
to fetch booking records (paginated with
page
+
size
):

interface NavanBooking {
  uuid: string;           // Primary key for all booking records
  traveler_name: string;
  origin: string;
  destination: string;
  departure_date: string;
  return_date: string;
  booking_status: string;
  booking_type: string;   // "flight", "hotel", "car"
}

async function getBookings(token: string): Promise<NavanBooking[]> {
  const response = await fetch(
    `${process.env.NAVAN_BASE_URL}/v1/bookings?page=0&size=50`,
    {
      headers: { Authorization: `Bearer ${token}` },
    }
  );

  if (!response.ok) {
    throw new Error(`GET /v1/bookings failed: ${response.status} ${response.statusText}`);
  }

  const { data } = await response.json(); // records in .data array
  return data ?? [];
}

// Execute
const token = await getNavanToken();
const bookings = await getBookings(token);
console.log(`Retrieved ${bookings.length} bookings:`);
bookings.forEach((b) =>
  console.log(`  [${b.uuid}] ${b.origin} -> ${b.destination} (${b.booking_status})`)
);

Step 3: Retrieve Bookings (Python)

import os
import requests
from dotenv import load_dotenv

load_dotenv()

def get_navan_token() -> str:
    resp = requests.post(
        f"{os.environ.get('NAVAN_BASE_URL', 'https://api.navan.com')}/ta-auth/oauth/token",
        data={
            "grant_type": "client_credentials",
            "client_id": os.environ["NAVAN_CLIENT_ID"],
            "client_secret": os.environ["NAVAN_CLIENT_SECRET"],
        },
    )
    resp.raise_for_status()
    return resp.json()["access_token"]

def get_bookings(token: str) -> list[dict]:
    base_url = os.environ.get('NAVAN_BASE_URL', 'https://api.navan.com')
    resp = requests.get(
        f"{base_url}/v1/bookings",
        params={"page": 0, "size": 50},
        headers={"Authorization": f"Bearer {token}"},
    )
    resp.raise_for_status()
    return resp.json()["data"]  # records in .data array

token = get_navan_token()
bookings = get_bookings(token)
print(f"Retrieved {len(bookings)} bookings:")
for b in bookings:
    print(f"  [{b['uuid']}] {b.get('origin')} -> {b.get('destination')}")

Step 4: Paginate and Filter Bookings

Once the basic call works, use pagination and date filtering:

// Paginate: page starts at 0, size controls page size
const page2 = await fetch(
  `${process.env.NAVAN_BASE_URL}/v1/bookings?page=1&size=50`,
  { headers: { Authorization: `Bearer ${token}` } }
);

// Filter by creation date range (incremental params)
const filtered = await fetch(
  `${process.env.NAVAN_BASE_URL}/v1/bookings?createdFrom=2026-01-01&createdTo=2026-03-31&page=0&size=50`,
  { headers: { Authorization: `Bearer ${token}` } }
);

Step 5: Understand the Response Shape

Navan API responses use

uuid
as the primary key for booking records. Key fields to expect:

FieldTypeDescription
uuid
stringUnique booking identifier (primary key)
traveler_name
stringFull name of the traveler
booking_type
string"flight", "hotel", or "car"
booking_status
stringCurrent status of the booking
origin
/
destination
stringAirport codes or city names

Output

Successful completion produces:

  • A working API call retrieving real booking data from the Navan organization
  • Parsed response structure with records in
    .data
    array and
    uuid
    primary key
  • Tested pagination (
    page
    +
    size
    ) and date filtering (
    createdFrom
    /
    createdTo
    )

Error Handling

ErrorCodeCauseSolution
Unauthorized401Expired or invalid OAuth tokenRe-run token exchange; check credentials
Forbidden403Insufficient permissions or wrong tierVerify admin role; contact Navan support
Not found404Invalid endpoint pathCheck spelling; use exact paths from this guide
Rate limited429Too many requestsWait and retry with exponential backoff
Server error500Navan service issueRetry after 30s; check Navan status
Maintenance503Scheduled or unscheduled downtimeWait and retry; check for maintenance windows

Examples

Quick curl test from terminal:

# Get token and fetch bookings in one pipeline
TOKEN=$(curl -s -X POST https://api.navan.com/ta-auth/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=$NAVAN_CLIENT_ID&client_secret=$NAVAN_CLIENT_SECRET" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

curl -s "https://api.navan.com/v1/bookings?page=0&size=5" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

Resources

Next Steps

Now that your first API call works, proceed to

navan-sdk-patterns
to build a typed wrapper class, or see
navan-local-dev-loop
for a structured development environment.