Astrocal
Guides

Event Types

Create and manage bookable event types with duration, pricing, and booking caps.

Event types are templates for bookable meetings. Each event type defines a duration, availability rules, and optional settings like pricing, buffer times, and booking caps.

Prerequisites

Creating an event type

Create an event type by sending a POST request to /v1/event-types:

curl -X POST https://api.astrocal.dev/v1/event-types \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "30 Minute Meeting",
    "slug": "30-min-meeting",
    "duration_minutes": 30
  }'
const response = await fetch("https://api.astrocal.dev/v1/event-types", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "30 Minute Meeting",
    slug: "30-min-meeting",
    duration_minutes: 30,
  }),
});
const data = await response.json();

Try it in the API playground →

Required fields

FieldTypeDescription
titlestringDisplay name (1-255 characters)
slugstringURL-safe identifier (lowercase, hyphens only)
duration_minutesnumberMeeting length in minutes (5-480)

Optional fields

FieldTypeDefaultDescription
descriptionstringnullEvent type description (max 1000 chars)
buffer_before_minutesnumber0Buffer time before meetings (0-120 minutes)
buffer_after_minutesnumber0Buffer time after meetings (0-120 minutes)
colorstring"#3b82f6"Hex color for calendar display
max_attendeesnumber1Maximum attendees per slot (1-100). Values > 1 enable group bookings.
duration_optionsnumber[]nullAvailable durations for variable-length bookings (e.g., [15, 30, 60])
price_amountnumber|nullnullPrice in cents (min 100). See Payments
price_currencystring"usd"3-letter ISO currency code
reminder_intervalsnumber[][1440, 60]Reminder email intervals in minutes before the meeting
booking_capnumber|nullnullMaximum bookings allowed per period (1-10000)
booking_cap_periodstring|nullnullCap period: "daily", "weekly", "monthly", or "total"
booking_page_enabledbooleanfalseShow this event type on your organization's public booking page

Updating an event type

Update an event type with a PATCH request. Only include the fields you want to change:

curl -X PATCH https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Quick Chat",
    "duration_minutes": 15,
    "active": false
  }'
const response = await fetch(
  "https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID",
  {
    method: "PATCH",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      title: "Quick Chat",
      duration_minutes: 15,
      active: false,
    }),
  }
);
const data = await response.json();

Try it in the API playground →

The active field is only available on update (not creation). Setting active: false hides the event type from public availability queries.

Booking caps

Booking caps let you limit the number of bookings an event type can receive within a time period. This is useful for limiting consultation availability, capping class sizes per week, or restricting total bookings.

Setting a booking cap

Both booking_cap and booking_cap_period must be set together (or both null):

# Limit to 20 bookings per week
curl -X PATCH https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "booking_cap": 20,
    "booking_cap_period": "weekly"
  }'
const response = await fetch(
  "https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID",
  {
    method: "PATCH",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      booking_cap: 20,
      booking_cap_period: "weekly",
    }),
  }
);
const data = await response.json();

Try it in the API playground →

Cap periods

PeriodDescription
dailyResets at midnight in the event type's timezone
weeklyResets at the start of each week (Monday) in the event type's timezone
monthlyResets at the start of each month in the event type's timezone
totalNever resets -- counts all bookings since the cap was set

How caps are enforced

  1. Availability layer: When a cap is reached, the availability endpoint returns an empty slots array and a capped: true flag. This lets your UI show that the event type is fully booked for the period.

  2. Booking layer: If a booking is attempted after the cap is reached (e.g., due to a race condition), the API returns a 409 Conflict with error code booking_cap_reached.

Checking remaining capacity

The event type response includes a bookings_remaining field when a cap is configured:

curl https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID \
  -H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
  "https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID",
  {
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
    },
  }
);
const data = await response.json();

Try it in the API playground →

# Response (with booking cap)
{
  "id": "...",
  "title": "Weekly Consultation",
  "booking_cap": 20,
  "booking_cap_period": "weekly",
  "bookings_remaining": 14,
  ...
}

bookings_remaining is null when no cap is configured.

Removing a booking cap

Set both fields to null:

curl -X PATCH https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "booking_cap": null,
    "booking_cap_period": null
  }'
const response = await fetch(
  "https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID",
  {
    method: "PATCH",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      booking_cap: null,
      booking_cap_period: null,
    }),
  }
);
const data = await response.json();

Try it in the API playground →

Variable-duration bookings

To let invitees choose between multiple meeting lengths, set duration_options:

curl -X POST https://api.astrocal.dev/v1/event-types \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Consultation",
    "slug": "consultation",
    "duration_minutes": 30,
    "duration_options": [15, 30, 60]
  }'
const response = await fetch("https://api.astrocal.dev/v1/event-types", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "Consultation",
    slug: "consultation",
    duration_minutes: 30,
    duration_options: [15, 30, 60],
  }),
});
const data = await response.json();

Try it in the API playground →

duration_minutes is the default duration and must be included in duration_options. When checking availability, pass duration to get slots for a specific length.

Listing event types

curl https://api.astrocal.dev/v1/event-types \
  -H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch("https://api.astrocal.dev/v1/event-types", {
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
  },
});
const data = await response.json();

Try it in the API playground →

Filter by active status with ?active=true or ?active=false. Uses cursor-based pagination with starting_after and limit.

Deleting an event type

curl -X DELETE https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID \
  -H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
  "https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID",
  {
    method: "DELETE",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
    },
  }
);
const data = await response.json();

Try it in the API playground →

Deletion is a soft delete -- the event type is marked as deleted but existing bookings are preserved.

Booking pages

Each organization has a public booking page at https://book.astrocal.dev/{orgSlug} that lists all eligible event types. Invitees can browse and book directly from this page without needing an API key.

Enabling an event type on the booking page

By default, event types are not shown on the booking page. To include an event type, set booking_page_enabled to true:

curl -X PATCH https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"booking_page_enabled": true}'
const response = await fetch(
  "https://api.astrocal.dev/v1/event-types/YOUR_EVENT_TYPE_ID",
  {
    method: "PATCH",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ booking_page_enabled: true }),
  }
);
const data = await response.json();

Try it in the API playground →

In the dashboard, you can toggle this under Event Types → Settings → Enable booking page.

An event type appears on the booking page only when both active is true and booking_page_enabled is true.

Error handling

StatusError CodeDescription
409conflictSlug already exists within the organization
409booking_cap_reachedBooking cap has been reached for the current period
404not_foundEvent type does not exist or is not accessible

Next Steps

  • Quickstart -- Set up availability and create your first booking
  • Bookings -- Create, cancel, and reschedule bookings
  • Payments -- Add pricing to event types
  • API Reference -- Full endpoint documentation

On this page