API de Sarao

Conecta Sarao con tus herramientas: eventos, entradas, pedidos, pagos, devoluciones, clientes, reservas y listas; vende entradas cobrando con Sarao y recibe avisos con los webhooks.

Empezar

La API de Sarao te deja leer y escribir los datos de tu organización desde tus propias herramientas: tu web, tu CRM, tu app o tu programa de contabilidad. Es REST, responde siempre JSON y está versionada en /v1.

URL basehttps://api.saraotickets.com/v1
FormatoJSON (UTF-8). Las peticiones con cuerpo llevan Content-Type: application/json.
ImportesEn céntimos de euro (enteros): 1500 = 15,00 €. currency siempre es eur.
FechasISO 8601 en UTC (2026-12-05T22:00:00.000Z).
IdsUUID.

Pide una clave en el panel: Ajustes → Developer Portal → Solicitar clave (solo administradores). Elige qué puede hacer y hasta cuándo vale. Tu primera petición:

curl
curl https://api.saraotickets.com/v1/events \
  -H "Authorization: Bearer sk_live_…"

Autenticación

Manda la clave en la cabecera Authorization: Bearer sk_live_… en cada petición, siempre por HTTPS y siempre desde tu servidor: nunca la pongas en una web o una app que se pueda descargar. Cada clave es de una organización y solo ve y toca los datos de esa organización.

La clave en claro solo se enseña al crearla. Si se pierde o se filtra, anúlala en el Developer Portal y pide otra: deja de valer al momento. Una clave caducada, anulada o mal escrita responde 401.

Permisos de las claves

Cada clave tiene sus permisos por recurso: recurso:read (leer) o recurso:write (leer y escribir). all:read lee todo, también lo que se añada en el futuro, pero no escribe ni vende. Si la clave no tiene el permiso que pide un endpoint, la respuesta es 403.

RecursoPermisosEndpoints
Eventosevent:read/v1/events, tipos de entrada
Entradasticket:read/v1/tickets
Pedidosorder:read/v1/orders
Pagospayment:read/v1/payments
Devolucionesrefund:read/v1/refunds
Reservasreservation:read · reservation:write/v1/reservations
Listaslist:read · list:write/v1/lists y sus invitados
Clientescustomer:read · customer:write/v1/customers
Monederowallet:readReservado (todavía sin endpoints)
Webhookswebhook:read · webhook:write/v1/webhook-endpoints
Vender entradascheckout:write/v1/checkouts (vender entradas)

Errores

Los errores tienen siempre la misma forma. code es estable (puedes programar con él); message está en español y puede cambiar. fields (solo en errores de validación) dice qué falla en cada campo. Cada respuesta lleva X-Request-Id: dánoslo si escribes a soporte.

json
{
  "error": {
    "code": "validation_error",
    "message": "Pon el nombre del cliente",
    "fields": { "name": "Pon el nombre del cliente" }
  }
}
HTTPcodeCuándo
400invalid_requestParámetros o JSON mal formados (limit fuera de rango, fecha no válida, cuerpo que no es JSON…).
401unauthorizedSin clave, o clave no válida, caducada o anulada.
403forbiddenLa clave no tiene el permiso que hace falta.
404not_foundNo existe (o es de otra organización: para tu clave, no existe).
409conflictChoca con el estado actual: email repetido, sin plazas, evento que ya no está a la venta…
422validation_errorLos datos no cumplen las reglas (mira fields).
429rate_limitedDemasiadas peticiones: espera lo que diga Retry-After.
500internal_errorError nuestro. Reintenta más tarde con espera creciente.

Paginación y filtros

Los listados devuelven { object: "list", data, has_more, next_cursor }, del más reciente al más antiguo. Pide la página siguiente con ?cursor= y el next_cursor que te ha llegado, hasta que has_more sea false. limit va de 1 a 100 (25 por defecto).

curl
curl "https://api.saraotickets.com/v1/orders?status=paid&created_from=2026-11-01&limit=100" \
  -H "Authorization: Bearer sk_live_…"

# Página siguiente
curl "https://api.saraotickets.com/v1/orders?status=paid&created_from=2026-11-01&limit=100&cursor=WyIyMDI2…" \
  -H "Authorization: Bearer sk_live_…"

Fechas

Los filtros de fecha (created_from/created_to; en eventos, starts_from/starts_to) aceptan un día (2026-11-01, el inicio de ese día en UTC) o un momento con zona (2026-11-01T20:00:00+01:00). «Desde» incluye ese momento; «hasta» no.

Límites

Cada clave puede hacer 120 peticiones por minuto. Cada respuesta lleva X-RateLimit-Limit y X-RateLimit-Remaining; pasado el límite, la respuesta es 429 con Retry-After (segundos). Para sincronizar mucho, usa limit=100 y los filtros de fecha, y mejor aún, los webhooks.

Vender entradas desde tu app

Tu app puede vender entradas, pero el cobro lo hace siempre Sarao: la API nunca crea pedidos ni entradas pagados. El flujo: 1) lee los tipos de entrada del evento (GET /v1/events/{id}/ticket-types); 2) crea un checkout (POST /v1/checkouts); 3) lleva al comprador a su url; 4) el comprador paga en Sarao con los mismos precios, gastos de gestión, aforo y condiciones que en la web, y recibe sus entradas por email; 5) te llega el webhook order.paid (o consulta el checkout: status: completed y su order_id). Las devoluciones se hacen desde el panel, como siempre. Los pedidos llevan el api_key_id de la clave que los vendió.

