Skip to content

Authentication

The Sherpai Partner API uses JWT (JSON Web Token) bearer tokens to secure API endpoints. This guide provides comprehensive information about authentication, including how to obtain credentials, authenticate requests, and handle authentication errors.

Overview

All API endpoints except /health and /auth/login require authentication. You authenticate by including a valid JWT token in the Authorization header of your requests.

Authorization: Bearer YOUR_JWT_TOKEN

Authentication Flow

The authentication process follows these steps:

  1. Obtain API Credentials: Get your API client key and secret key from your Sherpai partner representative
  2. Request Access Token: Exchange your credentials for a JWT access token via /auth/login
  3. Use Access Token: Include the token in the Authorization header for all protected endpoints
  4. Handle Token Expiration: Re-authenticate when tokens expire (after 1 hour)

Getting Started

Step 1: Set up API Client Credentials

You'll need API client credentials to access the API. Contact your Sherpai partner representative to get:

  • API Client Key: Identifier for your account
  • API Secret Key: Secret for authentication
  • Production access details

Step 2: Obtain Access Token

Use the /auth/login endpoint to exchange your API client credentials for an access token:

curl -X POST https://partner-api.sherp.ai/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "api_client_key": "your-client-key",
    "api_secret_key": "your-secret-key"
  }'

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600,
  "user_id": "user_uid"
}

Step 3: Use the Access Token

Include the access token in the Authorization header for all subsequent requests:

curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  https://partner-api.sherp.ai/me

Token Management

Token Expiration

JWT tokens expire after 1 hour. When a token expires, you'll receive a 401 Unauthorized response. You'll need to re-authenticate to get a new token.

Security Best Practices

  • Never expose tokens: Don't include tokens in URLs, logs, or client-side code
  • Store securely: Use secure storage mechanisms for production applications
  • Rotate regularly: Implement token refresh logic in your applications
  • Use HTTPS: Always use HTTPS to prevent token interception

Error Responses

The API returns detailed error responses for authentication failures. All authentication errors return a 401 Unauthorized status code with a structured error response.

Standard Error Response Format

All authentication errors follow this format:

{
  "detail": "error message",
  "error_code": "ERROR_CODE",
  "status_code": 401,
  "status": "error",
  "timestamp": "2024-01-01T12:00:00Z",
  "request_id": "abc123-def456-ghi789"
}

Common Authentication Errors

Missing Token

Request:

curl https://partner-api.sherp.ai/me

Response:

{
  "detail": "Authentication required. Please provide a valid Bearer token in the Authorization header.",
  "error_code": "INVALID_CREDENTIALS",
  "status_code": 401,
  "status": "error",
  "timestamp": "2024-01-01T12:00:00Z",
  "request_id": "d90abe92-da54-4e51-afe5-5e256ba94858"
}

Solution: Include the Authorization: Bearer <token> header in your request.

Invalid Token Format

Request:

curl -H "Authorization: Bearer invalid_token" \
  https://partner-api.sherp.ai/me

Response:

{
  "detail": "Authentication error: Invalid authentication token format. Please check your credentials.",
  "error_code": "TOKEN_INVALID",
  "status_code": 401,
  "status": "error",
  "timestamp": "2024-01-01T12:00:00Z",
  "request_id": "af9dd27e-2b22-4f82-b354-098d275e89b9"
}

Solution: Ensure your token is a valid JWT token obtained from /auth/login.

Expired Token

Response:

{
  "detail": "Your session has expired. Please log in again.",
  "error_code": "TOKEN_EXPIRED",
  "status_code": 401,
  "status": "error",
  "timestamp": "2024-01-01T12:00:00Z",
  "request_id": "bbc01148-4ded-4bc2-8b66-ff9d21977c59"
}

Solution: Re-authenticate using /auth/login to get a new access token.

Invalid API Credentials

Request:

curl -X POST https://partner-api.sherp.ai/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "api_client_key": "invalid-key",
    "api_secret_key": "invalid-secret"
  }'

Response:

{
  "detail": "Invalid API client credentials",
  "error_code": "INVALID_CREDENTIALS",
  "status_code": 401,
  "status": "error",
  "timestamp": "2024-01-01T12:00:00Z",
  "request_id": "xyz789-abc123-def456"
}

Solution: Verify your API client key and secret key are correct. Contact your partner representative if you need new credentials.

