{"openapi":"3.1.0","info":{"title":"Cacao — API de integraciones","version":"1.0.0","description":"API de solo lectura (más modificación de reservas) para integrar sistemas externos —agentes de IA, ERPs, apps propias— con los datos de un restaurante en Cacao: negocio, sucursales y horarios, carta, tienda, pedidos, gift cards, disponibilidad y reservas.\n\n**Autenticación**: `Authorization: Bearer cacao_sk_…` (o el header `x-api-key`). La clave se genera en el panel del restaurante (Integraciones → API) e identifica al negocio: no hace falta mandar ningún id de tenant.\n\n**Reservas**: se pueden consultar y modificar, pero **no crear ni cancelar**. Para reservar, derivá al cliente al `bookingUrl` que devuelve `/availability` — ahí se cierra la reserva y se cobra la seña."},"servers":[{"url":"https://mariantonieta.com.ar/api/v1"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key del restaurante (cacao_sk_…)."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key"}}},"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"x-scopes":[{"scope":"business:read","label":"Negocio y sucursales","description":"Nombre, contacto, direcciones y horarios de cada sucursal."},{"scope":"menu:read","label":"Carta","description":"Categorías y platos con sus precios y descripciones."},{"scope":"store:read","label":"Tienda","description":"Productos con precio y stock, y estado de los pedidos."},{"scope":"giftcards:read","label":"Gift cards","description":"Diseños a la venta con su precio (para poder ofrecerlas)."},{"scope":"reservations:read","label":"Reservas y calendario","description":"Consultar reservas y la disponibilidad de mesas por día y turno."},{"scope":"reservations:write","label":"Modificar reservas","description":"Cambiar fecha, horario o cantidad de personas de una reserva existente. Nunca cancelar ni cobrar."}],"paths":{"/knowledge":{"get":{"summary":"Snapshot completo del negocio","description":"Todo lo que hace falta para 'conocer' el restaurante en un solo request: negocio, sucursales con horarios, carta, tienda, gift cards y reglas de reserva. Devuelve solo las secciones que la clave tiene permitidas. Trae `version` + ETag: cacheá y refrescá solo cuando cambie (soporta `If-None-Match` → 304).","x-required-scope":"business:read (+ los scopes de cada sección)","responses":{"200":{"description":"Snapshot del negocio.","content":{"application/json":{"schema":{"type":"object"}}}},"304":{"description":"Sin cambios."},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene el permiso requerido."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"429":{"description":"Rate limit excedido."}}}},"/branches":{"get":{"summary":"Sucursales: direcciones, horarios y teléfonos","x-required-scope":"business:read","responses":{"200":{"description":"Lista de sucursales.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene el permiso requerido."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"429":{"description":"Rate limit excedido."}}}},"/menu":{"get":{"summary":"La carta: categorías y platos con precio","x-required-scope":"menu:read","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Busca en nombre y descripción del plato."}],"responses":{"200":{"description":"Categorías con sus platos activos.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene el permiso requerido."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"429":{"description":"Rate limit excedido."}}}},"/products":{"get":{"summary":"Tienda: productos con precio y stock actual","x-required-scope":"store:read","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Busca en nombre y descripción."},{"name":"category","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra por categoría exacta."},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"1..200 (default 100)."}],"responses":{"200":{"description":"Productos activos + config de entrega.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene el permiso requerido."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"429":{"description":"Rate limit excedido."}}}},"/orders":{"get":{"summary":"Pedidos: estado de pago y etapa de preparación","x-required-scope":"store:read","parameters":[{"name":"email","in":"query","required":false,"schema":{"type":"string"},"description":"Email del comprador (exacto)."},{"name":"type","in":"query","required":false,"schema":{"type":"string"},"description":"tienda | gift_card | reserva."},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"pending | paid | failed."},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"1..100 (default 20)."}],"responses":{"200":{"description":"Pedidos, más recientes primero.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene el permiso requerido."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"429":{"description":"Rate limit excedido."}}}},"/gift-cards":{"get":{"summary":"Gift cards a la venta (para ofrecerlas)","x-required-scope":"giftcards:read","responses":{"200":{"description":"Diseños activos con precio y disponibilidad.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene el permiso requerido."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"429":{"description":"Rate limit excedido."}}}},"/availability":{"get":{"summary":"Calendario: franjas con mesa libre + link para reservar","description":"Devuelve la ocupación por franja y un `bookingUrl` prellenado. La API no crea reservas: derivá al cliente a ese link, donde reserva y paga la seña.","x-required-scope":"reservations:read","parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string"},"description":"YYYY-MM-DD."},{"name":"branchId","in":"query","required":false,"schema":{"type":"string"},"description":"Id o nombre de sucursal. Sin él, la primera."},{"name":"guests","in":"query","required":false,"schema":{"type":"string"},"description":"Cantidad de personas (filtra mesas por capacidad)."},{"name":"turno","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra por turno (id o nombre)."},{"name":"time","in":"query","required":false,"schema":{"type":"string"},"description":"Consulta una sola franja (HH:MM)."}],"responses":{"200":{"description":"Franjas, disponibilidad, política de reservas y bookingUrl.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene el permiso requerido."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"429":{"description":"Rate limit excedido."}}}},"/reservations":{"get":{"summary":"Consultar y buscar reservas","description":"El caso central: buscar por teléfono. El match ignora prefijos (+54, 0, 15), así que podés usar el número de WhatsApp tal cual.","x-required-scope":"reservations:read","parameters":[{"name":"phone","in":"query","required":false,"schema":{"type":"string"},"description":"Teléfono del cliente (match tolerante por sufijo)."},{"name":"email","in":"query","required":false,"schema":{"type":"string"},"description":"Email del cliente (exacto)."},{"name":"branchId","in":"query","required":false,"schema":{"type":"string"},"description":"Id o nombre de sucursal."},{"name":"date","in":"query","required":false,"schema":{"type":"string"},"description":"Día exacto (YYYY-MM-DD)."},{"name":"from","in":"query","required":false,"schema":{"type":"string"},"description":"Desde (YYYY-MM-DD)."},{"name":"to","in":"query","required":false,"schema":{"type":"string"},"description":"Hasta (YYYY-MM-DD)."},{"name":"upcoming","in":"query","required":false,"schema":{"type":"string"},"description":"'true' = solo de hoy en adelante."},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"pending | confirmed | seated | cancelled."},{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Busca en nombre, email, teléfono y notas."},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"1..100 (default 50)."}],"responses":{"200":{"description":"Reservas, las más próximas primero.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene el permiso requerido."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"429":{"description":"Rate limit excedido."}}}},"/reservations/{id}":{"get":{"summary":"Detalle de una reserva","x-required-scope":"reservations:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"La reserva.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene el permiso requerido."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"429":{"description":"Rate limit excedido."}}},"patch":{"summary":"Modificar una reserva","description":"Cambia personas, fecha, horario, sucursal, notas o contacto. Re-chequea el cupo de forma atómica. **No cancela**: `status: \"cancelled\"` devuelve 403 — cancelar se hace en el panel del restaurante. Si cambian las personas y hay seña, informa el saldo adicional (`payment.additionalDue`) sin cobrarlo.","x-required-scope":"reservations:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"guests":{"type":"integer"},"date":{"type":"string","description":"YYYY-MM-DD"},"time":{"type":"string","description":"HH:MM"},"branchId":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"evento":{"type":"string"},"notes":{"type":"string"}}}}}},"responses":{"200":{"description":"La reserva actualizada, los cambios aplicados y el saldo de seña si corresponde.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key ausente, inválida o revocada."},"403":{"description":"La API key no tiene permiso, o se intentó cancelar la reserva (no permitido por API)."},"404":{"description":"El módulo está desactivado en este restaurante, o el recurso no existe."},"409":{"description":"Sin disponibilidad en la nueva franja."},"429":{"description":"Rate limit excedido."}}}}}}