Eventos y tipos de entrada · Lectura

  • GET/v1/events
  • GET/v1/events/{id}
  • GET/v1/events/{id}/ticket-types
GET/v1/eventsevent:read

Eventos de tu organización, del que empieza más tarde al que empezó antes.

ParámetroDescripción
statusdraft, published o cancelled.
starts_from / starts_toEventos que empiezan en ese rango (ISO 8601).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "event",
      "id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
      "title": "Noche de Reggaeton",
      "status": "published",
      "starts_at": "2026-12-05T22:00:00.000Z",
      "ends_at": "2026-12-06T05:00:00.000Z",
      "timezone": "Europe/Madrid",
      "venue": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Sala Central",
        "city": "Madrid"
      },
      "capacity": 800,
      "min_age": 18,
      "image_url": "https://saraotickets.com/storage/v1/object/public/…/cartel.jpg",
      "online_sales": true,
      "sales_end_at": null,
      "url": "https://saraotickets.com/e/noche-de-reggaeton",
      "created_at": "2026-11-02T10:15:00.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "WyIyMDI2LTExLTA4VDIzOjEwOjAwLjAwMFoiLCI5YjE…"
}
GET/v1/events/{id}event:read

Un evento.

Respuesta
{
  "object": "event",
  "id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "title": "Noche de Reggaeton",
  "status": "published",
  "starts_at": "2026-12-05T22:00:00.000Z",
  "ends_at": "2026-12-06T05:00:00.000Z",
  "timezone": "Europe/Madrid",
  "venue": {
    "id": "a1b2c3d4-0000-4000-8000-000000000001",
    "name": "Sala Central",
    "city": "Madrid"
  },
  "capacity": 800,
  "min_age": 18,
  "image_url": "https://saraotickets.com/storage/v1/object/public/…/cartel.jpg",
  "online_sales": true,
  "sales_end_at": null,
  "url": "https://saraotickets.com/e/noche-de-reggaeton",
  "created_at": "2026-11-02T10:15:00.000Z"
}
GET/v1/events/{id}/ticket-typesevent:read

Tipos de entrada y tarifas de mesa del evento con el precio de ahora (también el precio dinámico). kind es ticket o table.

sellable_by_api indica si el tipo se puede vender con POST /v1/checkouts (activo y con venta por link de pago).

Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "ticket_type",
      "id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
      "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
      "name": "Entrada general con copa",
      "kind": "ticket",
      "description": null,
      "price_cents": 1500,
      "currency": "eur",
      "active": true,
      "hidden": false,
      "sold_out": false,
      "capacity": 500,
      "min_per_order": 1,
      "max_per_order": 10,
      "persons": 1,
      "sales_start": null,
      "sales_end": null,
      "sellable_by_api": true
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Pedidos, pagos y devoluciones · Lectura

  • GET/v1/orders
  • GET/v1/orders/{id}
  • GET/v1/payments
  • GET/v1/refunds
  • GET/v1/refunds/{id}
GET/v1/ordersorder:read

Pedidos de tu organización, del más reciente al más antiguo. api_key_id dice qué clave lo vendió (si vino de la API).

ParámetroDescripción
event_idSolo los de un evento.
customer_idSolo los de un cliente.
statuspending, paid, expired, cancelled, refunded o failed.
created_fromCreados desde este momento (incluido). ISO 8601: 2026-11-01 o 2026-11-01T20:00:00Z.
created_toCreados antes de este momento (no incluido).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "order",
      "id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
      "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
      "status": "paid",
      "channel": "online",
      "customer": {
        "id": "c0ffee00-3333-4abc-9def-001122334455",
        "email": "laura@ejemplo.com",
        "name": "Laura Gómez",
        "phone": "+34600000000"
      },
      "currency": "eur",
      "subtotal_cents": 3000,
      "discount_cents": 0,
      "fee_cents": 180,
      "total_cents": 3180,
      "refunded_cents": 0,
      "net_cents": 3180,
      "items": [
        {
          "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
          "quantity": 2,
          "unit_price_cents": 1500,
          "unit_discount_cents": 0,
          "unit_fee_cents": 90,
          "extra_cents": 0
        }
      ],
      "promoter_id": null,
      "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
      "checkout_id": "e1d2c3b4-5555-4a6b-8c7d-9e0f1a2b3c4d",
      "paid_at": "2026-11-08T23:10:00.000Z",
      "created_at": "2026-11-08T23:08:41.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "WyIyMDI2LTExLTA4VDIzOjEwOjAwLjAwMFoiLCI5YjE…"
}
GET/v1/orders/{id}order:read

Un pedido con sus líneas.

