CREATE FREE ACCOUNT · NO CARD REQUIRED START FREE
  • LOG IN
MuseRelay
  • Solutions
    • AI agents
    • Customer service
    • Online booking calendar
    • Leads and sales
    • Tickets and issues
  • Channels
    • WhatsApp
    • Instagram
    • Messenger
    • Website chatbot
    • Email
    • Telegram
  • Industries
    • Clinics and practices
    • Hair and beauty
    • Real estate
    • Restaurants
  • White label
  • Resources
    • Blog
    • Documentation
    • API
  • Pricing
LOG IN SIGN UP
Home
Getting Started
Overview Getting started
Chatbots & AI
Agent Tools
MuseRelay Calendar n8n & Make
Channels
Overview WhatsApp Instagram Messenger Website chatbot Email Telegram
Leads & sales
Voice Agents
Customer service
Tickets & notifications
White Label
Overview Getting started Configuration
API Reference
Overview Reference
Documentation
API Reference
Overview
  • API Reference — Introduction
Reference
  • Calendar API v1 — Endpoint reference
Documentation / Reference / Calendar API v1 — Endpoint reference

Calendar API v1 — Endpoint reference

ES EN

Manage calendars, availability and appointments from external systems.

Base URL: https://muserelay.com/api/calendar/v1

Authentication: Authorization: Bearer mrcal_...

See the API introduction for authentication, rate limits and conventions.


Required scopes

ScopeEndpoints that require it
calendar:readGET /me, GET /calendars, GET /calendars/{calendar}/services, GET /calendars/{calendar}/resources
calendar:availabilityGET /calendars/{calendar}/availability
calendar:appointments:readGET /calendars/{calendar}/appointments, GET /appointments/{appointment}
calendar:appointments:createPOST /calendars/{calendar}/appointments
calendar:appointments:cancelPOST /appointments/{appointment}/cancel
calendar:appointments:updatePATCH /appointments/{appointment}
calendar:adminSpecial scope: satisfies any required scope

If the token doesn't have the scope:

{ "error": "Insufficient scope" }

Me

GET /me

Returns basic information about the current token.

GET /api/calendar/v1/me
Authorization: Bearer mrcal_...
{
  "token": {
    "id": 10,
    "name": "Retell AI clinic",
    "scopes": ["calendar:read", "calendar:availability", "calendar:appointments:create"],
    "calendar_ids": [12],
    "expires_at": null
  },
  "workspace_id": 3,
  "organization_id": 2
}

Calendars

GET /calendars

Lists the active calendars the token can access.

GET /api/calendar/v1/calendars
Authorization: Bearer mrcal_...
{
  "data": [
    {
      "id": 12,
      "name": "Main calendar",
      "slug": "main-calendar",
      "timezone": "Europe/Madrid",
      "time_format": "24h",
      "is_default": true,
      "brand_color": "#2563eb",
      "public_url": "https://muserelay.com/c/main-calendar"
    }
  ]
}

GET /calendars/{calendar}/services

Lists the calendar's active appointment types.

GET /api/calendar/v1/calendars/12/services
Authorization: Bearer mrcal_...
{
  "data": [
    {
      "id": 5,
      "name": "Haircut",
      "description": "Sample service",
      "duration_minutes": 30,
      "buffer_before_minutes": 0,
      "buffer_after_minutes": 0,
      "requires_approval": false,
      "allows_group_booking": false,
      "max_party_size": 1,
      "price": "25.00",
      "color": "#2563eb"
    }
  ]
}

GET /calendars/{calendar}/resources

Lists the calendar's active resources (people, rooms, equipment or any bookable resource).

GET /api/calendar/v1/calendars/12/resources
Authorization: Bearer mrcal_...
{
  "data": [
    {
      "id": 3,
      "name": "Maria",
      "type": "person",
      "capacity": 1,
      "color": "#2563eb"
    }
  ]
}

Availability

GET /calendars/{calendar}/availability

Returns free slots for a service within a date range.

Parameters:

ParameterTypeRequiredRules
service_idintegerYesmin:1
fromdateYesStart date/time
todateYesMust be after from
resource_idintegerNomin:1
limitintegerNomin:1, max:500, default 120
GET /api/calendar/v1/calendars/12/availability?service_id=5&from=2026-05-12&to=2026-05-16&limit=20
Authorization: Bearer mrcal_...
{
  "data": [
    {
      "start_at": "2026-05-12T08:00:00+00:00",
      "end_at": "2026-05-12T08:30:00+00:00",
      "day": "2026-05-12",
      "start_local": "10:00",
      "end_local": "10:30",
      "timezone": "Europe/Madrid",
      "resource_id": 3,
      "resource_name": "Maria",
      "capacity_total": 1,
      "capacity_available": 1
    }
  ]
}
Note: in availability, start_local and end_local are HH:mm strings in the calendar's time zone (not ISO 8601). In appointment responses they are full ISO 8601 with offset.

