Search docs...

Cmd+K

Error Handling

Understanding and handling errors in the Bookwell API.

The Bookwell API uses standard HTTP status codes and provides detailed error messages to help you handle issues gracefully.

Error Response Format

All errors follow this structure:

{
  "error": {
    "code": "invalid_request",
    "message": "The request was invalid or cannot be served.",
    "details": {
      "field": "start_time",
      "reason": "Must be a future date"
    }
  }
}

Fields

FieldDescription
codeMachine-readable error code
messageHuman-readable description
detailsAdditional context (optional)

HTTP Status Codes

Success Codes

CodeDescription
200Request succeeded
201Resource created
204Request succeeded, no content

Client Error Codes

CodeDescription
400Bad request (invalid parameters)
401Authentication required
403Insufficient permissions
404Resource not found
409Conflict (e.g., slot already booked)
422Validation error
429Rate limit exceeded

Server Error Codes

CodeDescription
500Internal server error
502Bad gateway
503Service unavailable

Error Codes Reference

Authentication Errors

CodeHTTPDescription
authentication_required401No API key provided
invalid_api_key401API key is invalid or revoked
expired_api_key401API key has expired
insufficient_permissions403Key lacks required scope

Example:

{
  "error": {
    "code": "invalid_api_key",
    "message": "The provided API key is invalid or has been revoked."
  }
}

Validation Errors

CodeHTTPDescription
invalid_request400Request format is invalid
missing_parameter400Required parameter missing
invalid_parameter400Parameter value is invalid
validation_error422Multiple validation failures

Example:

{
  "error": {
    "code": "validation_error",
    "message": "The request contains invalid data.",
    "details": {
      "errors": [
        {
          "field": "customer.email",
          "message": "Must be a valid email address"
        },
        {
          "field": "start_time",
          "message": "Must be in the future"
        }
      ]
    }
  }
}

Resource Errors

CodeHTTPDescription
not_found404Resource doesn't exist
service_not_found404Service ID invalid
therapist_not_found404Therapist ID invalid
appointment_not_found404Appointment ID invalid
customer_not_found404Customer ID invalid

Example:

{
  "error": {
    "code": "appointment_not_found",
    "message": "No appointment found with ID apt_abc123."
  }
}

Booking Errors

CodeHTTPDescription
slot_unavailable409Time slot is not available
therapist_unavailable409Therapist not available
service_unavailable409Service not bookable
past_booking400Cannot book past dates
booking_limit_reached400Too far in advance
customer_blocked403Customer is blocked
duplicate_booking409Booking already exists

Example:

{
  "error": {
    "code": "slot_unavailable",
    "message": "The requested time slot is no longer available.",
    "details": {
      "requested_time": "2025-01-15T10:00:00Z",
      "next_available": "2025-01-15T11:00:00Z"
    }
  }
}

Rate Limiting Errors

CodeHTTPDescription
rate_limit_exceeded429Too many requests

Example:

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Please try again later.",
    "details": {
      "limit": 60,
      "period": "minute",
      "retry_after": 45
    }
  }
}

Server Errors

CodeHTTPDescription
internal_error500Unexpected server error
service_unavailable503Temporary outage

Example:

{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred. Please try again.",
    "details": {
      "request_id": "req_xyz789"
    }
  }
}

Handling Errors

General Pattern

try {
  const response = await fetch('https://api.bookwell.app/v1/appointments', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(appointmentData)
  });
 
  if (!response.ok) {
    const error = await response.json();
    handleError(response.status, error);
    return;
  }
 
  const data = await response.json();
  // Process successful response
} catch (networkError) {
  // Handle network errors
}

Error Handler Example

function handleError(status, error) {
  switch (error.error.code) {
    case 'slot_unavailable':
      // Show alternative times
      showAlternativeSlots(error.error.details.next_available);
      break;
 
    case 'validation_error':
      // Show field errors
      showFieldErrors(error.error.details.errors);
      break;
 
    case 'rate_limit_exceeded':
      // Wait and retry
      const retryAfter = error.error.details.retry_after;
      setTimeout(() => retryRequest(), retryAfter * 1000);
      break;
 
    case 'authentication_required':
    case 'invalid_api_key':
      // Handle auth issues
      refreshAuthentication();
      break;
 
    default:
      // Generic error handling
      showGenericError(error.error.message);
  }
}

Retry Strategy

Retryable Errors

These errors may succeed on retry:

CodeRetry Strategy
429Wait for retry_after
500Exponential backoff
502Wait and retry
503Wait and retry

Non-Retryable Errors

Don't retry these:

  • 400 - Fix the request first
  • 401 - Fix authentication
  • 403 - Get proper permissions
  • 404 - Resource doesn't exist
  • 409 - Resolve conflict first
  • 422 - Fix validation errors

Exponential Backoff

async function retryWithBackoff(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);
    }
  }
}

Request IDs

Every response includes a request ID:

X-Request-Id: req_abc123xyz

Include this when contacting support about errors.

Best Practices

User-Friendly Messages

Map error codes to user-friendly messages:

const userMessages = {
  'slot_unavailable': 'This time is no longer available. Please choose another.',
  'customer_blocked': 'Unable to complete booking. Please contact us.',
  'rate_limit_exceeded': 'Too many requests. Please wait a moment.',
  // ...
};
 
function getUserMessage(code) {
  return userMessages[code] || 'Something went wrong. Please try again.';
}

Logging

Log errors for debugging:

function logError(error, context) {
  console.error('API Error:', {
    code: error.error.code,
    message: error.error.message,
    details: error.error.details,
    context: context,
    timestamp: new Date().toISOString()
  });
}

Validation Before Requests

Validate locally before API calls:

function validateAppointmentData(data) {
  const errors = [];
 
  if (!data.service_id) {
    errors.push({ field: 'service_id', message: 'Service is required' });
  }
 
  if (!data.start_time || new Date(data.start_time) <= new Date()) {
    errors.push({ field: 'start_time', message: 'Must be a future time' });
  }
 
  if (!data.customer?.email?.includes('@')) {
    errors.push({ field: 'customer.email', message: 'Valid email required' });
  }
 
  return errors;
}

Getting Help

For persistent errors:

  1. Check the API Status
  2. Review error details and request ID
  3. Check documentation for the endpoint
  4. Contact support with request ID

Next Steps