Respuesta
{
  "object": "order",
  "id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
  "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "status": "paid",
  "channel": "online",
  "customer": {
    "id": "c0ffee00-3333-4abc-9def-001122334455",
    "email": "laura@ejemplo.com",
    "name": "Laura Gómez",
    "phone": "+34600000000"
  },
  "currency": "eur",
  "subtotal_cents": 3000,
  "discount_cents": 0,
  "fee_cents": 180,
  "total_cents": 3180,
  "refunded_cents": 0,
  "net_cents": 3180,
  "items": [
    {
      "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
      "quantity": 2,
      "unit_price_cents": 1500,
      "unit_discount_cents": 0,
      "unit_fee_cents": 90,
      "extra_cents": 0
    }
  ],
  "promoter_id": null,
  "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
  "checkout_id": "e1d2c3b4-5555-4a6b-8c7d-9e0f1a2b3c4d",
  "paid_at": "2026-11-08T23:10:00.000Z",
  "created_at": "2026-11-08T23:08:41.000Z"
}
GET/v1/paymentspayment:read

Lo cobrado al comprar en cada pedido pagado (o devuelto después) con importe. net_cents = cobrado menos devuelto.

ParámetroDescripción
event_idSolo los de un evento.
created_fromCreados desde este momento (incluido). ISO 8601: 2026-11-01 o 2026-11-01T20:00:00Z.
created_toCreados antes de este momento (no incluido).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "payment",
      "id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
      "order_id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
      "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
      "provider": "stripe",
      "currency": "eur",
      "amount_cents": 3180,
      "refunded_cents": 0,
      "net_cents": 3180,
      "order_status": "paid",
      "paid_at": "2026-11-08T23:10:00.000Z",
      "created_at": "2026-11-08T23:08:41.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/refundsrefund:read

Devoluciones de tu organización. Las devoluciones se hacen desde el panel; la API solo las lee.

ParámetroDescripción
order_idSolo las de un pedido.
created_fromCreados desde este momento (incluido). ISO 8601: 2026-11-01 o 2026-11-01T20:00:00Z.
created_toCreados antes de este momento (no incluido).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "refund",
      "id": "f0e1d2c3-7777-4b5a-9c8d-7e6f5a4b3c2d",
      "order_id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
      "ticket_id": "0a1b2c3d-6666-4e5f-8a9b-c0d1e2f3a4b5",
      "currency": "eur",
      "amount_cents": 1500,
      "fee_cents": 0,
      "addon_cents": 0,
      "status": "succeeded",
      "reason": "Evento aplazado",
      "provider": "stripe",
      "created_at": "2026-11-09T10:00:00.000Z",
      "updated_at": "2026-11-09T10:00:05.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/refunds/{id}refund:read

Una devolución.

Respuesta
{
  "object": "refund",
  "id": "f0e1d2c3-7777-4b5a-9c8d-7e6f5a4b3c2d",
  "order_id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
  "ticket_id": "0a1b2c3d-6666-4e5f-8a9b-c0d1e2f3a4b5",
  "currency": "eur",
  "amount_cents": 1500,
  "fee_cents": 0,
  "addon_cents": 0,
  "status": "succeeded",
  "reason": "Evento aplazado",
  "provider": "stripe",
  "created_at": "2026-11-09T10:00:00.000Z",
  "updated_at": "2026-11-09T10:00:05.000Z"
}

Entradas · Lectura

  • GET/v1/tickets
  • GET/v1/tickets/{id}
GET/v1/ticketsticket:read

Entradas emitidas. validated_at es el primer acceso válido en la puerta (null si aún no ha entrado).

ParámetroDescripción
event_idSolo las de un evento.
order_idSolo las de un pedido.
statusvalid, cancelled, void o refunded.
created_fromCreados desde este momento (incluido). ISO 8601: 2026-11-01 o 2026-11-01T20:00:00Z.
created_toCreados antes de este momento (no incluido).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "ticket",
      "id": "0a1b2c3d-6666-4e5f-8a9b-c0d1e2f3a4b5",
      "order_id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
      "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
      "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
      "status": "valid",
      "holder": {
        "name": "Laura Gómez",
        "email": "laura@ejemplo.com"
      },
      "persons": 1,
      "validated_at": null,
      "created_at": "2026-11-08T23:10:00.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "WyIyMDI2LTExLTA4VDIzOjEwOjAwLjAwMFoiLCI5YjE…"
}
GET/v1/tickets/{id}ticket:read

Una entrada.

Respuesta
{
  "object": "ticket",
  "id": "0a1b2c3d-6666-4e5f-8a9b-c0d1e2f3a4b5",
  "order_id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
  "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
  "status": "valid",
  "holder": {
    "name": "Laura Gómez",
    "email": "laura@ejemplo.com"
  },
  "persons": 1,
  "validated_at": null,
  "created_at": "2026-11-08T23:10:00.000Z"
}

Clientes · Lectura

  • GET/v1/customers
  • GET/v1/customers/{id}
GET/v1/customerscustomer:read

Clientes del CRM de tu organización (sin los eliminados).

ParámetroDescripción
emailBusca por email exacto (sin distinguir mayúsculas).
created_fromCreados desde este momento (incluido). ISO 8601: 2026-11-01 o 2026-11-01T20:00:00Z.
created_toCreados antes de este momento (no incluido).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "customer",
      "id": "c0ffee00-3333-4abc-9def-001122334455",
      "email": "laura@ejemplo.com",
      "name": "Laura Gómez",
      "phone": "+34600000000",
      "birthdate": "1999-04-12",
      "gender": "female",
      "country": "ES",
      "province": "Madrid",
      "city": "Madrid",
      "postal_code": "28004",
      "instagram": "lauragomez",
      "notes": null,
      "marketing_consent": false,
      "created_at": "2026-10-01T18:30:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/customers/{id}customer:read

