Error Handling
API error codes and how to handle them
The Keva API uses conventional HTTP response codes to indicate success or failure. This guide covers all error codes, their meanings, and how to handle them.
HTTP Status Codes
| Code | Meaning |
|---|---|
200 | Success |
201 | Created |
204 | No Content (successful deletion) |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid or missing API key |
403 | Forbidden - Insufficient permissions |
404 | Not Found - Resource doesn't exist |
422 | Unprocessable Entity - Validation error |
429 | Too Many Requests - Rate limit exceeded |
500 | Internal Server Error |
Error Response Format
All errors return a JSON object with an error field:
{
"error": "Description of the error"
}Validation errors may include additional details:
{
"error": "Validation failed",
"details": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "subject", "message": "Subject is required" }
]
}Common Errors
Authentication Errors (401)
Missing Authorization Header
{ "error": "Missing or invalid Authorization header" }Solution: Include the Authorization: Bearer <api_key> header.
Invalid API Key Format
{ "error": "Invalid API key format" }Solution: API keys must start with keva_live_.
Invalid API Key
{ "error": "Invalid API key" }Solution: Check that the API key is correct and hasn't been revoked.
Revoked API Key
{ "error": "API key has been revoked" }Solution: Generate a new API key in Settings > API Keys.
Expired API Key
{ "error": "API key has expired" }Solution: Generate a new API key or extend the expiration date.
Authorization Errors (403)
Insufficient Scope
{ "error": "Insufficient scope" }Solution: Your API key doesn't have the required scope. Create a new key with appropriate permissions.
Validation Errors (400/422)
Invalid Status
{ "error": "Invalid status. Must be one of: open, pending, solved, closed" }Solution: Use one of the valid status values.
Missing Required Field
{
"error": "Validation failed",
"details": [
{ "field": "customerEmail", "message": "customerEmail is required" }
]
}Solution: Include all required fields in your request.
Not Found Errors (404)
Resource Not Found
{ "error": "Ticket not found" }Solution: Verify the resource ID and that it belongs to your tenant.
Approval Already Decided
{ "error": "Approval not found or already decided" }Solution: The approval was already approved/rejected or has expired.
Rate Limiting (429)
{ "error": "Rate limit exceeded" }Solution: Wait before retrying. Implement exponential backoff.
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || Math.pow(2, i);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}Server Errors (500)
{ "error": "Internal server error" }Solution: Retry the request. If the error persists, contact support.
Error Handling Best Practices
1. Check Response Status
const response = await fetch('https://app.keva.support/api/v1/tickets', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}
const data = await response.json();2. Handle Specific Errors
async function createTicket(data) {
const response = await fetch('https://app.keva.support/api/v1/tickets', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (response.status === 401) {
// Handle authentication error
throw new AuthError('Invalid API key');
}
if (response.status === 422) {
// Handle validation error
const { details } = await response.json();
throw new ValidationError(details);
}
if (response.status === 429) {
// Handle rate limiting
throw new RateLimitError('Rate limit exceeded');
}
if (!response.ok) {
const { error } = await response.json();
throw new Error(error);
}
return response.json();
}3. Implement Retry Logic
async function withRetry(fn, maxRetries = 3) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
// Don't retry on client errors (4xx)
if (error.status >= 400 && error.status < 500) {
throw error;
}
// Exponential backoff
await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
}
}
throw lastError;
}Getting Help
If you encounter persistent errors:
- Check the API documentation for correct usage
- Verify your API key and scopes
- Review the error message and details
- Contact support at hello@keva.support