API Reference
Error Codes
Complete reference for all API error codes and their resolution
All API errors follow a standardized envelope:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request body failed validation",
"details": [
{ "field": "email", "issue": "Invalid email format" },
{ "field": "start_date", "issue": "Date cannot be in the future" }
],
"request_id": "req_abc123",
"documentation_url": "https://dev-developer.totalaccess.co.za/docs/api-reference/errors#validation_error"
}
}
The request_id field is included in all error responses. Include this ID
when contacting support for faster resolution.
| Code | Description | Common Causes | Resolution |
|---|
BAD_REQUEST | Malformed request body or missing required fields | Missing action field, invalid JSON, wrong parameter types | Check request body against the action's schema |
VALIDATION_ERROR | Request body failed field validation | Invalid email format, date out of range, negative values where positive expected | Review details array for field-specific errors |
INVALID_ACTION | Unknown action specified | Typo in action name, action not available for this client | Check available actions via GET /api/v1/public/data |
| Code | Description | Common Causes | Resolution |
|---|
UNAUTHORIZED | Missing, invalid, or expired API key | Key revoked, key expired, wrong header | Generate a new API key, check X-API-Key header |
TOKEN_EXPIRED | JWT access token has expired | Token TTL exceeded (15 min) | Refresh using the refresh token |
INVALID_API_KEY | API key format is invalid | Malformed key, wrong prefix | Ensure key starts with ta_sk_ |
| Code | Description | Common Causes | Resolution |
|---|
FORBIDDEN | Valid auth but insufficient permissions | Missing required scope, IP not in allowlist | Add required scope to API key, add IP to allowlist |
IP_NOT_ALLOWED | Request from non-allowlisted IP | IP allowlist configured but request IP not listed | Add the IP to the key's allowlist |
SCOPE_REQUIRED | API key lacks a specific scope | Key created without the needed scope | Edit the key to add the scope, or create a new key |
TIER_RESTRICTED | Finance document type not allowed by subscription tier | Client's Total Finance tier does not include the requested document type | Upgrade the Total Finance subscription tier to include the document type |
| Code | Description | Common Causes | Resolution |
|---|
NOT_FOUND | Requested resource not found | Wrong ID, resource deleted, resource belongs to different client | Verify the ID and client context |
| Code | Description | Common Causes | Resolution |
|---|
METHOD_NOT_ALLOWED | HTTP method not supported for this endpoint | GET on a POST-only endpoint | Check the endpoint's allowed methods |
| Code | Description | Common Causes | Resolution |
|---|
CONFLICT | Request conflicts with current state | Duplicate unique constraint violation | Use a new idempotency key or resolve the conflict |
IDEMPOTENCY_CONFLICT | Same idempotency key with different request body | Key reused with different payload | Use a unique key per unique request |
| Code | Description | Common Causes | Resolution |
|---|
UNPROCESSABLE_ENTITY | Request is valid but cannot be processed | Business rule violation (e.g., clock out before clock in) | Review the message field for the specific rule |
| Code | Description | Common Causes | Resolution |
|---|
RATE_LIMITED | Rate limit exceeded | Too many requests in the time window | Wait for X-RateLimit-Reset, upgrade tier |
| Code | Description | Common Causes | Resolution |
|---|
INTERNAL_ERROR | Unexpected server error | Bug, database timeout, infrastructure issue | Retry with exponential backoff, contact support if persistent |
BAD_GATEWAY | Upstream service unavailable | WebSocket server, email server, or FreeSWITCH unreachable | Retry, check status page |
SERVICE_UNAVAILABLE | Service temporarily unavailable | Maintenance mode, capacity exceeded | Retry after a delay, check status page |
GATEWAY_TIMEOUT | Upstream service timed out | Database query timeout, external API timeout | Retry with longer timeout |
When rate limited, check these headers to understand your limits:
| Header | Description |
|---|
X-RateLimit-Limit | Maximum requests per minute |
X-RateLimit-Remaining | Remaining requests in current window |
X-RateLimit-Reset | ISO 8601 timestamp when the window resets |
try {
const response = await fetch('https://totalaccess.co.za/api/v1/public/data', {
method: 'POST',
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'list-work-items' }),
});
const data = await response.json();
if (!response.ok) {
console.error(`Error ${data.error.code}: ${data.error.message}`);
console.error(`Request ID: ${data.error.request_id}`);
// Retry with exponential backoff for 5xx errors
if (response.status >= 500) {
await retryWithBackoff();
}
}
} catch (err) {
console.error('Network error:', err);
}
import requests
import time
def call_api(action, params, api_key, max_retries=3):
for attempt in range(max_retries):
response = requests.post(
'https://totalaccess.co.za/api/v1/public/data',
headers={'X-API-Key': api_key, 'Content-Type': 'application/json'},
json={'action': action, **params}
)
if response.status_code == 429:
reset = response.headers.get('X-RateLimit-Reset')
wait_time = 60 # Default wait
print(f"Rate limited. Reset at: {reset}")
time.sleep(wait_time)
continue
data = response.json()
if not response.ok:
error = data.get('error', {})
print(f"Error {error.get('code')}: {error.get('message')}")
print(f"Request ID: {error.get('request_id')}")
if response.status_code >= 500:
time.sleep(2 ** attempt)
continue
return None
return data
return None