Un cliente.

Respuesta
{
  "object": "customer",
  "id": "c0ffee00-3333-4abc-9def-001122334455",
  "email": "laura@ejemplo.com",
  "name": "Laura Gómez",
  "phone": "+34600000000",
  "birthdate": "1999-04-12",
  "gender": "female",
  "country": "ES",
  "province": "Madrid",
  "city": "Madrid",
  "postal_code": "28004",
  "instagram": "lauragomez",
  "notes": null,
  "marketing_consent": false,
  "created_at": "2026-10-01T18:30:00.000Z"
}

Reservas · Lectura

  • GET/v1/reservations
  • GET/v1/reservations/{id}
GET/v1/reservationsreservation:read

Reservas de mesa confirmadas. status es el estado de la reserva; order_status, el de su pedido.

ParámetroDescripción
event_idSolo las de un evento.
statuspending, pending_review, accepted, arrived, seated, charged, no_show, released, rejected, not_completed o cancelled_by_customer.
created_fromCreados desde este momento (incluido). ISO 8601: 2026-11-01 o 2026-11-01T20:00:00Z.
created_toCreados antes de este momento (no incluido).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "reservation",
      "id": "d4c3b2a1-8888-4f0e-9d8c-7b6a5f4e3d2c",
      "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
      "order_status": "paid",
      "status": "accepted",
      "rate_id": "b7a6c5d4-9999-4e3f-8a2b-1c0d9e8f7a6b",
      "customer": {
        "id": "c0ffee00-3333-4abc-9def-001122334455",
        "name": "Laura Gómez",
        "email": "laura@ejemplo.com",
        "phone": "+34600000000"
      },
      "party_size": 6,
      "time": "00:30",
      "notes": "Cumpleaños",
      "spaces": [
        {
          "id": "a9b8c7d6-aaaa-4b5c-8d7e-6f5a4b3c2d1e",
          "label": "Mesa 12"
        }
      ],
      "currency": "eur",
      "paid_cents": 0,
      "deposit_cents": null,
      "balance_due_cents": 30000,
      "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
      "created_at": "2026-11-08T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/reservations/{id}reservation:read

Una reserva, con los pagos apuntados en el local (venue_payments).

Respuesta
{
  "object": "reservation",
  "id": "d4c3b2a1-8888-4f0e-9d8c-7b6a5f4e3d2c",
  "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "order_status": "paid",
  "status": "accepted",
  "rate_id": "b7a6c5d4-9999-4e3f-8a2b-1c0d9e8f7a6b",
  "customer": {
    "id": "c0ffee00-3333-4abc-9def-001122334455",
    "name": "Laura Gómez",
    "email": "laura@ejemplo.com",
    "phone": "+34600000000"
  },
  "party_size": 6,
  "time": "00:30",
  "notes": "Cumpleaños",
  "spaces": [
    {
      "id": "a9b8c7d6-aaaa-4b5c-8d7e-6f5a4b3c2d1e",
      "label": "Mesa 12"
    }
  ],
  "currency": "eur",
  "paid_cents": 0,
  "deposit_cents": null,
  "balance_due_cents": 30000,
  "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
  "created_at": "2026-11-08T12:00:00.000Z",
  "venue_payments": []
}

Listas · Lectura

  • GET/v1/lists
  • GET/v1/lists/{id}
  • GET/v1/lists/{id}/guests
GET/v1/listslist:read

Listas de invitados de tus eventos.

ParámetroDescripción
event_idSolo las de un evento.
created_fromCreados desde este momento (incluido). ISO 8601: 2026-11-01 o 2026-11-01T20:00:00Z.
created_toCreados antes de este momento (no incluido).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "list",
      "id": "11223344-bbbb-4c5d-8e9f-a0b1c2d3e4f5",
      "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
      "name": "Lista VIP",
      "capacity": 150,
      "closes_at": "2026-12-06T01:30:00.000Z",
      "visible": true,
      "public_signup": false,
      "max_companions": 3,
      "created_at": "2026-11-01T09:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/lists/{id}list:read

Una lista.

Respuesta
{
  "object": "list",
  "id": "11223344-bbbb-4c5d-8e9f-a0b1c2d3e4f5",
  "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "name": "Lista VIP",
  "capacity": 150,
  "closes_at": "2026-12-06T01:30:00.000Z",
  "visible": true,
  "public_signup": false,
  "max_companions": 3,
  "created_at": "2026-11-01T09:00:00.000Z"
}
GET/v1/lists/{id}/guestslist:read

Apuntados a una lista (checked_in: personas que ya han entrado).