If to is not after from:

{ "message": "to_must_be_after_from" }

Appointments

The appointment response uses the same structure in every endpoint:

{
  "id": 4821,
  "calendar_id": 12,
  "service_id": 5,
  "service_name": "Haircut",
  "resource_id": 3,
  "resource_name": "Maria",
  "contact_id": 88,
  "customer": {
    "id": 88,
    "name": "Jane Smith",
    "email": "jane@example.com",
    "phone": "+34600111222"
  },
  "start_at": "2026-05-12T10:30:00+02:00",
  "end_at": "2026-05-12T11:00:00+02:00",
  "start_local": "2026-05-12T10:30:00+02:00",
  "end_local": "2026-05-12T11:00:00+02:00",
  "timezone": "Europe/Madrid",
  "status": "confirmed",
  "appointment_kind": "booking",
  "created_via": "api",
  "party_size": 1,
  "notes": "Booked from Retell AI",
  "created_at": "2026-05-11T18:42:00+02:00"
}
Note: start_local and end_local are the same dates as start_at/end_at converted to the calendar's time zone (ISO 8601 with local offset).

POST /calendars/{calendar}/appointments

Creates an appointment. Use Idempotency-Key to avoid duplicates on retries.

HeaderRequiredDescription
AuthorizationYesBearer mrcal_...
Content-TypeYesapplication/json
Idempotency-KeyNoIdempotency key cached for 24 hours

Body:

FieldTypeRequiredRules
service_idintegerYesmin:1
start_atdateYesStart date/time
customerobjectYesRequired object
customer.namestringNomax:255
customer.first_namestringNomax:120
customer.last_namestringNomax:120
customer.emailemailNomax:255
customer.phonestringNomax:64
resource_idintegerNomin:1
any_resourcebooleanNoSelects the first free resource
statusstringNopending or confirmed
party_sizeintegerNomin:1, max:999
notesstringNomax:4000
sourcestringNomax:64
external_idstringNomax:191
POST /api/calendar/v1/calendars/12/appointments
Authorization: Bearer mrcal_...
Content-Type: application/json
Idempotency-Key: retell-call-abc123-slot-20260512-1030

{
  "service_id": 5,
  "start_at": "2026-05-12T10:30:00+02:00",
  "customer": {
    "name": "Jane Smith",
    "phone": "+34600111222",
    "email": "jane@example.com"
  },
  "any_resource": true,
  "notes": "Booked from Retell AI",
  "source": "retell_ai"
}

Response 201 Created: full appointment object wrapped in { "data": { ... } }.

If the same Idempotency-Key is repeated within 24h: 200 with X-Idempotent-Replay: true.

If no resource is available: 422 { "message": "slot_unavailable" }


GET /calendars/{calendar}/appointments

Lists the calendar's appointments.

ParameterTypeRules
fromdateOptional
todateOptional
statusstringpending, confirmed, cancelled, completed, no_show
limitintegermin:1, max:200, default 100
GET /api/calendar/v1/calendars/12/appointments?from=2026-05-12&to=2026-05-19&status=confirmed&limit=50
Authorization: Bearer mrcal_...

Responds with { "data": [ ... ] } — an array of appointment objects.


GET /appointments/{appointment}

Returns an appointment by its ID.

GET /api/calendar/v1/appointments/4821
Authorization: Bearer mrcal_...

Responds with { "data": { ... } }.


POST /appointments/{appointment}/cancel

Cancels an appointment. If it is already cancelled, returns the appointment unchanged.

FieldTypeRules
reasonstringmax:1000, default cancelled_via_calendar_api
notify_customerbooleanDefault false
POST /api/calendar/v1/appointments/4821/cancel
Authorization: Bearer mrcal_...
Content-Type: application/json

{
  "reason": "The customer requested cancellation by phone",
  "notify_customer": true
}

Responds 200 with the appointment object with "status": "cancelled".


PATCH /appointments/{appointment}

Modifies or reschedules an appointment. All fields are optional.

FieldTypeRules
service_idintegermin:1
resource_idintegermin:1
start_atdateNew start date/time
statusstringpending, confirmed, cancelled, completed, no_show
notesstringmax:4000
notify_customerbooleanDefault false

Sending start_at triggers appointment.rescheduled. Other fields trigger appointment.updated.

PATCH /api/calendar/v1/appointments/4821
Authorization: Bearer mrcal_...
Content-Type: application/json

{
  "start_at": "2026-05-14T16:00:00+02:00",
  "notify_customer": true
}

Responds 200 with the updated appointment object.


Integration guides

Retell AI

Typical flow for booking during a call:

1. Agent detects booking intent
2. GET /calendars/{calendar}/availability with service_id
3. Caller picks a time
4. POST /calendars/{calendar}/appointments with Idempotency-Key
5. Appointment created with created_via = api

Minimum scopes: calendar:read, calendar:availability, calendar:appointments:create

Use an Idempotency-Key made of call ID + slot to avoid double bookings on retries.


n8n / Make

Method: GET
URL: https://muserelay.com/api/calendar/v1/calendars/{{$json["calendar_id"]}}/availability
Headers:
  Authorization: Bearer mrcal_...
Query params:
  service_id: {{$json["service_id"]}}
  from: {{$today.format("YYYY-MM-DD")}}
  to: {{$today.plus(7, "days").format("YYYY-MM-DD")}}
  limit: 20

Webhooks

MuseRelay sends HTTP POST notifications when appointment events occur.

Available events

EventWhen it fires
appointment.createdWhen an appointment is created
appointment.updatedWhen fields are updated
appointment.cancelledWhen cancelled
appointment.rescheduledWhen moved to another time

Configuration

Panel → Settings → API & Integrations → Webhooks.

FieldDescription
urlDestination URL
eventsList of events; empty = all
calendar_idsList of calendars; empty = all
is_activeEnables or disables the endpoint

The generated secret uses the mrcal_whsec_ prefix.

Signature headers

HeaderValue
X-MuseRelay-EventEvent name, e.g. appointment.created
X-MuseRelay-DeliveryDelivery ID with the mrcal_evt_ prefix
X-MuseRelay-TimestampUnix timestamp in seconds
X-MuseRelay-Signaturesha256= + HMAC-SHA256

Verify the signature

The signature is HMAC-SHA256 over {X-MuseRelay-Timestamp}.{raw_json_body}.

PHP:

$secret    = 'mrcal_whsec_...';
$timestamp = $_SERVER['HTTP_X_MUSERELAY_TIMESTAMP'] ?? '';
$rawBody   = file_get_contents('php://input');
$expected  = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
$received  = $_SERVER['HTTP_X_MUSERELAY_SIGNATURE'] ?? '';

if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit('Invalid signature');
}

Node.js:

const crypto = require('crypto');
const secret = 'mrcal_whsec_...';
const timestamp = req.headers['x-muserelay-timestamp'] ?? '';
const signed = `${timestamp}.${req.rawBody}`;
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(signed).digest('hex');
const received = req.headers['x-muserelay-signature'] ?? '';

if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))) {
  res.status(401).send('Invalid signature');
}
Important: keep the raw body before parsing it. It is signed with JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE.

Sample payload — appointment.created

{
  "id": "mrcal_evt_550e8400-e29b-41d4-a716-446655440000",
  "event": "appointment.created",
  "created_at": "2026-05-11T18:42:00+00:00",
  "data": {
    "id": 4821,
    "appointment_id": 4821,
    "calendar_id": 12,
    "workspace_id": 3,
    "organization_id": 2,
    "service_id": 5,
    "service_name": "Haircut",
    "resource_id": 3,
    "resource_name": "Maria",
    "contact_id": 88,
    "customer": {
      "id": 88,
      "name": "Jane Smith",
      "email": "jane@example.com",
      "phone": "+34600111222"
    },
    "start_at": "2026-05-12T10:30:00+02:00",
    "end_at": "2026-05-12T11:00:00+02:00",
    "timezone": "Europe/Madrid",
    "status": "confirmed",
    "appointment_kind": "booking",
    "created_via": "api",
    "party_size": 1,
    "notes": "Booked from Retell AI"
  }
}

Retry policy

ItemValue
Maximum attempts3
Backofffixed 30 seconds
Timeout HTTP10 seconds
SuccessAny HTTP 2xx status
On this page
  • Cargando...

Platform

  • WhatsApp
  • Instagram
  • Messenger
  • Website chatbot
  • Email
  • Telegram
  • AI agents
  • Online booking calendar

White label

  • White-label chatbot
  • What white label is
  • Getting started as a reseller
  • Customise your panel

Resources

  • Blog
  • Documentation
  • API
  • Pricing
  • Contact

Legal

  • Legal Notice
  • Privacy
  • Terms
  • Cookies
  • DPA
  • Subprocessors
  • Integrations & GDPR
  • Data Deletion

© 2026 Muse Layer LLC · Infrastructure and data processing in the EU · GDPR compliant

Privacy | Legal notice | Terms | Cookies
ES | EN

LOG IN

Access your MuseRelay account

Forgot your password?
Don't have an account? Create new account

Create new account

Join the MuseRelay beta community

Contact us to get your code: webmaster@muserelay.com
Already have an account? LOG IN
We value your privacy

We use strictly necessary technical cookies for the platform to work. No advertising or third-party tracking cookies. Cookie Policy · Privacy