Invalid API Client Key Format

Response:

{
  "detail": "Invalid API client key format. Please check your client key.",
  "error_code": "INVALID_CREDENTIALS",
  "status_code": 401,
  "status": "error",
  "timestamp": "2024-01-01T12:00:00Z",
  "request_id": "xyz789-abc123-def456"
}

Invalid API Secret Key Format

Response:

{
  "detail": "Invalid API secret key format. Please check your secret key.",
  "error_code": "INVALID_CREDENTIALS",
  "status_code": 401,
  "status": "error",
  "timestamp": "2024-01-01T12:00:00Z",
  "request_id": "xyz789-abc123-def456"
}

Error Code Reference

Error Code Description Solution
INVALID_CREDENTIALS Missing or invalid credentials Provide valid API client key and secret key
TOKEN_INVALID Token format is invalid Re-authenticate to get a new token
TOKEN_EXPIRED Token has expired Re-authenticate to get a new token
USER_DISABLED User account is disabled Contact support
TOO_MANY_ATTEMPTS Too many failed login attempts Wait before retrying

Token Lifetime & Refresh Strategy

Situation Error Code/Status Recommended Handling
Expired token TOKEN_EXPIRED + 401 Re-authenticate with /auth/login and retry once
Invalid token format TOKEN_INVALID + 401 Log error, do not retry, surface to operator
Invalid credentials INVALID_CREDENTIALS + 401 Escalate to admin / Sherpai support
Too many attempts TOO_MANY_ATTEMPTS + 401 Wait before retrying, implement exponential backoff

Secret Management

Never Commit Secrets

Never commit API credentials, access tokens, or secrets to version control systems.

Best Practices:

  1. Use Environment Variables

    export API_CLIENT_KEY="your-client-key"
    export API_SECRET_KEY="your-secret-key"
    

  2. Use Secret Management Services

  3. AWS Secrets Manager
  4. GCP Secret Manager
  5. HashiCorp Vault
  6. Kubernetes Secrets

  7. Never Type Secrets in Shell

    # ❌ Bad - Secrets visible in shell history
    curl -d '{"api_client_key":"secret"}' ...
    
    # ✅ Good - Use environment variables
    curl -d "{\"api_client_key\":\"$API_CLIENT_KEY\"}" ...
    

  8. Rotate Credentials Regularly

  9. Rotate every 90 days (recommended)
  10. Rotate immediately if compromised

Complete security guide →

Code Examples

JavaScript/Node.js

Basic Authentication

async function authenticate(apiClientKey, apiSecretKey) {
  const response = await fetch('https://partner-api.sherp.ai/auth/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      api_client_key: apiClientKey,
      api_secret_key: apiSecretKey
    })
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Authentication failed: ${error.detail}`);
  }

  const data = await response.json();
  return data.access_token;
}

// Usage
const accessToken = await authenticate('your-client-key', 'your-secret-key');

// Use the token for authenticated requests
const userResponse = await fetch('https://partner-api.sherp.ai/me', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
});

const user = await userResponse.json();
console.log('User:', user);

Complete Client with Token Management

class SherpAiClient {
  constructor(apiClientKey, apiSecretKey) {
    this.apiClientKey = apiClientKey;
    this.apiSecretKey = apiSecretKey;
    this.accessToken = null;
    this.tokenExpiry = null;
    this.baseURL = 'https://partner-api.sherp.ai';
  }

  async authenticate() {
    const response = await fetch(`${this.baseURL}/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        api_client_key: this.apiClientKey,
        api_secret_key: this.apiSecretKey
      })
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`Authentication failed: ${error.detail}`);
    }

    const data = await response.json();
    this.accessToken = data.access_token;
    this.tokenExpiry = Date.now() + (data.expires_in * 1000);
    return this.accessToken;
  }

  async ensureAuthenticated() {
    if (!this.accessToken || Date.now() >= this.tokenExpiry) {
      await this.authenticate();
    }
    return this.accessToken;
  }

  async request(endpoint, options = {}) {
    const token = await this.ensureAuthenticated();
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      ...options,
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
        ...options.headers
      }
    });

    if (response.status === 401) {
      // Token expired, re-authenticate and retry
      await this.authenticate();
      return this.request(endpoint, options);
    }

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`API Error ${response.status}: ${error.detail}`);
    }

    return response.json();
  }
}

