All EngageFabric API errors follow a consistent format:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": [...],
"requestId": "req-abc123",
"timestamp": "2025-01-21T10:00:00Z"
}
}
Authentication Errors
| Code | HTTP Status | Description |
|---|
UNAUTHORIZED | 401 | Missing or invalid API key/token |
TOKEN_EXPIRED | 401 | JWT token has expired |
INVALID_API_KEY | 401 | API key is malformed or revoked |
FORBIDDEN | 403 | Valid credentials but insufficient permissions |
PROJECT_SUSPENDED | 403 | Project has been suspended |
Example
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired API key",
"requestId": "req-abc123",
"timestamp": "2025-01-21T10:00:00Z"
}
}
Validation Errors
| Code | HTTP Status | Description |
|---|
VALIDATION_ERROR | 400 | Request body validation failed |
INVALID_PARAMETER | 400 | Query parameter is invalid |
MISSING_FIELD | 400 | Required field is missing |
INVALID_FORMAT | 400 | Field format is incorrect |
Example
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{
"field": "externalUserId",
"message": "Must be a non-empty string"
},
{
"field": "email",
"message": "Must be a valid email address"
}
],
"requestId": "req-abc123",
"timestamp": "2025-01-21T10:00:00Z"
}
}
Resource Errors
| Code | HTTP Status | Description |
|---|
NOT_FOUND | 404 | Requested resource does not exist |
PLAYER_NOT_FOUND | 404 | Player with given ID not found |
QUEST_NOT_FOUND | 404 | Quest with given ID not found |
LEADERBOARD_NOT_FOUND | 404 | Leaderboard with given ID not found |
LOBBY_NOT_FOUND | 404 | Lobby with given ID not found |
ALREADY_EXISTS | 409 | Resource already exists |
CONFLICT | 409 | Operation conflicts with current state |
Example
{
"error": {
"code": "PLAYER_NOT_FOUND",
"message": "Player with ID 'player-123' not found",
"requestId": "req-abc123",
"timestamp": "2025-01-21T10:00:00Z"
}
}
Rate Limiting Errors
| Code | HTTP Status | Description |
|---|
RATE_LIMITED | 429 | Too many requests |
QUOTA_EXCEEDED | 429 | Monthly quota exceeded |
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1699900000
Retry-After: 60
Example
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 60 seconds.",
"details": {
"limit": 100,
"window": "1 minute",
"retryAfter": 60
},
"requestId": "req-abc123",
"timestamp": "2025-01-21T10:00:00Z"
}
}
Business Logic Errors
| Code | HTTP Status | Description |
|---|
INSUFFICIENT_CURRENCY | 400 | Not enough currency for operation |
INSUFFICIENT_LIVES | 400 | No lives remaining |
QUEST_NOT_AVAILABLE | 400 | Quest is locked or expired |
QUEST_ALREADY_COMPLETED | 400 | Quest already finished |
LOBBY_FULL | 400 | Lobby has reached max players |
LOBBY_STARTED | 400 | Cannot join started lobby |
PLAYER_BANNED | 403 | Player is banned from action |
PLAYER_MUTED | 403 | Player cannot send messages |
Example
{
"error": {
"code": "INSUFFICIENT_CURRENCY",
"message": "Not enough coins. Required: 100, Available: 50",
"details": {
"currencyId": "coins",
"required": 100,
"available": 50
},
"requestId": "req-abc123",
"timestamp": "2025-01-21T10:00:00Z"
}
}
Server Errors
| Code | HTTP Status | Description |
|---|
INTERNAL_ERROR | 500 | Unexpected server error |
SERVICE_UNAVAILABLE | 503 | Service temporarily unavailable |
TIMEOUT | 504 | Request timed out |
Example
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred. Please try again.",
"requestId": "req-abc123",
"timestamp": "2025-01-21T10:00:00Z"
}
}
If you receive a 500 error, please contact support with the requestId
for investigation.
Handling Errors
JavaScript Example
try {
const player = await client.players.get('player-123');
} catch (error) {
switch (error.code) {
case 'PLAYER_NOT_FOUND':
// Create the player
await client.players.identify({ externalUserId: 'player-123' });
break;
case 'RATE_LIMITED':
// Wait and retry
await sleep(error.details.retryAfter * 1000);
return retry();
case 'UNAUTHORIZED':
// Refresh credentials
await refreshApiKey();
break;
default:
console.error('Unexpected error:', error);
}
}
Retry Strategy
For transient errors (429, 503, 504), implement exponential backoff:
async function withRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
if (!isRetryable(error)) throw error;
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await sleep(delay);
}
}
}
function isRetryable(error) {
return ['RATE_LIMITED', 'SERVICE_UNAVAILABLE', 'TIMEOUT'].includes(error.code);
}