Error Handling

Understanding ETERA API error responses

Error Response Format

All ETERA APIs return errors in a consistent JSON format:

1{
2 "error": {
3 "code": "VALIDATION_ERROR",
4 "message": "The 'checkIn' field is required",
5 "status": 400,
6 "details": []
7 }
8}

HTTP Status Codes

4xx Client Errors
CodeMeaning
400Bad Request — Invalid parameters or malformed request body
401Unauthorized — Missing or invalid API key
403Forbidden — Insufficient permissions for this resource
404Not Found — Resource does not exist
409Conflict — Resource state conflict (e.g., double booking)
422Unprocessable Entity — Validation failure on request body
429Too Many Requests — Rate limit exceeded
CodeMeaning
500Internal Server Error — Something went wrong on our end
502Bad Gateway — Upstream service unavailable
503Service Unavailable — Service temporarily down
504Gateway Timeout — Upstream service took too long

For 5xx errors, always implement retry logic with exponential backoff. These are typically transient issues.


Error Codes

Common error codes returned in the error.code field:

CodeDescription
VALIDATION_ERRORRequest body failed validation
AUTHENTICATION_ERRORInvalid or expired credentials
AUTHORIZATION_ERRORInsufficient permissions
NOT_FOUNDRequested resource not found
RATE_LIMIT_EXCEEDEDToo many requests
BOOKING_CONFLICTConflicting reservation
PAYMENT_FAILEDPayment processing error
SERVICE_UNAVAILABLEUpstream service is down

Retry Strategy

For 429 and 5xx errors, implement exponential backoff:

1

First retry

Wait 1 second, then retry the request

2

Second retry

Wait 2 seconds, then retry

3

Third retry

Wait 4 seconds, then retry

4

Give up

After 3 retries, log the error and notify the user

Implementation

1async function fetchWithRetry(
2 url: string,
3 options: RequestInit,
4 retries = 3
5) {
6 for (let attempt = 0; attempt < retries; attempt++) {
7 const response = await fetch(url, options);
8
9 if (response.ok) return response;
10
11 if (response.status === 429 || response.status >= 500) {
12 const delay = Math.pow(2, attempt) * 1000;
13 await new Promise((resolve) => setTimeout(resolve, delay));
14 continue;
15 }
16
17 throw new Error(`Request failed: ${response.status}`);
18 }
19 throw new Error("Max retries exceeded");
20}

For 429 responses, check the Retry-After header if present — it indicates exactly how long to wait before retrying.


Common Scenarios

Your token has expired or is invalid. Re-authenticate using your preferred method:

1if (response.status === 401) {
2 const newToken = await refreshAuth();
3 // Retry with new token
4}

See Authentication for details on token refresh.

Check the error.details array for field-level validation messages:

1{
2 "error": {
3 "code": "VALIDATION_ERROR",
4 "message": "Validation failed",
5 "status": 422,
6 "details": [
7 { "field": "checkIn", "message": "Must be a future date" },
8 { "field": "partySize", "message": "Must be between 1 and 20" }
9 ]
10 }
11}

You’ve exceeded the rate limit. Check these headers:

HeaderDescription
X-RateLimit-LimitMaximum requests per window
X-RateLimit-RemainingRemaining requests in current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait before retrying

The requested time slot or resource is no longer available. Fetch updated availability and let the user choose an alternative.


Next Steps