Agent Skills: Navan CI Integration

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/navan-ci-integration

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/navan-pack/skills/navan-ci-integration

Skill Files

Browse the full folder contents for navan-ci-integration.

Download Skill

Loading file tree…

plugins/saas-packs/navan-pack/skills/navan-ci-integration/SKILL.md

Skill Metadata

Name
navan-ci-integration
Description
|

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_ID, NAVAN_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:

  • NAVAN_CLIENT_ID — from Navan Admin > API Settings
  • NAVAN_CLIENT_SECRET — from Navan Admin > API Settings

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 | |-----------|---------|-----------| | 200 | Success | Continue | | 401 | Invalid or expired OAuth token | Fail build, alert on credential rotation | | 403 | Insufficient API scopes | Fail build, check OAuth app permissions | | 404 | Endpoint not found (API version change) | Fail build, review API changelog | | 429 | Rate limit exceeded | Retry with exponential backoff (max 3 attempts) | | 500-503 | 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

Next Steps

  • Add navan-deploy-integration for production deployment patterns
  • Add navan-observability for runtime monitoring of the endpoints validated here
  • See navan-rate-limits to configure retry policies in CI