API Reference

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

CodeMeaning
200Success
201Created
204No Content (successful deletion)
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing API key
403Forbidden - Insufficient permissions
404Not Found - Resource doesn't exist
422Unprocessable Entity - Validation error
429Too Many Requests - Rate limit exceeded
500Internal 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:

  1. Check the API documentation for correct usage
  2. Verify your API key and scopes
  3. Review the error message and details
  4. Contact support at hello@keva.support