From navan-pack
Sets up OAuth 2.0 client credentials for Navan REST API: dashboard creation, .env storage, Node.js/TypeScript or Python token exchange code. Use for new integrations or credential rotation.
How this skill is triggered — by the user, by Claude, or both
Slash command
/navan-pack:navan-install-authThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Configure OAuth 2.0 client credentials for the Navan REST API. Navan has **no public SDK** — all API access uses raw REST calls with bearer tokens obtained via the client_credentials grant.
Configure OAuth 2.0 client credentials for the Navan REST API. Navan has no public SDK — all API access uses raw REST calls with bearer tokens obtained via the client_credentials grant.
Purpose: Obtain a working OAuth 2.0 bearer token for calling Navan API endpoints.
.env-aware project (dotenv for Node, python-dotenv for Python)Navigate to: Admin > Travel admin > Settings > Integrations > Navan API Credentials > Create New
Save the client_id and client_secret immediately — credentials are only viewable once. If lost, you must revoke and regenerate.
Create a .env file in your project root:
# .env — NEVER commit this file
NAVAN_CLIENT_ID="your-client-id-here"
NAVAN_CLIENT_SECRET="your-client-secret-here"
NAVAN_BASE_URL="https://api.navan.com"
Ensure .env is in your .gitignore:
echo ".env" >> .gitignore
Install dependencies and implement the OAuth 2.0 client credentials flow:
npm install dotenv
import 'dotenv/config';
interface TokenResponse {
access_token: string;
token_type: string;
expires_in: number;
}
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) {
const error = await response.text();
throw new Error(`Auth failed (${response.status}): ${error}`);
}
const data: TokenResponse = await response.json();
return data.access_token;
}
// Verify connection
const token = await getNavanToken();
console.log('Auth successful — token acquired');
pip install requests python-dotenv
import os
import requests
from dotenv import load_dotenv
load_dotenv()
def get_navan_token() -> str:
"""Exchange client credentials for an OAuth 2.0 bearer token."""
response = requests.post(
f"{os.environ['NAVAN_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"],
},
)
response.raise_for_status()
return response.json()["access_token"]
token = get_navan_token()
print("Auth successful — token acquired")
Make an authenticated API call to confirm credentials work:
const bookings = await fetch(`${process.env.NAVAN_BASE_URL}/v1/bookings?page=0&size=50`, {
headers: { Authorization: `Bearer ${token}` },
});
if (bookings.ok) {
const { data } = await bookings.json();
console.log(`Connection verified — retrieved ${data.length} bookings`);
} else {
console.error(`Verification failed: ${bookings.status}`);
}
Successful completion produces:
client_id and client_secret stored in .envgetNavanToken() function (TypeScript or Python) returning a bearer token| Error | Code | Cause | Solution |
|---|---|---|---|
| Invalid credentials | 401 | Wrong client_id or client_secret | Regenerate credentials in Admin > Integrations |
| Insufficient permissions | 403 | Account lacks API access or wrong tier | Contact Navan support to enable API access |
| Rate limited | 429 | Too many auth requests | Implement token caching (see navan-local-dev-loop) |
| Endpoint not found | 404 | Wrong base URL or path | Verify NAVAN_BASE_URL is https://api.navan.com |
| Server error | 500 | Navan service issue | Retry after 30 seconds; check Navan status page |
| Service unavailable | 503 | Navan maintenance window | Wait and retry; check for scheduled maintenance |
Minimal auth check script:
# Quick credential test with curl
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; d=json.load(sys.stdin); print('OK' if 'access_token' in d else 'FAIL')"
After authentication is working, proceed to navan-hello-world to make your first API call, or see navan-sdk-patterns to build a reusable typed wrapper.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin navan-packBuilds typed TypeScript NavanAPI client for REST endpoints with OAuth2 token refresh, retries, typed responses, and error handling. For production Navan travel SaaS integrations.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.