Claude-code-plugins navan-ci-integration
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-ci-integration" ~/.claude/skills/jeremylongshore-claude-code-plugins-navan-ci-integration && rm -rf "$T"
manifest:
plugins/saas-packs/navan-pack/skills/navan-ci-integration/SKILL.mdsource content
Navan CI Integration
Overview
Navan has no SDK — all CI integration uses raw REST calls against
https://api.navan.com with OAuth 2.0 client_credentials authentication. This skill generates GitHub Actions workflows that validate your Navan integration on every push: token health checks, booking data schema validation, and travel policy compliance reports. Secrets (client_id, client_secret) are stored in GitHub Actions secrets, never in code.
Prerequisites
- Navan Admin access to create OAuth 2.0 application credentials (Admin > API Settings)
- GitHub repo with Actions enabled
- GitHub Secrets configured:
,NAVAN_CLIENT_IDNAVAN_CLIENT_SECRET - Navan API base URL:
https://api.navan.com
Instructions
Step 1 — Store OAuth Credentials in GitHub Secrets
Navigate to your GitHub repo > Settings > Secrets and variables > Actions. Add:
— from Navan Admin > API SettingsNAVAN_CLIENT_ID
— from Navan Admin > API SettingsNAVAN_CLIENT_SECRET
Step 2 — Create the CI Workflow
# .github/workflows/navan-integration-check.yml name: Navan Integration Health Check on: push: branches: [main] pull_request: schedule: - cron: '0 6 * * 1' # Weekly Monday 6am UTC jobs: navan-health: runs-on: ubuntu-latest env: NAVAN_BASE_URL: https://api.navan.com steps: - uses: actions/checkout@v4 - name: Authenticate with Navan OAuth 2.0 id: auth run: | TOKEN_RESPONSE=$(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" \ -d "client_id=${{ secrets.NAVAN_CLIENT_ID }}" \ -d "client_secret=${{ secrets.NAVAN_CLIENT_SECRET }}") ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token') if [ "$ACCESS_TOKEN" = "null" ] || [ -z "$ACCESS_TOKEN" ]; then echo "::error::OAuth authentication failed" echo "$TOKEN_RESPONSE" | jq . exit 1 fi echo "::add-mask::$ACCESS_TOKEN" echo "token=$ACCESS_TOKEN" >> "$GITHUB_OUTPUT" - name: API Health Check — Fetch Bookings run: | HTTP_CODE=$(curl -s -o /tmp/bookings.json -w "%{http_code}" \ "$NAVAN_BASE_URL/v1/bookings?page=0&size=5" \ -H "Authorization: Bearer ${{ steps.auth.outputs.token }}") echo "Health check status: $HTTP_CODE" if [ "$HTTP_CODE" != "200" ]; then echo "::error::API health check failed with HTTP $HTTP_CODE" cat /tmp/bookings.json exit 1 fi - name: Validate Booking Data Schema run: | # Response structure: records in .data array, primary key uuid REQUIRED_FIELDS='["uuid","traveler","status","created_at"]' echo "$REQUIRED_FIELDS" | jq -r '.[]' | while read field; do if ! jq -e ".data[0].$field" /tmp/bookings.json > /dev/null 2>&1; then echo "::warning::Missing expected field: $field" fi done - name: Generate Compliance Report run: | curl -s "$NAVAN_BASE_URL/v1/bookings?page=0&size=50" \ -H "Authorization: Bearer ${{ steps.auth.outputs.token }}" \ -o /tmp/compliance.json echo "## Navan Compliance Report" >> "$GITHUB_STEP_SUMMARY" jq -r '"| Metric | Value |\n|--------|-------|\n| Total Bookings | \(.total_bookings) |\n| In Policy | \(.in_policy) |\n| Out of Policy | \(.out_of_policy) |"' \ /tmp/compliance.json >> "$GITHUB_STEP_SUMMARY" 2>/dev/null || echo "Report data unavailable" >> "$GITHUB_STEP_SUMMARY"
Step 3 — Add Integration Test Script
#!/usr/bin/env bash # scripts/navan-smoke-test.sh — Run locally or in CI set -euo pipefail BASE_URL="${NAVAN_BASE_URL:-https://api.navan.com}" # Obtain token TOKEN=$(curl -sf -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}" \ | jq -r '.access_token') # Test endpoints (records returned in .data array) ENDPOINTS=("v1/bookings?page=0&size=1") FAILED=0 for ep in "${ENDPOINTS[@]}"; do CODE=$(curl -s -o /dev/null -w "%{http_code}" \ "$BASE_URL/$ep" -H "Authorization: Bearer $TOKEN") if [ "$CODE" = "200" ]; then echo "PASS: $ep ($CODE)" else echo "FAIL: $ep ($CODE)" FAILED=$((FAILED + 1)) fi done exit $FAILED
Output
The CI workflow produces:
- Pass/fail status on each PR for Navan API connectivity
- GitHub Step Summary with a compliance report table
- Annotations warning about missing booking data fields
- Weekly scheduled runs catching credential expiration before it causes outages
Error Handling
| HTTP Code | Meaning | CI Action |
|---|---|---|
| Success | Continue |
| Invalid or expired OAuth token | Fail build, alert on credential rotation |
| Insufficient API scopes | Fail build, check OAuth app permissions |
| Endpoint not found (API version change) | Fail build, review API changelog |
| Rate limit exceeded | Retry with exponential backoff (max 3 attempts) |
| Navan server error | Warn but do not fail (transient) |
Examples
Parallel endpoint validation with matrix strategy:
jobs: validate-endpoints: runs-on: ubuntu-latest strategy: matrix: endpoint: [bookings, expenses, users, invoices] steps: - name: Check ${{ matrix.endpoint }} run: | CODE=$(curl -s -o /dev/null -w "%{http_code}" \ "https://api.navan.com/v1/${{ matrix.endpoint }}?page=0&size=1" \ -H "Authorization: Bearer $TOKEN") [ "$CODE" = "200" ] || exit 1
Resources
- Navan Help Center — API documentation and guides
- Navan Integrations — Supported third-party connectors
- GitHub Actions Encrypted Secrets
Next Steps
- Add
for production deployment patternsnavan-deploy-integration - Add
for runtime monitoring of the endpoints validated herenavan-observability - See
to configure retry policies in CInavan-rate-limits