Errors
Complete reference of Astrocal API error codes, HTTP statuses, and handling guidance.
Every Astrocal API error returns a consistent JSON structure. This page documents all error codes, when they occur, and how to handle them.
Error response format
All errors follow this shape:
{
"error": {
"code": "not_found",
"message": "Booking '770e8400-e29b-41d4-a716-446655440000' not found",
"details": {}
}
}| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error code (use this for programmatic branching) |
message | string | Human-readable description of what went wrong |
details | object | Optional field-level errors or additional context (varies by code) |
Validation errors
Validation errors occur when the request body or query parameters are malformed or contain invalid values.
validation_error (400 / 422)
Returned when request parameters fail schema validation or contain semantically invalid values.
{
"error": {
"code": "validation_error",
"message": "Invalid request parameters",
"details": {
"title": "Required",
"duration_minutes": "Number must be greater than or equal to 5"
}
}
}Common causes:
- Missing required fields
- Values out of range (e.g.,
duration_minutesbelow 5 or above 480) - Invalid format (e.g.,
start_timenot in ISO 8601,timezonenot a valid IANA zone) end_timebeforestart_timein availability rules- Date range exceeds the maximum allowed days for availability queries
How to handle: Fix the request based on the details field. Each key in details is the field name, and the value describes what's wrong.
bad_request (400)
Returned for business logic validation that goes beyond schema checks.
{
"error": {
"code": "bad_request",
"message": "Invalid duration: 45. Allowed values: 15, 30, 60"
}
}Common causes:
- Invalid
durationvalue for a variable-duration event type - Too many attendees for a group booking
- Duplicate attendee emails in a multi-attendee booking
- Waitlist not enabled for the event type
- Paid event type but no Stripe account connected
- Expired or revoked invitation
- Invalid CalDAV preset or missing
server_url
How to handle: Read the message field for specific guidance. These are not retryable -- fix the request.
Authentication errors
unauthorized (401)
Returned when authentication is missing, invalid, or expired.
{
"error": {
"code": "unauthorized",
"message": "Invalid API key"
}
}Common causes:
- Missing
Authorization: Bearerheader - API key not found in the database
- API key has been revoked
- API key has expired
- Invalid or missing cancel token on cancel/reschedule endpoints
How to handle: Check that you're sending a valid API key. For cancel/reschedule endpoints, ensure the ?token= parameter matches the cancel_token from the booking response. Do not retry -- fix the credentials.
Authorization errors
forbidden (403)
Returned when the authenticated user lacks permission for the requested action.
{
"error": {
"code": "forbidden",
"message": "Insufficient permissions"
}
}How to handle: The API key is valid but doesn't have the required permission. Check that the key belongs to the correct organization and that the user's role allows the action.
org_suspended (403)
Returned when the organization has been suspended.
{
"error": {
"code": "org_suspended",
"message": "Organization is suspended"
}
}How to handle: Contact support to resolve the suspension.
plan_limit_exceeded (403)
Returned when the organization has reached its plan's booking limit.
{
"error": {
"code": "plan_limit_exceeded",
"message": "Monthly booking limit reached. Upgrade your plan."
}
}How to handle: Upgrade the organization's plan or wait for the limit to reset.
Not found errors
not_found (404)
Returned when the requested resource doesn't exist or isn't accessible to the current API key.
{
"error": {
"code": "not_found",
"message": "Event type '660e8400-e29b-41d4-a716-446655440000' not found"
}
}Common resources: event types, bookings, calendar connections, webhook endpoints, webhook deliveries, API keys, team members, invitations, waitlist entries.
How to handle: Verify the resource ID is correct. The resource may have been deleted, or it may belong to a different organization.
Conflict errors
Conflict errors indicate the request can't be completed because it contradicts the current state of the system.
conflict (409)
The most common conflict scenarios:
Slot no longer available:
{
"error": {
"code": "conflict",
"message": "The selected time slot is no longer available"
}
}This occurs when two requests try to book the same slot simultaneously. Handle by refreshing available slots and asking the user to pick again.
Duplicate slug:
{
"error": {
"code": "conflict",
"message": "Event type with slug 'quick-call' already exists"
}
}Cannot reschedule cancelled booking:
{
"error": {
"code": "conflict",
"message": "Cannot reschedule a cancelled booking"
}
}Already on the waitlist:
{
"error": {
"code": "conflict",
"message": "Already on waitlist for this slot",
"details": {
"code": "already_on_waitlist"
}
}
}Returned by POST /v1/waitlist when the same email is already waiting for that event type and slot. Treat the existing entry as the live one -- fetch it rather than retrying.
Waitlist is full:
{
"error": {
"code": "conflict",
"message": "Waitlist is full",
"details": {
"code": "waitlist_full"
}
}
}Returned by POST /v1/waitlist when the event type already has waitlist_max entries in waiting status. See the Waitlist guide.
Both waitlist conflicts carry a nested details.code. Branch on it to tell them apart -- the outer code is conflict for both.
Other conflict causes:
- Slot is full (group booking at max attendees)
- Calendar account already connected
- Duplicate team invitation
- Invitation already accepted
- Member already a host for the event type
- Webhook delivery not in
failedstatus (for retries)
How to handle: Read the message to understand the specific conflict. For slot conflicts, refresh availability and retry with a new slot. For duplicate resources, use a different identifier.
booking_cap_reached (409)
Returned when the event type's booking cap has been reached for the current period.
{
"error": {
"code": "booking_cap_reached",
"message": "This event type has reached its booking limit for the current period"
}
}How to handle: Check availability first -- the capped: true flag indicates the cap has been reached. If the event type has waitlists enabled, offer the user to join the waitlist instead.
no_host_available (409)
Returned when booking an event type with an assignment strategy and every assigned host is busy at the requested time. Astrocal checks each host's calendar and assigns the first free host in strategy order; this error means none of them were free.
{
"error": {
"code": "no_host_available",
"message": "No assigned host is available at the requested time",
"details": {
"requested_time": "2026-09-01T14:00:00.000Z",
"hosts_checked": 4
}
}
}How to handle: The whole team is booked at this time -- retrying the same slot will not succeed. Refresh availability and offer a different time. This is distinct from conflict, which covers slot-level clashes.
no_google_calendar_connection (422)
Returned when creating or updating an event type with conferencing_provider: "google_meet" while the organization has no active Google Calendar connection.
{
"error": {
"code": "no_google_calendar_connection",
"message": "Google Meet requires an active Google Calendar connection. Connect one at /dashboard/calendars."
}
}How to handle: Connect a Google Calendar in the dashboard at /dashboard/calendars, then retry. See Conferencing.
no_microsoft_calendar_connection (422)
Returned when creating or updating an event type with conferencing_provider: "microsoft_teams" while the organization has no active Microsoft calendar connection.
{
"error": {
"code": "no_microsoft_calendar_connection",
"message": "Microsoft Teams requires an active Microsoft calendar connection. Connect one at /dashboard/calendars."
}
}How to handle: Connect a Microsoft calendar in the dashboard at /dashboard/calendars, then retry. See Conferencing.
Rate limit errors
rate_limit_exceeded (429)
Returned when you've exceeded the request rate for your plan tier.
Authenticated endpoints (API key):
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Your limit is 60 requests/minute (Launch tier).",
"details": {}
},
"limit": 60,
"window": "1m",
"reset_at": "2026-03-15T14:01:00Z",
"retry_after": 45,
"tier": "starter"
}Public endpoints (per-IP):
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Please try again later."
},
"retry_after": 30
}Rate limit responses include these headers:
| Header | Description |
|---|---|
Retry-After | Seconds to wait before retrying |
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Plan limits:
| Plan | Requests/minute | Requests/day |
|---|---|---|
| Free | 30 | 200 |
| Launch | 60 | 1,000 |
| Grow | 500 | 10,000 |
| Scale | 2,000 | 50,000 |
How to handle: Back off and retry after the Retry-After duration. Implement exponential backoff for sustained rate limiting. See the Rate Limits guide for strategies.
Payment errors
bad_gateway (502)
Returned when an upstream payment provider (Stripe) fails during booking creation.
{
"error": {
"code": "bad_gateway",
"message": "Failed to create payment — please try again"
}
}How to handle: This is a transient error. Retry the request after a short delay. If it persists, check Stripe's status page.
Server errors
internal_error (500)
Returned for unexpected server-side failures.
{
"error": {
"code": "internal_error",
"message": "An unexpected error occurred"
}
}How to handle: This indicates a bug or infrastructure issue on Astrocal's side. Retry once after a short delay. If it persists, contact support.
Best practices
- Branch on
error.code, noterror.message. Messages may change; codes are stable. - Check
error.detailsfor field-level errors. Validation errors include per-field descriptions to help you fix the request. - Always handle 409 on bookings. Slot conflicts are expected in concurrent systems. Refresh availability and let the user pick again.
- Respect
Retry-Afterheaders. Rate limit responses tell you exactly when to retry. - Don't retry 400/401/403/404 errors. These require fixing the request or credentials, not retrying.
- Retry 429/500/502 with backoff. These are transient and may resolve on their own.
Next steps
- Authentication -- Set up API keys
- Rate Limits -- Understand rate limiting and plan tiers
- Bookings -- Handle booking lifecycle errors
- API Reference -- Full endpoint documentation