Appearance
Errors
All AvailEngine errors follow a consistent format.
Error Format
json
{
"error": {
"code": "string",
"message": "string"
}
}HTTP Status Codes
| Status | Meaning |
|---|---|
200 | Success |
201 | Created |
400 | Bad request — check your payload |
401 | Unauthorized — invalid or missing API key |
402 | Payment Required — developer's subscription is past due |
403 | Forbidden — key lacks required scope |
404 | Not found |
409 | Conflict — duplicate idempotency key or resource clash |
422 | Validation error — body doesn't match schema |
429 | Too many requests — rate limit exceeded. Check Retry-After header |
Error Codes
| Code | Meaning |
|---|---|
invalid_token | API key or JWT is missing, expired, or malformed |
missing_scope | API key doesn't have the required scope for this endpoint |
insufficient_permissions | Dashboard user doesn't have the required role |
not_found | Resource doesn't exist or doesn't belong to this business |
validation_error | Request body failed Pydantic validation |
rate_limit_exceeded | Key's RPM limit reached |
payment_required | Developer's account is past due |
conflict | Resource already exists (e.g. duplicate slug) |
slot_unavailable | Requested time slot is already booked |
outside_operating_hours | Booking time is outside business hours |
idempotency_mismatch | Same idempotency key with different request body |
Example
bash
curl https://api.availengine.com/v1/availability/nonexistent \
-H "Authorization: Bearer avail_test_invalid"
# 401
{
"error": {
"code": "invalid_token",
"message": "Could not validate credentials"
}
}Recovery Guide
What to do when you get each error.
invalid_token (401)
Cause: Missing, expired, or malformed API key or JWT.
Fix:
- Check the
Authorizationheader is present and starts withBearer - API keys must start with
avail_live_oravail_test_ - JWT tokens expire after 1 hour — refresh via Supabase Auth
- Verify the key hasn't been revoked (
GET /v1/manage/api-keys)
missing_scope (403)
Cause: Your API key doesn't have the required scope for this endpoint.
Fix:
- Check the key's scopes:
GET /v1/manage/api-keys - Create a new key with the needed scope (
readorwrite) - Most public endpoints need
read. Booking creation needswrite.
insufficient_permissions (403)
Cause: Your user role doesn't allow this action.
Fix:
- Dashboard users need
business_ownerorstaff_memberrole - Developer endpoints need
developerrole - Check
app_metadata.rolein the JWT payload
rate_limit_exceeded (429)
Cause: Your API key has exceeded its requests-per-minute limit.
Fix:
- Check the
Retry-Afterheader — it tells you how many seconds to wait - Implement exponential backoff in your client
- Create a new key with a higher RPM limit
- Batch requests where possible (e.g., check availability for multiple dates client-side)
payment_required (402)
Cause: Your developer subscription is past due.
Fix:
- Go to the Developer Portal → Billing
- Update your payment method
- Pay the outstanding invoice
- API access resumes automatically after payment
not_found (404)
Cause: The resource doesn't exist or doesn't belong to your business.
Fix:
- Verify the ID/UUID is correct (not truncated or mistyped)
- API keys are scoped to one business — you can't access another business's data
- Check if the resource was deleted or deactivated
conflict (409)
Cause: State conflict — booking status transition not allowed, or duplicate resource.
Fix:
- Status transitions: Check the booking lifecycle — you can't go
completed → cancelled - Duplicate slug: Choose a different business name
- Resource in use: Cancel future bookings before deleting a resource
validation_error (422)
Cause: Request body failed validation.
Fix:
- Read the error
message— it includes which field failed and why - Check field constraints: strings have min/max lengths, dates must be YYYY-MM-DD, etc.
- The response body includes details about each failing field
slot_unavailable
Cause: The requested time slot is already booked or outside availability.
Fix:
- Re-fetch availability:
GET /v1/availability/{business_id} - Try a different time or resource
- The slot may have been taken between your availability check and booking attempt
outside_operating_hours
Cause: The booking time is outside the business's operating hours.
Fix:
- Check operating hours:
GET /v1/public/hours/{business_id} - Respect
last_booking_time— a 2-hour booking can't start atlast_booking_time - Check for blackout dates:
GET /v1/public/blackout-dates/{business_id}
idempotency_mismatch (409)
Cause: Same idempotency key, different request body.
Fix:
- Use a unique idempotency key for each distinct operation
- If you're retrying the same operation, send the exact same body
- Generate a new key if the request details changed
Idempotency
All POST and PATCH endpoints accept an optional Idempotency-Key header. Send the same key with the same request to safely retry without duplicating side effects.
bash
curl -X POST https://api.availengine.com/v1/bookings/ \
-H "Authorization: Bearer avail_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-key-per-operation" \
-d '{ ... }'Responses are cached for 24 hours. If you send the same key with a different body, you get 409 with code idempotency_mismatch.
Best Practices
- Generate a UUID for each booking creation request
- Use the same key when retrying after a network error
- Don't reuse keys across different operations
- Store the key alongside the booking until you get a response