ParámetroDescripción
created_fromCreados desde este momento (incluido). ISO 8601: 2026-11-01 o 2026-11-01T20:00:00Z.
created_toCreados antes de este momento (no incluido).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "guest",
      "id": "55667788-cccc-4d5e-8f9a-b0c1d2e3f4a5",
      "list_id": "11223344-bbbb-4c5d-8e9f-a0b1c2d3e4f5",
      "name": "Marta Ruiz",
      "plus": 2,
      "email": "marta@ejemplo.com",
      "phone": null,
      "status": "approved",
      "checked_in": 0,
      "created_at": "2026-11-10T19:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Webhooks (lectura) · Lectura

  • GET/v1/webhook-endpoints
  • GET/v1/webhook-endpoints/{id}
  • GET/v1/webhook-endpoints/{id}/deliveries
GET/v1/webhook-endpointswebhook:read

URLs de webhooks de tu organización (nunca devuelve el secreto).

Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "webhook_endpoint",
      "id": "99aabbcc-dddd-4e5f-8a9b-c0d1e2f3a4b5",
      "url": "https://hooks.tuapp.com/sarao",
      "description": "Mi CRM",
      "events": [
        "order.paid",
        "order.refunded"
      ],
      "enabled": true,
      "created_at": "2026-11-10T19:00:00.000Z",
      "updated_at": "2026-11-10T19:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/webhook-endpoints/{id}webhook:read

Una URL de webhooks.

Respuesta
{
  "object": "webhook_endpoint",
  "id": "99aabbcc-dddd-4e5f-8a9b-c0d1e2f3a4b5",
  "url": "https://hooks.tuapp.com/sarao",
  "description": "Mi CRM",
  "events": [
    "order.paid",
    "order.refunded"
  ],
  "enabled": true,
  "created_at": "2026-11-10T19:00:00.000Z",
  "updated_at": "2026-11-10T19:00:00.000Z"
}
GET/v1/webhook-endpoints/{id}/deliverieswebhook:read

Registro de entregas de una URL, de la más reciente a la más antigua.

ParámetroDescripción
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "webhook_delivery",
      "id": "12ab34cd-eeee-4f5a-8b9c-d0e1f2a3b4c5",
      "endpoint_id": "99aabbcc-dddd-4e5f-8a9b-c0d1e2f3a4b5",
      "event": {
        "id": "77665544-ffff-4a3b-8c2d-1e0f9a8b7c6d",
        "type": "order.paid",
        "object_type": "order",
        "object_id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6"
      },
      "status": "delivered",
      "attempts": 1,
      "next_attempt_at": null,
      "last_attempt_at": "2026-11-08T23:10:03.000Z",
      "response_status": 200,
      "last_error": null,
      "delivered_at": "2026-11-08T23:10:03.000Z",
      "created_at": "2026-11-08T23:10:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Checkouts (lectura) · Lectura

  • GET/v1/checkouts
  • GET/v1/checkouts/{id}
GET/v1/checkoutscheckout:write

Checkouts creados por la API (de cualquier clave de tu organización).

