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 →