Bookings (appointments) represent scheduled meetings between hosts and attendees. Each booking links a meeting type to a specific time slot.
Booking Lifecycle
Create a Booking
curl -X POST "https://api.meetergo.com/v4/booking" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"meetingTypeId": "770e8400-e29b-41d4-a716-446655440002",
"start": "2024-01-15T09:00:00+01:00",
"hostIds": ["550e8400-e29b-41d4-a716-446655440000"],
"attendee": {
"email": "customer@example.com",
"firstname": "Jane",
"lastname": "Doe",
"receiveReminders": true,
"notes": {}
}
}'
Request Body
| Field | Type | Required | Description |
|---|
meetingTypeId | string | Yes | Meeting type UUID |
start | string | Yes | Start time (ISO 8601 with timezone) |
hostIds | string[] | No* | Host user UUIDs |
queueId | string | No* | Queue ID for round-robin (alternative to hostIds) |
attendee | object | Yes | Attendee information |
duration | number | No | Duration in minutes (must match an allowed duration on the meeting type) |
channel | string | No | Meeting channel: google, zoom, teamsForBusiness2, phone, phone-incoming, local, local-attendee, connect, webex, whatsapp |
location | string | No | For local channel: selects from configured locations. For local-attendee: can also use attendee.notes.address |
icsTitle | string | No | Custom calendar invite title (max 200 chars), overrides meeting type default |
icsDescription | string | No | Custom calendar invite description (max 2000 chars), overrides meeting type default |
skipNotifications | boolean | No | Skip email notifications (default: false) |
phoneOnlyBooking | boolean | No | Use phone as identifier instead of email. See Voice AI Bot |
*Either hostIds or queueId is required
Attendee Object
| Field | Type | Required | Description |
|---|
email | string | Yes* | Attendee email |
firstname | string | No† | First name (†either fullname or firstname/lastname required) |
lastname | string | No† | Last name |
fullname | string | No† | Full name (alternative to first/last) |
phone | string | No | Phone number |
receiveReminders | boolean | Yes | Whether the attendee receives reminder emails |
notes | object | Yes | Key-value object for form field responses and UTM parameters. Pass {} if none |
language | string | No | Locale for notifications (en, de, etc.) |
timezone | string | No | Attendee timezone (e.g. Europe/Berlin) |
bringalongEmails | string[] | No | Additional attendee emails (max 5) |
*Not required when phoneOnlyBooking is true and the meeting type has allowPhoneOnlyBooking enabled. See Voice AI Bot for details.
See Create Booking API Reference for full field documentation, examples with custom ICS content, and UTM tracking.
Response (Standard)
{
"appointmentId": "appt-uuid-12345",
"secret": "secret-token-xyz"
}
Store the secret to cancel or reschedule without authentication.
Response (Double Opt-In)
If the meeting type requires email confirmation:
{
"bookingType": "DoubleOptIn",
"provisionalBookingId": "provisional-uuid-123"
}
The booking is finalized after the attendee confirms via email.
Get Appointments
Get Paginated Appointments
curl -X GET "https://api.meetergo.com/v4/appointment/paginated/?page=1&limit=20" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "x-meetergo-api-user-id: {userId}"
Get Single Appointment
curl -X GET "https://api.meetergo.com/v4/appointment/{appointmentId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "x-meetergo-api-user-id: {userId}"
Response
{
"id": "appt-uuid-12345",
"start": "2024-01-15T09:00:00+01:00",
"end": "2024-01-15T09:30:00+01:00",
"isCancelled": false,
"meetingType": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"name": "Discovery Call",
"duration": 30
},
"attendees": [
{
"id": "att-uuid-789",
"email": "customer@example.com",
"firstname": "Jane",
"lastname": "Doe"
}
],
"hosts": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "john@example.com",
"givenName": "John",
"familyName": "Smith"
}
]
}
Cancel an Appointment
As Host (Authenticated)
curl -X POST "https://api.meetergo.com/v4/appointment/{appointmentId}/cancel" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "x-meetergo-api-user-id: {userId}" \
-H "Content-Type: application/json" \
-d '{
"reason": "Unable to attend"
}'
With Secret (Unauthenticated)
For self-service cancellation by attendees:
curl -X POST "https://api.meetergo.com/booking/confirm/cancel/{appointmentId}/{secret}" \
-H "Content-Type: application/json" \
-d '{
"cancellationReason": "Schedule conflict"
}'
Both methods trigger:
- Cancellation emails to all parties
booking_cancelled webhook event
- Calendar event removal (if integrated)
Reschedule an Appointment
With Secret (Unauthenticated)
curl -X POST "https://api.meetergo.com/booking/confirm/reschedule/" \
-H "Content-Type: application/json" \
-d '{
"appointmentId": "appt-uuid-12345",
"secret": "secret-token-xyz",
"start": "2024-01-16T10:00:00+01:00"
}'
As Host (Authenticated)
curl -X POST "https://api.meetergo.com/v4/appointment/{appointmentId}/reschedule" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "x-meetergo-api-user-id: {userId}" \
-H "Content-Type: application/json" \
-d '{
"start": "2024-01-16T10:00:00+01:00"
}'
Response
{
"appointmentId": "appt-uuid-12345",
"secret": "secret-token-xyz"
}
Rescheduling triggers:
- Updated calendar invites
- Notification emails
booking_rescheduled webhook event
Pass form field responses and UTM tracking data via attendee.notes:
{
"meetingTypeId": "770e8400-e29b-41d4-a716-446655440002",
"start": "2024-01-15T09:00:00+01:00",
"hostIds": ["550e8400-e29b-41d4-a716-446655440000"],
"attendee": {
"email": "customer@example.com",
"firstname": "Jane",
"receiveReminders": true,
"notes": {
"company_size": "50-200",
"referral_source": "Google",
"utm_source": "website",
"utm_content": "demo_cta"
}
}
}
Notes values are:
- Included in webhook payloads
- Visible in the dashboard
- Synced to CRM integrations (UTM parameters can be mapped to Salesforce fields via Integrations → Salesforce → Settings)
Round-Robin Bookings
For team meeting types, use queueId instead of hostIds:
{
"meetingTypeId": "770e8400-e29b-41d4-a716-446655440002",
"start": "2024-01-15T09:00:00+01:00",
"queueId": "queue-uuid-123",
"attendee": {
"email": "customer@example.com",
"receiveReminders": true,
"notes": {}
}
}
The system automatically selects the next available team member based on:
- Round-robin rotation
- Current availability
- Existing bookings
Error Handling
Time Slot Unavailable
{
"statusCode": 400,
"message": "The selected time slot is no longer available",
"error": "Bad Request"
}
Solution: Query fresh availability and select another slot.
Invalid Duration
{
"statusCode": 400,
"message": "Duration must match one of the allowed durations for this meeting type",
"error": "Bad Request"
}
Solution: Check the meeting type’s allowed durations.
Missing Host
{
"statusCode": 400,
"message": "hostIds or queueId is required",
"error": "Bad Request"
}
Solution: Provide either hostIds array or queueId.
Best Practices
Always query availability first - Don’t book without checking available slots
Store the secret - You’ll need it for self-service cancel/reschedule
Handle race conditions - Slots can be booked between availability query and booking
Use webhooks - Get real-time notifications instead of polling
Time format - Always include timezone offset in ISO 8601 format (e.g., +01:00)