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.
Authentication Flow¶
The authentication process follows these steps:
- Obtain API Credentials: Get your API client key and secret key from your Sherpai partner representative
- Request Access Token: Exchange your credentials for a JWT access token via
/auth/login - Use Access Token: Include the token in the
Authorizationheader for all protected endpoints - 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:
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:
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:
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:
-
Use Environment Variables
-
Use Secret Management Services
- AWS Secrets Manager
- GCP Secret Manager
- HashiCorp Vault
-
Kubernetes Secrets
-
Never Type Secrets in Shell
-
Rotate Credentials Regularly
- Rotate every 90 days (recommended)
- Rotate immediately if compromised
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"