CREATE FREE ACCOUNT · NO CARD REQUIRED START FREE
  • LOG IN
MuseRelay
  • Home
  • Solutions
    • Customer support
    • Automated sales
    • Bookings & appointments
    • AI Assistants
    • Automation
  • Channels
    • WhatsApp Chatbot
    • Instagram Chatbot
    • Messenger Chatbot
    • Web Chatbot
  • White label
    • White-label chatbot
    • Reseller panel
    • For agencies
    • How to start as reseller
  • Pricing
LOG IN SIGN UP
Home
Getting Started
Chatbots & AI
Agent Tools
Channels
Voice Agents
Operations
White Label
API Reference
Documentación
Documentation / Reference / Calendar API v1 — Referencia de endpoints

Calendar API v1 — Referencia de endpoints

Gestiona calendarios, disponibilidad y citas desde sistemas externos.

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

Autenticación: Authorization: Bearer mrcal_...

Ver Introducción a la API para autenticación, rate limits y convenciones.


Scopes requeridos

ScopeEndpoints que lo requieren
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:adminScope especial: acepta cualquier scope requerido

Si el token no tiene el scope:

{ "error": "Insufficient scope" }

Me

GET /me

Devuelve información básica del token actual.

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

Calendarios

GET /calendars

Lista los calendarios activos accesibles por el token.

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

GET /calendars/{calendar}/services

Lista los tipos de cita activos del calendario.

GET /api/calendar/v1/calendars/12/services
Authorization: Bearer mrcal_...
{
  "data": [
    {
      "id": 5,
      "name": "Corte de pelo",
      "description": "Servicio de ejemplo",
      "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

Lista los recursos activos del calendario (personas, salas, equipos o cualquier recurso reservable).

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

Disponibilidad

GET /calendars/{calendar}/availability

Devuelve slots libres para un servicio en un rango de fechas.

Parámetros:

ParámetroTipoObligatorioReglas
service_idintegerSímin:1
fromdateSíFecha/hora inicial
todateSíDebe ser posterior a 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": "María",
      "capacity_total": 1,
      "capacity_available": 1
    }
  ]
}
Nota: start_local y end_local en disponibilidad son strings HH:mm en la zona horaria del calendario (no ISO 8601). En respuestas de cita son ISO 8601 completo con offset.

Si to no es posterior a from:

{ "message": "to_must_be_after_from" }

Citas

La respuesta de cita usa la misma estructura en todos los endpoints:

{
  "id": 4821,
  "calendar_id": 12,
  "service_id": 5,
  "service_name": "Corte de pelo",
  "resource_id": 3,
  "resource_name": "María",
  "contact_id": 88,
  "customer": {
    "id": 88,
    "name": "Ana Pérez",
    "email": "ana@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": "Reserva desde Retell AI",
  "created_at": "2026-05-11T18:42:00+02:00"
}
Nota: start_local y end_local son las mismas fechas que start_at/end_at convertidas a la zona horaria del calendario (ISO 8601 con offset local).

POST /calendars/{calendar}/appointments

Crea una cita. Usa Idempotency-Key para evitar duplicados en reintentos.

CabeceraObligatoriaDescripción
AuthorizationSíBearer mrcal_...
Content-TypeSíapplication/json
Idempotency-KeyNoClave de idempotencia cacheada 24 horas

Body:

CampoTipoObligatorioReglas
service_idintegerSímin:1
start_atdateSíFecha/hora de inicio
customerobjectSíObjeto requerido
customer.namestringNomax:255
customer.first_namestringNomax:120
customer.last_namestringNomax:120
customer.emailemailNomax:255
customer.phonestringNomax:64
resource_idintegerNomin:1
any_resourcebooleanNoSelecciona el primer recurso libre
statusstringNopending o 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": "Ana Pérez",
    "phone": "+34600111222",
    "email": "ana@example.com"
  },
  "any_resource": true,
  "notes": "Reserva desde Retell AI",
  "source": "retell_ai"
}