// Usage
const client = new SherpAiClient('your-client-key', 'your-secret-key');
const user = await client.request('/me');
const posts = await client.request('/posts?limit=10');

Python

Basic Authentication

import requests
from typing import Optional, Dict, Any

def authenticate(api_client_key: str, api_secret_key: str) -> str:
    """Authenticate and get access token."""
    response = requests.post(
        'https://partner-api.sherp.ai/auth/login',
        json={
            'api_client_key': api_client_key,
            'api_secret_key': api_secret_key
        }
    )

    response.raise_for_status()
    data = response.json()
    return data['access_token']

# Usage
access_token = authenticate('your-client-key', 'your-secret-key')

# Use token for authenticated requests
headers = {'Authorization': f'Bearer {access_token}'}
user_response = requests.get('https://partner-api.sherp.ai/me', headers=headers)
user = user_response.json()
print(f"User: {user}")

Complete Client with Token Management

import requests
import time
from typing import Optional, Dict, Any
from datetime import datetime, timedelta

class SherpAiClient:
    def __init__(self, api_client_key: str, api_secret_key: str):
        self.api_client_key = api_client_key
        self.api_secret_key = api_secret_key
        self.access_token: Optional[str] = None
        self.token_expiry: Optional[datetime] = None
        self.base_url = 'https://partner-api.sherp.ai'
        self.session = requests.Session()

    def authenticate(self) -> str:
        """Authenticate and get access token."""
        response = requests.post(
            f'{self.base_url}/auth/login',
            json={
                'api_client_key': self.api_client_key,
                'api_secret_key': self.api_secret_key
            }
        )

        response.raise_for_status()
        data = response.json()
        self.access_token = data['access_token']
        self.token_expiry = datetime.now() + timedelta(seconds=data['expires_in'])
        return self.access_token

    def ensure_authenticated(self) -> str:
        """Ensure we have a valid access token."""
        if not self.access_token or datetime.now() >= self.token_expiry:
            self.authenticate()
        return self.access_token

    def request(self, endpoint: str, method: str = 'GET', **kwargs) -> Dict[str, Any]:
        """Make an authenticated API request."""
        token = self.ensure_authenticated()
        headers = {
            'Authorization': f'Bearer {token}',
            'Content-Type': 'application/json',
            **kwargs.pop('headers', {})
        }

        response = self.session.request(
            method,
            f'{self.base_url}{endpoint}',
            headers=headers,
            **kwargs
        )

        if response.status_code == 401:
            # Token expired, re-authenticate and retry
            self.authenticate()
            return self.request(endpoint, method, **kwargs)

        response.raise_for_status()
        return response.json()

# Usage
client = SherpAiClient('your-client-key', 'your-secret-key')
user = client.request('/me')
posts = client.request('/posts', params={'limit': 10})

cURL

Basic Authentication

# Get access token
ACCESS_TOKEN=$(curl -s -X POST https://partner-api.sherp.ai/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "api_client_key": "your-client-key",
    "api_secret_key": "your-secret-key"
  }' | jq -r '.access_token')

# Use token for authenticated requests
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
  https://partner-api.sherp.ai/me

Shell Script with Error Handling

#!/bin/bash

API_BASE="https://partner-api.sherp.ai"
API_CLIENT_KEY="your-client-key"
API_SECRET_KEY="your-secret-key"

# Authenticate
AUTH_RESPONSE=$(curl -s -X POST "${API_BASE}/auth/login" \
  -H "Content-Type: application/json" \
  -d "{
    \"api_client_key\": \"${API_CLIENT_KEY}\",
    \"api_secret_key\": \"${API_SECRET_KEY}\"
  }")

# Check for errors
if echo "$AUTH_RESPONSE" | jq -e '.error_code' > /dev/null 2>&1; then
  echo "Authentication failed: $(echo "$AUTH_RESPONSE" | jq -r '.detail')"
  exit 1
fi

# Extract token
ACCESS_TOKEN=$(echo "$AUTH_RESPONSE" | jq -r '.access_token')

if [ "$ACCESS_TOKEN" == "null" ] || [ -z "$ACCESS_TOKEN" ]; then
  echo "Failed to get access token"
  exit 1
fi

echo "Authentication successful"
echo "Token expires in: $(echo "$AUTH_RESPONSE" | jq -r '.expires_in') seconds"

# Make authenticated request
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  "${API_BASE}/me"

Next Steps