ParámetroDescripción
event_idSolo los de un evento.
created_fromCreados desde este momento (incluido). ISO 8601: 2026-11-01 o 2026-11-01T20:00:00Z.
created_toCreados antes de este momento (no incluido).
limitElementos por página, de 1 a 100 (25 por defecto).
cursorEl next_cursor de la respuesta anterior para pedir la página siguiente.
Respuesta
{
  "object": "list",
  "data": [
    {
      "object": "checkout",
      "id": "e1d2c3b4-5555-4a6b-8c7d-9e0f1a2b3c4d",
      "url": "https://saraotickets.com/p/9xQ2…",
      "status": "open",
      "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
      "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
      "max_quantity": 4,
      "customer": {
        "email": "laura@ejemplo.com",
        "name": "Laura Gómez"
      },
      "order_id": null,
      "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
      "expires_at": "2026-11-11T19:00:00.000Z",
      "created_at": "2026-11-10T19:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/checkouts/{id}checkout:write

Un checkout: status open, completed (pagado: mira order_id), expired o cancelled.

Respuesta
{
  "object": "checkout",
  "id": "e1d2c3b4-5555-4a6b-8c7d-9e0f1a2b3c4d",
  "url": "https://saraotickets.com/p/9xQ2…",
  "status": "completed",
  "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
  "max_quantity": 4,
  "customer": {
    "email": "laura@ejemplo.com",
    "name": "Laura Gómez"
  },
  "order_id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
  "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
  "expires_at": "2026-11-11T19:00:00.000Z",
  "created_at": "2026-11-10T19:00:00.000Z"
}

Clientes · Escritura

  • POST/v1/customers
  • PATCH/v1/customers/{id}
POST/v1/customerscustomer:write

Da de alta un cliente. El email es único en tu organización (409 si ya existe).

CampoDescripción
emailObligatorio.
name, phone, birthdate (AAAA-MM-DD), gender, country (ES), province, city, address, postal_code, instagram, notesOpcionales.
document_type, document_numberDocumento de identidad (opcional).
marketing_consenttrue solo si el cliente ha dado su consentimiento expreso; entonces manda también consent_confirmed: true.
Petición (JSON)
{
  "email": "laura@ejemplo.com",
  "name": "Laura Gómez",
  "phone": "+34600000000",
  "city": "Madrid"
}
Respuesta
201 Created
{
  "object": "customer",
  "id": "c0ffee00-3333-4abc-9def-001122334455",
  "email": "laura@ejemplo.com",
  "name": "Laura Gómez",
  "phone": "+34600000000",
  "birthdate": "1999-04-12",
  "gender": "female",
  "country": "ES",
  "province": "Madrid",
  "city": "Madrid",
  "postal_code": "28004",
  "instagram": "lauragomez",
  "notes": null,
  "marketing_consent": false,
  "created_at": "2026-10-01T18:30:00.000Z"
}
PATCH/v1/customers/{id}customer:write

Cambia los campos que mandes; los demás se quedan igual. Mismas reglas que «Editar cliente» del panel (a quien se dio de baja no se le puede volver a dar el consentimiento).

Petición (JSON)
{
  "phone": "+34611111111",
  "notes": "Cliente habitual"
}
Respuesta
{
  "object": "customer",
  "id": "c0ffee00-3333-4abc-9def-001122334455",
  "email": "laura@ejemplo.com",
  "name": "Laura Gómez",
  "phone": "+34611111111",
  "birthdate": "1999-04-12",
  "gender": "female",
  "country": "ES",
  "province": "Madrid",
  "city": "Madrid",
  "postal_code": "28004",
  "instagram": "lauragomez",
  "notes": "Cliente habitual",
  "marketing_consent": false,
  "created_at": "2026-10-01T18:30:00.000Z"
}

Reservas · Escritura

  • POST/v1/reservations
  • PATCH/v1/reservations/{id}
POST/v1/reservationsreservation:write

Crea una reserva de mesa con las mismas reglas que el panel (aforo, cupo de la tarifa, mesa libre). La API no cobra ni apunta cobros: con payment «none» se paga en el local; con «paylink» se crea un checkout de Sarao y el cliente paga el adelanto online.

CampoDescripción
event_id, rate_idObligatorios: el evento y la tarifa de mesa (un ticket_type con kind table).
party_sizePersonas (obligatorio).
nameNombre del cliente (obligatorio).
email, phone, notesOpcionales (email obligatorio con paylink).
paymentnone (por defecto) o paylink.
space_id, zone_id, hour (HH:MM)Mesa, zona y hora (opcionales).
notifytrue: envía al cliente su reserva (o el link de pago) por email.
Petición (JSON)
{
  "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "rate_id": "b7a6c5d4-9999-4e3f-8a2b-1c0d9e8f7a6b",
  "party_size": 6,
  "name": "Laura Gómez",
  "email": "laura@ejemplo.com",
  "payment": "none",
  "notify": true
}
Respuesta
201 Created
{
  "reservation": {
    "object": "reservation",
    "id": "d4c3b2a1-8888-4f0e-9d8c-7b6a5f4e3d2c",
    "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
    "order_status": "paid",
    "status": "accepted",
    "rate_id": "b7a6c5d4-9999-4e3f-8a2b-1c0d9e8f7a6b",
    "customer": {
      "id": "c0ffee00-3333-4abc-9def-001122334455",
      "name": "Laura Gómez",
      "email": "laura@ejemplo.com",
      "phone": "+34600000000"
    },
    "party_size": 6,
    "time": "00:30",
    "notes": "Cumpleaños",
    "spaces": [
      {
        "id": "a9b8c7d6-aaaa-4b5c-8d7e-6f5a4b3c2d1e",
        "label": "Mesa 12"
      }
    ],
    "currency": "eur",
    "paid_cents": 0,
    "deposit_cents": null,
    "balance_due_cents": 30000,
    "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
    "created_at": "2026-11-08T12:00:00.000Z"
  }
}

// Con "payment": "paylink"
{
  "checkout": {
    "object": "checkout",
    "id": "e1d2c3b4-5555-4a6b-8c7d-9e0f1a2b3c4d",
    "url": "https://saraotickets.com/p/9xQ2…",
    "status": "open",
    "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
    "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
    "max_quantity": 1,
    "customer": {
      "email": "laura@ejemplo.com",
      "name": "Laura Gómez"
    },
    "order_id": null,
    "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
    "expires_at": "2026-11-11T19:00:00.000Z",
    "created_at": "2026-11-10T19:00:00.000Z"
  },
  "emailed": true
}
PATCH/v1/reservations/{id}reservation:write

Cambia el estado y/o las observaciones. cancelled_by_customer cancela la reserva sin devolver nada (las devoluciones, desde el panel).

CampoDescripción
statuspending, accepted, arrived, seated, no_show o cancelled_by_customer.
notesObservaciones internas (null las borra).
Petición (JSON)
{
  "status": "arrived"
}
Respuesta
{
  "object": "reservation",
  "id": "d4c3b2a1-8888-4f0e-9d8c-7b6a5f4e3d2c",
  "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "order_status": "paid",
  "status": "arrived",
  "rate_id": "b7a6c5d4-9999-4e3f-8a2b-1c0d9e8f7a6b",
  "customer": {
    "id": "c0ffee00-3333-4abc-9def-001122334455",
    "name": "Laura Gómez",
    "email": "laura@ejemplo.com",
    "phone": "+34600000000"
  },
  "party_size": 6,
  "time": "00:30",
  "notes": "Cumpleaños",
  "spaces": [
    {
      "id": "a9b8c7d6-aaaa-4b5c-8d7e-6f5a4b3c2d1e",
      "label": "Mesa 12"
    }
  ],
  "currency": "eur",
  "paid_cents": 0,
  "deposit_cents": null,
  "balance_due_cents": 30000,
  "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
  "created_at": "2026-11-08T12:00:00.000Z",
  "venue_payments": []
}

Listas · Escritura

  • POST/v1/lists/{id}/guests
POST/v1/lists/{id}/guestslist:write

Apunta invitados (hasta 200 por petición) con las reglas de la lista: cupo, cierre y máximo de acompañantes. Los que llevan email reciben su QR.

CampoDescripción
guests[].nameNombre (obligatorio).
guests[].plusAcompañantes (0 por defecto).
guests[].email, guests[].phoneOpcionales.
tagsIds de etiquetas de venta (si la lista las usa).
Petición (JSON)
{
  "guests": [
    {
      "name": "Marta Ruiz",
      "plus": 2,
      "email": "marta@ejemplo.com"
    }
  ]
}
Respuesta
201 Created
{
  "object": "list",
  "data": [
    {
      "object": "guest",
      "id": "55667788-cccc-4d5e-8f9a-b0c1d2e3f4a5",
      "list_id": "11223344-bbbb-4c5d-8e9f-a0b1c2d3e4f5",
      "name": "Marta Ruiz",
      "plus": 2,
      "email": "marta@ejemplo.com",
      "phone": null,
      "status": "approved",
      "checked_in": 0,
      "created_at": "2026-11-10T19:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Vender entradas · Escritura

  • POST/v1/checkouts
POST/v1/checkoutscheckout:write

Crea un checkout de Sarao para un tipo de entrada y devuelve su url. Redirige al comprador a esa URL: paga en Sarao (tarjeta, Apple Pay, Google Pay…), recibe sus entradas por email y el pedido queda con el api_key_id de tu clave.

CampoDescripción
event_id, ticket_type_idObligatorios (el tipo tiene que tener sellable_by_api).
quantityMáximo de entradas que puede comprar (1 a 50); elige al pagar.
customer_email, customer_name, customer_phoneOpcionales: datos del comprador.
expires_in_hoursHoras que vale el checkout (1 a 720; 24 por defecto).
send_emailtrue: Sarao envía el link de pago al customer_email.
Petición (JSON)
{
  "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
  "quantity": 4,
  "customer_email": "laura@ejemplo.com"
}
Respuesta
201 Created
{
  "object": "checkout",
  "id": "e1d2c3b4-5555-4a6b-8c7d-9e0f1a2b3c4d",
  "url": "https://saraotickets.com/p/9xQ2…",
  "status": "open",
  "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
  "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
  "max_quantity": 4,
  "customer": {
    "email": "laura@ejemplo.com",
    "name": "Laura Gómez"
  },
  "order_id": null,
  "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
  "expires_at": "2026-11-11T19:00:00.000Z",
  "created_at": "2026-11-10T19:00:00.000Z",
  "delivery": "Link de pago creado"
}

Webhooks · Escritura

  • POST/v1/webhook-endpoints
  • PATCH/v1/webhook-endpoints/{id}
  • DELETE/v1/webhook-endpoints/{id}
  • POST/v1/webhook-endpoints/{id}/rotate-secret
POST/v1/webhook-endpointswebhook:write

Da de alta una URL (https y pública). La respuesta trae el secreto de firma (secret): guárdalo, no se vuelve a enseñar.

CampoDescripción
urlhttps://… (puerto 443 u 8443; nunca direcciones privadas ni internas).
descriptionOpcional (máximo 120 caracteres).
eventsAvisos que quieres (vacío o sin mandar = todos).
Petición (JSON)
{
  "url": "https://hooks.tuapp.com/sarao",
  "description": "Mi CRM",
  "events": [
    "order.paid",
    "order.refunded"
  ]
}
Respuesta
201 Created
{
  "object": "webhook_endpoint",
  "id": "99aabbcc-dddd-4e5f-8a9b-c0d1e2f3a4b5",
  "url": "https://hooks.tuapp.com/sarao",
  "description": "Mi CRM",
  "events": [
    "order.paid",
    "order.refunded"
  ],
  "enabled": true,
  "created_at": "2026-11-10T19:00:00.000Z",
  "updated_at": "2026-11-10T19:00:00.000Z",
  "secret": "whsec_…"
}
PATCH/v1/webhook-endpoints/{id}webhook:write

Cambia la URL, la descripción, los avisos o enabled (false la pausa).

Petición (JSON)
{
  "enabled": false
}
Respuesta
{
  "object": "webhook_endpoint",
  "id": "99aabbcc-dddd-4e5f-8a9b-c0d1e2f3a4b5",
  "url": "https://hooks.tuapp.com/sarao",
  "description": "Mi CRM",
  "events": [
    "order.paid",
    "order.refunded"
  ],
  "enabled": false,
  "created_at": "2026-11-10T19:00:00.000Z",
  "updated_at": "2026-11-10T19:00:00.000Z"
}
DELETE/v1/webhook-endpoints/{id}webhook:write

Borra la URL y su registro de entregas.

Respuesta
{
  "object": "webhook_endpoint",
  "id": "99aabbcc-dddd-4e5f-8a9b-c0d1e2f3a4b5",
  "deleted": true
}
POST/v1/webhook-endpoints/{id}/rotate-secretwebhook:write

Genera un secreto nuevo; el anterior deja de valer al momento.

Respuesta
{
  "object": "webhook_endpoint",
  "id": "99aabbcc-dddd-4e5f-8a9b-c0d1e2f3a4b5",
  "url": "https://hooks.tuapp.com/sarao",
  "description": "Mi CRM",
  "events": [
    "order.paid",
    "order.refunded"
  ],
  "enabled": true,
  "created_at": "2026-11-10T19:00:00.000Z",
  "updated_at": "2026-11-10T19:00:00.000Z",
  "secret": "whsec_…"
}

Webhooks

Con los webhooks, Sarao avisa a tu servidor cuando pasa algo, sin que tengas que preguntar. Da de alta tus URLs en Ajustes → Developer Portal → Webhooks o con POST /v1/webhook-endpoints. Las URLs tienen que ser https y públicas (nunca direcciones privadas ni internas).

Avisos

TipoCuándodata.object
order.paidPedido pagadoorder
order.refundedPedido devueltoorder
ticket.validatedEntrada validada en la puertaticket
reservation.createdReserva creadareservation
reservation.updatedReserva cambiadareservation

Llegan por POST con este cuerpo (el objeto tiene la misma forma que en la API, congelado en el momento del primer envío):

json
{
  "id": "77665544-ffff-4a3b-8c2d-1e0f9a8b7c6d",
  "object": "event",
  "type": "order.paid",
  "created_at": "2026-11-08T23:10:00.000Z",
  "data": {
    "object": {
      "object": "order",
      "id": "9b1e4c7a-2222-4d3e-8f90-a1b2c3d4e5f6",
      "event_id": "3f0c6a2e-5b1d-4c8e-9a77-2d4e8f1b6c10",
      "status": "paid",
      "channel": "online",
      "customer": {
        "id": "c0ffee00-3333-4abc-9def-001122334455",
        "email": "laura@ejemplo.com",
        "name": "Laura Gómez",
        "phone": "+34600000000"
      },
      "currency": "eur",
      "subtotal_cents": 3000,
      "discount_cents": 0,
      "fee_cents": 180,
      "total_cents": 3180,
      "refunded_cents": 0,
      "net_cents": 3180,
      "items": [
        {
          "ticket_type_id": "7d2f9c1a-1111-4a2b-9c3d-4e5f6a7b8c9d",
          "quantity": 2,
          "unit_price_cents": 1500,
          "unit_discount_cents": 0,
          "unit_fee_cents": 90,
          "extra_cents": 0
        }
      ],
      "promoter_id": null,
      "api_key_id": "5e6f7a8b-4444-4c5d-8e9f-0a1b2c3d4e5f",
      "checkout_id": "e1d2c3b4-5555-4a6b-8c7d-9e0f1a2b3c4d",
      "paid_at": "2026-11-08T23:10:00.000Z",
      "created_at": "2026-11-08T23:08:41.000Z"
    }
  }
}

Cabeceras: Sarao-Signature (la firma), Sarao-Event-Id, Sarao-Event-Type y Sarao-Delivery-Id.

Verificar la firma

Cada URL tiene su secreto (whsec_…), que solo se enseña al crearla o al cambiarlo. La cabecera es t=1762643400,v1=5257a8…: t es la hora del envío (segundos Unix) y v1 el HMAC-SHA256 en hexadecimal, con tu secreto, del texto <t>.<cuerpo tal cual>. Compara en tiempo constante y rechaza las firmas de hace más de 5 minutos.

Node.js (Express)
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.SARAO_WEBHOOK_SECRET; // whsec_…

// Ojo: la firma va sobre el cuerpo TAL CUAL llega (sin volver a serializar el JSON)
app.post("/sarao", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("Sarao-Signature") ?? "";
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  const expected = crypto.createHmac("sha256", SECRET).update(`${t}.${req.body}`).digest("hex");
  const ok =
    parts.v1 && parts.v1.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected)) &&
    Math.abs(Date.now() / 1000 - t) < 300;
  if (!ok) return res.status(400).send("Firma no válida");

  const event = JSON.parse(req.body);
  // Responde rápido (2xx) y procesa después; ignora los event.id que ya hayas procesado
  res.sendStatus(200);
});
PHP
<?php
$secret = getenv('SARAO_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_SARAO_SIGNATURE'] ?? ''), $parts);
$expected = hash_hmac('sha256', $parts['t'] . '.' . $body, $secret);
if (!hash_equals($expected, $parts['v1'] ?? '') || abs(time() - (int) $parts['t']) > 300) {
  http_response_code(400);
  exit('Firma no válida');
}
$event = json_decode($body, true);
http_response_code(200);

Reintentos y registro

Responde con un 2xx en menos de 10 segundos (procesa después). Cualquier otra respuesta, una redirección o no contestar es un fallo: se reintenta a los 1, 5 y 30 minutos, y a las 2, 6, 12 y 24 horas (8 intentos en total). El mismo aviso puede llegar más de una vez: usa id para no procesarlo dos veces. Cada entrega queda en el registro (GET /v1/webhook-endpoints/{id}/deliveries y el Developer Portal), donde puedes reenviarla. El registro se guarda 30 días.