Response 201 Created: objeto de cita completo envuelto en { "data": { ... } }.

Si se repite el mismo Idempotency-Key en 24h: 200 con X-Idempotent-Replay: true.

Si no hay recurso disponible: 422 { "message": "slot_unavailable" }


GET /calendars/{calendar}/appointments

Lista citas del calendario.

ParámetroTipoReglas
fromdateOpcional
todateOpcional
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_...

Responde con { "data": [ ... ] } — array de objetos de cita.


GET /appointments/{appointment}

Devuelve una cita por su ID.

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

Responde con { "data": { ... } }.


POST /appointments/{appointment}/cancel

Cancela una cita. Si ya está cancelada, devuelve la cita sin cambios.

CampoTipoReglas
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": "El cliente solicitó cancelación por teléfono",
  "notify_customer": true
}

Responde 200 con el objeto de cita con "status": "cancelled".


PATCH /appointments/{appointment}

Modifica o reprograma una cita. Todos los campos son opcionales.

CampoTipoReglas
service_idintegermin:1
resource_idintegermin:1
start_atdateNueva fecha/hora de inicio
statusstringpending, confirmed, cancelled, completed, no_show
notesstringmax:4000
notify_customerbooleanDefault false

Enviar start_at dispara appointment.rescheduled. Otros campos disparan 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
}

Responde 200 con el objeto de cita actualizado.


Guías de integración

Retell AI

Flujo típico para reservar durante una llamada:

1. Agente detecta intención de reserva
2. GET /calendars/{calendar}/availability con service_id
3. Caller elige horario
4. POST /calendars/{calendar}/appointments con Idempotency-Key
5. Cita creada con created_via = api

Scopes mínimos: calendar:read, calendar:availability, calendar:appointments:create

Usa Idempotency-Key con identificador de llamada + slot para evitar dobles reservas en reintentos.


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 envía notificaciones HTTP POST cuando ocurren eventos de citas.

Eventos disponibles

EventoCuándo se dispara
appointment.createdAl crear una cita
appointment.updatedAl actualizar campos
appointment.cancelledAl cancelar
appointment.rescheduledAl mover a otro horario

Configuración

Panel → Ajustes → API e Integraciones → Webhooks.

CampoDescripción
urlURL destino
eventsLista de eventos; vacío = todos
calendar_idsLista de calendarios; vacío = todos
is_activeActiva o desactiva el endpoint

El secreto generado usa prefijo mrcal_whsec_.

Cabeceras de firma

CabeceraValor
X-MuseRelay-EventNombre del evento, p. ej. appointment.created
X-MuseRelay-DeliveryID de entrega con prefijo mrcal_evt_
X-MuseRelay-TimestampUnix timestamp en segundos
X-MuseRelay-Signaturesha256= + HMAC-SHA256

Verificar la firma

La firma es HMAC-SHA256 sobre {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');
}
Importante: conserva el body crudo antes de parsearlo. Se firma con JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE.

Payload de ejemplo — 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": "Corte de pelo",
    "resource_id": 3,
    "resource_name": "María",
    "contact_id": 88,
    "customer": {
      "id": 88,
      "name": "Ana Pérez",
      "email": "ana@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": "Reserva desde Retell AI"
  }
}

Política de reintentos

PuntoValor
Intentos máximos3
Backoff30 segundos fijo
Timeout HTTP10 segundos
ÉxitoCualquier status HTTP 2xx
En esta página
  • Cargando...

Platform

  • WhatsApp Chatbot
  • Instagram Chatbot
  • Messenger Chatbot
  • Web Chatbot
  • Automation
  • Integrations

White label

  • White-label chatbot
  • Reseller panel
  • For agencies
  • How to start

Resources

  • Blog
  • Documentation
  • Guides
  • API docs
  • 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