Overview
The Voyant API uses standard HTTP status codes to indicate success or failure of requests. Error responses include a JSON body with details about what went wrong.
All errors return a JSON object with an error field:
{
"error": "Error message description"
}
Some errors may include additional fields:
{
"error": "Validation failed",
"details": {
"field": "email",
"message": "Invalid email format"
}
}
HTTP status codes
2xx Success
| Code | Status | Description |
|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 204 | No Content | Request succeeded with no response body |
4xx Client errors
| Code | Status | Description |
|---|
| 400 | Bad Request | Invalid request format or parameters |
| 401 | Unauthorized | Missing or invalid Authorization token |
| 403 | Forbidden | Valid key but insufficient permissions |
| 404 | Not Found | Resource not found |
| 409 | Conflict | Request conflicts with existing resource |
| 422 | Unprocessable Entity | Valid format but invalid data |
| 429 | Too Many Requests | Rate limit exceeded |
5xx Server errors
| Code | Status | Description |
|---|
| 500 | Internal Server Error | Unexpected server error |
| 502 | Bad Gateway | Upstream service error |
| 503 | Service Unavailable | Temporary service outage |
| 504 | Gateway Timeout | Request timeout |
Common errors
400 Bad Request
Invalid request format, missing required fields, or malformed JSON.
{
"error": "Invalid request"
}
Common causes:
- Malformed JSON in request body
- Missing required fields
- Invalid parameter types
- Invalid UUID format
How to fix:
- Validate JSON before sending
- Check required fields in documentation
- Ensure correct data types
- Use proper UUID v4 format
401 Unauthorized
Missing or invalid Authorization token.
{
"error": "Unauthorized"
}
Common causes:
- Missing
Authorization: Bearer header
- Invalid API key
- Revoked API key
- Extra spaces in API key
How to fix:
- Verify Authorization header is included in request
- Check for typos or extra characters
- Generate new API key if needed
- Ensure key hasn’t been revoked
404 Not Found
Requested resource doesn’t exist or you don’t have access.
Common causes:
- Invalid resource ID
- Resource deleted
- No access to resource (wrong workspace)
- Typo in endpoint URL
How to fix:
- Verify resource ID is correct
- Check resource still exists
- Confirm resource belongs to your workspace
- Review endpoint URL for typos
429 Rate Limit Exceeded
Too many requests in a short time period.
{
"error": "Rate limit exceeded",
"retry_after": 30
}
Common causes:
- Exceeding 3,000 requests per minute
- Surpassing the 30 requests per second burst window
- Missing rate limit handling
How to fix:
- Implement exponential backoff
- Use
retry_after value for delays
- Cache responses when possible
- Coordinate with support ahead of high-volume events
500 Internal Server Error
Unexpected server error.
{
"error": "Internal server error"
}
Common causes:
- Temporary service issue
- Database connection problem
- Upstream service failure
How to fix:
- Retry request with exponential backoff
- Contact support if persistent
- Include
X-Request-ID when reporting
Error handling best practices
1. Check status codes
Always check the HTTP status code before parsing the response:
const response = await fetch('https://api.voyantcloud.com/v1/products', {
headers: { Authorization: `Bearer ${process.env.VOYANT_API_KEY}` }
});
if (!response.ok) {
const error = await response.json();
throw new Error(`API error (${response.status}): ${error.error}`);
}
const data = await response.json();
2. Implement retry logic
Use exponential backoff for retrying failed requests:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('retry-after') || (2 ** i);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
if (response.status >= 500) {
await new Promise(resolve => setTimeout(resolve, (2 ** i) * 1000));
continue;
}
return response;
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, (2 ** i) * 1000));
}
}
}
3. Log request IDs
Always log the X-Request-ID header for debugging:
const requestId = response.headers.get("x-request-id")
console.error(`API error: ${error.error}`, { requestId })
Include the request ID when contacting support for faster troubleshooting.
4. Handle rate limits
Respect rate limit headers to avoid throttling:
const limit = response.headers.get("x-ratelimit-limit")
const remaining = response.headers.get("x-ratelimit-remaining")
const reset = response.headers.get("x-ratelimit-reset")
if (remaining < 10) {
console.warn(`Rate limit warning: ${remaining}/${limit} remaining`)
}
Validate data before sending to avoid 400 errors:
function validatePriceRequest(data) {
if (!data.productId || !isUUID(data.productId)) {
throw new Error("Invalid productId")
}
if (!data.departureId || !isUUID(data.departureId)) {
throw new Error("Invalid departureId")
}
if (!data.pax || data.pax.adults < 1) {
throw new Error("At least one adult required")
}
return data
}
const validData = validatePriceRequest(requestData)
Error handling patterns
Graceful degradation
async function getProducts() {
try {
const response = await fetch("https://api.voyantcloud.com/v1/products", {
headers: { Authorization: `Bearer ${process.env.VOYANT_API_KEY}` },
})
if (!response.ok) {
console.error("Failed to fetch products:", await response.json())
return [] // Return empty array instead of throwing
}
return await response.json()
} catch (error) {
console.error("Network error:", error)
return [] // Fallback to empty array
}
}
User-friendly messages
Map technical errors to user-friendly messages:
function getUserMessage(statusCode, error) {
const messages = {
400: "Invalid request. Please check your input.",
401: "Authentication failed. Please check your Authorization token.",
404: "Resource not found. It may have been deleted.",
429: "Too many requests. Please try again in a moment.",
500: "Server error. Please try again later.",
503: "Service temporarily unavailable. Please try again.",
}
return messages[statusCode] || "An unexpected error occurred."
}
Circuit breaker pattern
Prevent cascading failures with circuit breaker:
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.failureCount = 0
this.threshold = threshold
this.timeout = timeout
this.state = "CLOSED" // CLOSED, OPEN, HALF_OPEN
this.nextAttempt = Date.now()
}
async call(fn) {
if (this.state === "OPEN") {
if (Date.now() < this.nextAttempt) {
throw new Error("Circuit breaker is OPEN")
}
this.state = "HALF_OPEN"
}
try {
const result = await fn()
this.onSuccess()
return result
} catch (error) {
this.onFailure()
throw error
}
}
onSuccess() {
this.failureCount = 0
this.state = "CLOSED"
}
onFailure() {
this.failureCount++
if (this.failureCount >= this.threshold) {
this.state = "OPEN"
this.nextAttempt = Date.now() + this.timeout
}
}
}
Debugging errors
Enable verbose logging
Add detailed logging for development:
const DEBUG = process.env.NODE_ENV === "development"
async function apiRequest(endpoint, options) {
if (DEBUG) {
console.log("Request:", { endpoint, options })
}
const response = await fetch(`https://api.voyantcloud.com${endpoint}`, options)
if (DEBUG) {
console.log("Response:", {
status: response.status,
headers: Object.fromEntries(response.headers),
requestId: response.headers.get("x-request-id"),
})
}
return response
}
Check API status
Before debugging, verify API status:
curl -I https://api.voyantcloud.com/v1/products
Test authentication
Isolate authentication issues:
curl https://api.voyantcloud.com/v1/products \
-H "Authorization: Bearer YOUR_API_KEY" \
-v
Getting help
If you encounter persistent errors:
Collect information
- Request ID (
X-Request-ID) from response headers
- Endpoint, method, and sanitized request payload
- Response status code and body
- Workspace ID and timestamp of the failure