Skip to main content

API Conventions

Learn common patterns, best practices, and conventions used across all AuthSec APIs.

Common Patterns

Authentication Header

All API requests require a Bearer token in the Authorization header:

Authorization: Bearer <jwt-token>

Example:

curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
https://dev.api.authsec.dev/uflow/admin/users

Content Type

All request bodies must be JSON with the appropriate header:

Content-Type: application/json

Tenant Identification

Most endpoints require a tenant_id parameter to specify which tenant's data to access:

Query Parameter:

GET /uflow/admin/users?tenant_id=your-tenant-id

Path Parameter:

GET /clientms/tenants/your-tenant-id/clients

Request Body:

{
"tenant_id": "your-tenant-id",
"username": "[email protected]"
}

Response Format

Success Response

Successful requests return appropriate HTTP status codes with JSON bodies:

{
"id": "resource-id",
"field1": "value1",
"field2": "value2",
"created_at": "2026-02-11T19:56:00Z",
"updated_at": "2026-02-11T19:56:00Z"
}

List/Pagination Response

List endpoints return paginated results:

{
"items": [
{"id": "1", "name": "Item 1"},
{"id": "2", "name": "Item 2"}
],
"total": 42,
"page": 1,
"page_size": 10,
"total_pages": 5
}

Pagination Parameters:

  • page (integer): Page number (1-indexed)
  • page_size (integer): Items per page (default: 10, max: 100)
  • offset (integer): Alternative to page, number of items to skip

Example:

curl "https://dev.api.authsec.dev/uflow/admin/users?tenant_id=abc&page=2&page_size=20" \
-H "Authorization: Bearer TOKEN"

Error Handling

Error Response Structure

All error responses follow a consistent format:

{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error description",
"details": {
"field": "Additional context"
}
}
}

HTTP Status Codes

CodeMeaningWhen to Expect
200OKSuccessful GET, PUT, PATCH requests
201CreatedSuccessful POST creating a resource
204No ContentSuccessful DELETE requests
400Bad RequestInvalid request body or parameters
401UnauthorizedMissing or invalid authentication token
403ForbiddenAuthenticated but lacking permissions
404Not FoundResource doesn't exist
409ConflictResource already exists (e.g., duplicate username)
422Unprocessable EntityValidation errors
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error

Common Error Codes

Error CodeHTTP StatusDescription
INVALID_CREDENTIALS401Username/password incorrect
TOKEN_EXPIRED401JWT token has expired
INSUFFICIENT_PERMISSIONS403User lacks required permissions
RESOURCE_NOT_FOUND404Requested resource doesn't exist
DUPLICATE_RESOURCE409Resource with same identifier exists
VALIDATION_ERROR422Request validation failed
RATE_LIMIT_EXCEEDED429Too many requests

Error Handling Example

Python:

import requests

try:
response = requests.get(
"https://dev.api.authsec.dev/uflow/admin/users/user-123",
headers={"Authorization": f"Bearer {token}"}
)
response.raise_for_status()
user = response.json()
except requests.exceptions.HTTPError as e:
error_data = e.response.json()

if e.response.status_code == 401:
print("Authentication failed - token may be expired")
# Re-authenticate
elif e.response.status_code == 403:
print("Permission denied")
elif e.response.status_code == 404:
print("User not found")
else:
print(f"Error: {error_data['error']['message']}")

TypeScript:

async function fetchUser(userId: string, token: string) {
try {
const response = await fetch(
`https://dev.api.authsec.dev/uflow/admin/users/${userId}`,
{ headers: { 'Authorization': `Bearer ${token}` }}
);

if (!response.ok) {
const error = await response.json();

switch (response.status) {
case 401:
throw new Error('Authentication required');
case 403:
throw new Error('Permission denied');
case 404:
throw new Error('User not found');
default:
throw new Error(error.error.message);
}
}

return await response.json();
} catch (error) {
console.error('API Error:', error);
throw error;
}
}

Rate Limiting

Rate Limit Headers

Responses include rate limit information:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1644598800
  • X-RateLimit-Limit: Maximum requests per window
  • X-RateLimit-Remaining: Requests remaining in current window
  • X-RateLimit-Reset: Unix timestamp when limit resets

Handling Rate Limits

Respect the limits:

import time

def make_request_with_retry(url, headers, max_retries=3):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)

if response.status_code == 429:
# Rate limited - wait and retry
reset_time = int(response.headers.get('X-RateLimit-Reset', 0))
wait_seconds = max(reset_time - time.time(), 1)
print(f"Rate limited. Waiting {wait_seconds}s...")
time.sleep(wait_seconds)
continue

return response

raise Exception("Max retries exceeded")

Filtering and Searching

Many list endpoints support filtering:

Query Parameters

# Filter by status
GET /uflow/admin/users?tenant_id=abc&active=true

# Search by username
GET /uflow/admin/users?tenant_id=abc&username=john

# Multiple filters
GET /uflow/admin/users?tenant_id=abc&active=true&role=admin

Common Filter Parameters

ParameterTypeDescription
searchstringFull-text search across relevant fields
activebooleanFilter by active/inactive status
created_afterISO8601 dateResources created after date
created_beforeISO8601 dateResources created before date
sortstringSort field (e.g., created_at, -username for desc)

Timestamps

All timestamps are in ISO 8601 format (UTC):

{
"created_at": "2026-02-11T19:56:00Z",
"updated_at": "2026-02-11T19:56:00Z"
}

Parsing in Code:

Python:

from datetime import datetime

timestamp = "2026-02-11T19:56:00Z"
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))

TypeScript:

const timestamp = "2026-02-11T19:56:00Z";
const date = new Date(timestamp);

Idempotency

Idempotent Operations

Some operations are naturally idempotent:

  • GET: Always safe to retry
  • PUT: Updates are idempotent
  • DELETE: Deleting already-deleted resource returns 404 or 204

Non-Idempotent Operations

POST requests (resource creation) are NOT idempotent:

# This creates TWO users if called twice
POST /uflow/admin/users
{"username": "[email protected]", ...}

Use idempotency keys (if supported):

POST /uflow/admin/users
Idempotency-Key: unique-request-id-123

{"username": "[email protected]", ...}

Versioning

API Versions

Some services use versioned paths:

/clients/v1/tenants/{tenantId}/clients

Deprecation Policy

  • Notice Period: 90 days minimum before endpoint removal
  • Headers: Deprecated endpoints return warning headers:
    X-API-Deprecated: true
    X-API-Sunset: 2026-06-01T00:00:00Z

Security Best Practices

1. Secure Token Storage

[!CAUTION] Never store tokens in:

  • Local storage (XSS vulnerable)
  • URL parameters
  • Git repositories
  • Client-side JavaScript variables

Do:

  • Use secure, httpOnly cookies for web apps
  • Use secure keychain/keystore for mobile apps
  • Rotate tokens regularly

2. HTTPS Only

[!IMPORTANT] All API requests MUST use HTTPS. HTTP requests will be rejected.

3. Token Expiration

Tokens expire after 1 hour by default. Implement token refresh:

def get_valid_token(current_token, refresh_token):
# Check if token is expired (decode JWT)
if is_token_expired(current_token):
# Refresh the token
return refresh_access_token(refresh_token)
return current_token

4. Validate Input

Always validate user input before sending to API:

function validateEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}

function validatePassword(password: string): boolean {
// At least 8 chars, 1 uppercase, 1 lowercase, 1 number
return password.length >= 8 &&
/[A-Z]/.test(password) &&
/[a-z]/.test(password) &&
/[0-9]/.test(password);
}

Testing

Development Environment

Base URL: https://dev.api.authsec.dev

Test Credentials

[!NOTE] Request test tenant credentials from your administrator

Sample Test Flow

# 1. Authenticate
TOKEN=$(curl -s -X POST https://dev.api.authsec.dev/uflow/admin/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"[email protected]","password":"TestPass123!"}' \
| jq -r '.access_token')

# 2. Make authenticated request
curl https://dev.api.authsec.dev/uflow/admin/users?tenant_id=test-tenant \
-H "Authorization: Bearer $TOKEN"

SDK Usage

Python SDK

from authsec import AuthSecClient

client = AuthSecClient(
base_url="https://dev.api.authsec.dev",
tenant_id="your-tenant-id"
)

# Authenticate
client.login(username="[email protected]", password="password")

# Make requests
users = client.users.list()
user = client.users.get(user_id="user-123")

TypeScript SDK

import { AuthSecClient } from '@authsec/sdk';

const client = new AuthSecClient({
baseUrl: 'https://dev.api.authsec.dev',
tenantId: 'your-tenant-id'
});

// Authenticate
await client.login('[email protected]', 'password');

// Make requests
const users = await client.users.list();
const user = await client.users.get('user-123');

Next Steps


Questions? Review the Quickstart Guide for a complete example →