Documentación

API

Para montar tu propio formulario, con tu diseño y en tu web. Cubre los tres módulos: reclamos —nosotros asignamos el correlativo, generamos la Hoja oficial y enviamos las copias—, reservas —nosotros llevamos el cupo, las mesas y los avisos— y Beneficios —Mi club en tu web, que está más abajo—.

Si lo único que quieres es que se vea con tu marca dentro de tu web, no necesitas nada de esto: incrusta el formulario hospedado con una línea de <script> y personalízalo desde el panel.

Antes de empezar

Si armas tu propio formulario, sigues obligado a capturar los campos del formato oficial y a mostrar los textos legales. Si omites alguno, el registro sale defectuoso y la responsabilidad es del proveedor, no nuestra.

Para no copiarlos a mano de un PDF y quedarte desactualizado cuando cambie la norma, consúltalos con GET /api/v1/schema: devuelve los campos obligatorios, sus validaciones y los textos que debes mostrar literales. Trae también captcha.sitekey, que es lo que necesita tu formulario si va en el navegador.

Si lo que quieres es solo que se vea con tu marca, no necesitas la API: personaliza el formulario hospedado desde el panel.

Desde dónde puedes llamar, y a qué ritmo

Desde donde quieras. No hay lista de servidores permitidos, ni hace falta avisarnos de dónde corre tu aplicación. La única restricción de origen que existe es para las claves de navegador, y por un motivo concreto: esas viajan en el HTML y las lee cualquiera con F12.

sk_live_… · servidor No mira el dominio. Funciona desde localhost, desde tu portátil o desde donde sea
pk_live_… · navegador Necesita dominios autorizados, y http://localhost vale como uno más

Lo que sí hay es un límite de ritmo, y existe para un caso muy concreto: que un bucle mal escrito registre cien reclamos en un segundo y eso se convierta en cien correos saliendo a nombre de un negocio que no ha hecho nada. El límite protege al cliente, no a nosotros.

Peticiones permitidas

Qué haces Por clave sk_ Por IP (solo pk_) Por clave pk_
Registrar un reclamo60 / min10 / min600 / min
Consultar una constancia60 / min10 / min600 / min
Listar reclamos60 / min——
Leer un reclamo120 / min——
Crear una reserva60 / min5 / 10 min600 / min
Consultar disponibilidad120 / min30 / min600 / min
Modificar una reserva60 / min——
Cancelar una reserva30 / min——

Con una pk_, se cuenta primero la conexión de quien visita tu web, y lo que frena no gasta el cupo de tu clave, que queda como techo para todo tu restaurante: quien copie la clave de tu web no puede dejar sin servicio tu formulario desde su computadora.

Pasado el límite responde 429 con retry_after_seconds: espera lo que diga y reintenta. No hace falta avisarnos ni rotar la clave.

Probar sin ensuciar nada

Antes de escribir una línea, crea una clave de pruebas en el panel, en Integración. Se llama sk_test_… en vez de sk_live_…, y esa diferencia en el nombre es deliberada: es lo primero que se lee en una captura de pantalla o en un curl pegado en un chat.

Por qué importa

Un reclamo no es una fila que se borra. Registrar uno consume un número correlativo que no vuelve, arranca el plazo legal de quince días hábiles, entra en una cadena de integridad que no admite borrado y manda correos a la dirección que hayas puesto. Una reserva de prueba bloquea una mesa toda la noche y el local pierde ese cubierto sin enterarse.

Con una clave de pruebas, nada de eso ocurre.

Qué hace y qué no

Comprueba todos los camposSí, con el mismo validador
Devuelve los mismos erroresSí, mismos códigos y mensajes
Dice si la hora estaría libreSí, en Reservas
Registra el reclamo o la reservaNo
Gasta un número de HojaNo
Ocupa una mesaNo
Envía correosNo
Lee datos realesNo — y es a propósito

Lo último no es un olvido. Todo el valor de una clave de pruebas es que puedas pegarla en Postman, en un repositorio o en una captura sin pensarlo dos veces. Si además sirviera para volcar nombres, documentos y teléfonos, volvería a ser una credencial que hay que custodiar. Para leer, usa la clave de servidor: leer no rompe nada.

curl -X POST https://app.dbtech.cloud/api/v1/complaints \
  -H "Authorization: Bearer sk_test_TU_CLAVE_DE_PRUEBA" \
  -H "Content-Type: application/json" \
  -d '{
    "sede": "miraflores",
    "tipo": "reclamo",
    "consumidor_nombre": "María Ñahui",
    "consumidor_doc_tipo": "DNI",
    "consumidor_doc_num": "70112233",
    "consumidor_domicilio": "Av. Ejemplo 500, Lima",
    "consumidor_email": "maria@ejemplo.pe",
    "bien_tipo": "producto",
    "bien_descripcion": "Pedido #A-4471",
    "monto_reclamado": 79.90,
    "detalle": "El pedido llegó incompleto y con el empaque abierto.",
    "pedido": "Reposición del producto faltante.",
    "consentimiento": true,
    "datos_adicionales": { "numero_pedido": "A-4471", "mesa": 12 }
  }'

Respuesta 200

{
  "modo": "prueba",
  "ok": true,
  "mensaje": "Los datos son correctos. No se registró nada: esta es una clave
              de prueba. Con una clave sk_live_ esta misma petición habría
              devuelto 201.",
  "sede": "miraflores",
  "codigo_ejemplo": "MIR-00000-2026"
}

Cuando todo pase, cambia la clave por la de producción. No cambies nada más: la petición es idéntica, y en producción esa misma llamada responde 201 con el número de Hoja de verdad.

¿Y probar en local?

No hace falta, y tampoco podrías: el servicio necesita su base de datos, sus claves y su proveedor de correo. La clave de pruebas existe justamente para eso — apuntas a la misma URL de siempre y no ocurre nada irreversible.

Todo lo que hay

Método Ruta Qué hace Clave
POST /api/v1/complaints Registrar un reclamo cualquiera
GET /api/v1/complaints Listar el Libro solo sk_live
GET /api/v1/complaints/{id} Un expediente completo solo sk_live
POST /api/v1/complaints/lookup Buscar por código y documento cualquiera
GET /api/v1/complaints/{id}/pdf La Hoja en PDF cualquiera
GET /api/v1/reservations/availability Reglas del local y horas libres cualquiera
POST /api/v1/reservations Reservar cualquiera
GET /api/v1/reservations La agenda de un día solo sk_live
GET /api/v1/reservations/{id} Una reserva solo sk_live
PATCH /api/v1/reservations/{id} Marcar estado, o reprogramar solo sk_live
DELETE /api/v1/reservations/{id} Cancelar (avisa al comensal) solo sk_live
GET /api/v1/schema Los campos del formato oficial cualquiera
GET /api/v1/beneficios/club Tu club y lo que da cada plan cualquiera
POST /api/v1/beneficios/club/acceso El socio pide su código cualquiera
POST /api/v1/beneficios/club/confirmar El código, a cambio del pase cualquiera
POST /api/v1/beneficios/club/socio Lo suyo, con el pase cualquiera
POST /api/v1/beneficios/club/qr Su QR que cambia, con el pase cualquiera
POST /api/v1/beneficios/club/salir Cerrar su pase cualquiera
POST /api/v1/beneficios/club/wallet Su tarjeta en Google Wallet cualquiera

Con una clave de pruebas

POST /api/v1/complaints Valida igual. No registra, no gasta número, no envía correo.
POST /api/v1/reservations Valida igual y dice si la hora estaría libre. No ocupa mesa.
GET …availability Funciona igual: no hay datos personales de por medio.
GET /api/v1/schema Funciona igual.
Todo lo de leer y gestionar Responde 403: una clave de pruebas no ve datos reales.

Un método que una ruta no atiende responde 405 con la cabecera Allow. Manda siempre Content-Type: application/json: sin él, la protección contra envíos de formulario cruzados lo rechaza antes de llegar a la ruta.

Autenticación

Todas las peticiones llevan Authorization: Bearer <clave>. Las claves se crean en el panel, en Integración.

sk_live_…
Servidor a servidor. Sin captcha. Nunca la expongas en el navegador: quien la tenga puede registrar reclamos en tu libro.
pk_live_…
Para el navegador. Exige turnstile_token y solo funciona desde los dominios que declaraste. Sin cabecera Origin válida se rechaza, aunque la clave sea buena. El captcha se monta en tres líneas: más abajo está el formulario entero.

El proveedor sale de la clave, no del cuerpo: una clave no puede escribir en el libro de otra empresa.

Los bloques que debes capturar

La hoja oficial tiene cuatro secciones. Las tres primeras las llena el consumidor; la cuarta la llena el proveedor al responder, desde el panel o por API.

1 Identificación del consumidor

consumidor_nombre · consumidor_doc_tipo · consumidor_doc_num · consumidor_domicilio · consumidor_telefono · consumidor_email · es_menor_edad · apoderado_nombre

2 Identificación del bien contratado

bien_tipo · bien_descripcion · monto_reclamado · moneda

3 Detalle y pedido

tipo (reclamo | queja) · detalle · pedido

Extra — datos_adicionales

Objeto libre para tus propios campos: número de pedido, mesa, código de reserva. Se guardan y entran en el hash de integridad, pero no aparecen en la hoja oficial porque el formato de INDECOPI no admite campos añadidos. Sí salen en el anexo del PDF.

Registrar un reclamo

POST /api/v1/complaints

curl -X POST https://app.dbtech.cloud/api/v1/complaints \
  -H "Authorization: Bearer sk_live_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "sede": "miraflores",
    "tipo": "reclamo",
    "consumidor_nombre": "María Ñahui",
    "consumidor_doc_tipo": "DNI",
    "consumidor_doc_num": "70112233",
    "consumidor_domicilio": "Av. Ejemplo 500, Lima",
    "consumidor_email": "maria@ejemplo.pe",
    "bien_tipo": "producto",
    "bien_descripcion": "Pedido #A-4471",
    "monto_reclamado": 79.90,
    "detalle": "El pedido llegó incompleto y con el empaque abierto.",
    "pedido": "Reposición del producto faltante.",
    "consentimiento": true,
    "datos_adicionales": { "numero_pedido": "A-4471", "mesa": 12 }
  }'

Respuesta 201

{
  "id": "cdbe525a-50fe-41d6-927f-5d5302ed7f23",
  "codigo": "MIR-00001-2026",
  "correlativo": 1,
  "deadline_at": "2026-09-04",
  "consulta_url": "https://app.dbtech.cloud/consulta/9771b92f…",
  "pdf_url": "https://app.dbtech.cloud/api/v1/complaints/cdbe…/pdf?token=9771…"
}

Guarda el codigo y la consulta_url: son lo que el consumidor necesita para seguir su caso. La copia por correo se envía sola.

Buscar una hoja — tu propio buscador

POST /api/v1/complaints/lookup

Para montar el buscador de seguimiento en tu web, con tu diseño. El consumidor teclea su número de hoja y su documento; esto te dice a dónde mandarlo.

curl -X POST https://app.dbtech.cloud/api/v1/complaints/lookup \
  -H "Authorization: Bearer pk_live_TU_CLAVE" \
  -H "Origin: https://www.tu-restaurante.pe" \
  -H "Content-Type: application/json" \
  -d '{
    "codigo": "MIR-00001-2026",
    "doc_num": "70112233",
    "turnstile_token": "…"
  }'

Respuesta 200

{
  "codigo": "MIR-00001-2026",
  "consulta_url": "https://app.dbtech.cloud/consulta/9771b92f…",
  "pdf_url": "https://app.dbtech.cloud/api/v1/complaints/cdbe…/pdf?token=9771…"
}

Redirige al consumidor a consulta_url.

Cuando el número encaja en varios de tus locales

El correlativo se cuenta por sede, así que cada local tiene su «00001». Si el consumidor solo recuerda el número corto, la respuesta trae con qué preguntarle — y no trae el enlace: la credencial que abre el expediente se entrega cuando no queda duda de cuál es la hoja.

{
  "ambiguo": true,
  "mensaje": "Ese número existe en más de uno de tus locales. Pregunta en cuál fue.",
  "sedes": [
    { "sede": "miraflores", "nombre": "Miraflores" },
    { "sede": "barranco",   "nombre": "Barranco" }
  ]
}

Repite la llamada añadiendo "sede": "barranco".

Dos cosas que conviene saber

No devuelve el expediente, solo el enlace. Meter los datos del reclamo en el HTML de tu web sería una segunda copia de datos personales fuera del registro, en un sitio del que no podemos responder. El buscador es tuyo; el último paso lo servimos nosotros.

Solo busca en tu libro. La empresa sale de tu clave, nunca del cuerpo de la petición: tu buscador no puede resolver la hoja de otro negocio.

Límites: 60 por minuto y clave; con claves publishable, 10 por minuto e IP —contadas antes que la clave, que pasa a ser un techo de 600— porque ahí cada IP es un consumidor, y sin ese freno el buscador sería una forma de probar documentos en serie.

El captcha, en tu formulario

Solo si usas una clave pk_live_. Las sk_live_ no llevan captcha: viven en tu servidor, donde no hay nadie a quien comprobar.

Tres cosas distintas, y solo una viaja

sitekey
— público y constante. Va escrito en tu HTML y dice qué widget pintar. Te lo damos en GET /api/v1/schema, en captcha.sitekey.
clave secreta
— nuestra. Verifica contra Cloudflare y no sale de nuestro servidor. Tú no la necesitas.
token
— lo genera el widget cada vez que alguien resuelve. Cientos de caracteres, dura unos 5 minutos y se gasta al primer uso. Esto es lo que va en turnstile_token.

Si lo que mandas no cambia entre una petición y la siguiente, es el sitekey y no el token — y el servidor devolverá 400. Un valor idéntico para todo el mundo no puede demostrar que tú pasaste el reto.

En tu página

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js"
        async defer></script>

<form id="buscar">
  <input name="codigo"  placeholder="MIR-00001-2026" required>
  <input name="doc_num" placeholder="Tu documento"   required>

  <!-- El widget inyecta aquí dentro un input oculto: cf-turnstile-response -->
  <div class="cf-turnstile" data-sitekey="TU_SITEKEY"></div>

  <button>Buscar mi reclamo</button>
</form>

Al enviar

document.getElementById('buscar').addEventListener('submit', async (e) => {
  e.preventDefault();
  const fd = new FormData(e.target);

  const r = await fetch('https://app.dbtech.cloud/api/v1/complaints/lookup', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer pk_live_TU_CLAVE',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      codigo:  fd.get('codigo'),
      doc_num: fd.get('doc_num'),
      // Ya viene en el FormData: lo puso el widget al resolverse.
      turnstile_token: fd.get('cf-turnstile-response'),
    }),
  });

  const data = await r.json();
  if (data.consulta_url) location.href = data.consulta_url;
  else alert(data.error ?? data.mensaje);

  // El token es de UN SOLO USO. Sin esto, la segunda búsqueda da 400.
  turnstile.reset();
});

No copies el token a mano ni lo busques en el DOM: el widget deja un input oculto dentro de tu formulario, así que el FormData ya lo lleva. Y no pongas la cabecera Origin: la pone el navegador, y por eso una clave publishable puede ser pública — la clave se copia, el origen no.

El fallo que se come todo el mundo

Falta turnstile.reset() después de enviar. La primera búsqueda funciona y la segunda devuelve 400, porque el widget entrega el mismo token ya gastado. El mensaje dice que la comprobación venció, y es literal.

Para probar desde Postman, que no es un navegador: abre tu formulario, deja que el widget se resuelva y en la consola pide copy(turnstile.getResponse()). Tienes 5 minutos y un solo envío.

Leer y gestionar

Solo con clave de servidor

Todo lo de esta sección exige sk_live_… y responde 403 a una clave publishable. Aquí salen nombres, documentos y teléfonos, y se puede anular la mesa de un cliente. Una clave pk_live_ está pensada para ser pública: la lee cualquiera con F12. Sirve para meter datos, nunca para sacarlos.

El Libro

GET /api/v1/complaints

Filtros: estado (pendiente · cerrado), sede, desde y hasta (AAAA-MM-DD), limit (1–100, por defecto 25).

Se pagina por cursor, no por número de página: pasa el siguiente_cursor que devuelve la respuesta. Con offset, un reclamo nuevo mientras paginas desplaza todo y hace que se repita una fila y se salte otra. Cuando llega null, no hay más.

El listado trae lo justo para pintar una bandeja. El documento, el domicilio y el detalle están en GET /api/v1/complaints/{id}, uno a uno: repartir cincuenta expedientes enteros por página es multiplicar copias de datos personales para que uses tres campos.

El detalle no devuelve el consulta_token —es la llave que abre la constancia sin más comprobación— ni la IP del envío. Sí devuelve el hash, para que puedas comprobar por tu cuenta que el expediente no cambió.

La agenda

GET /api/v1/reservations?fecha=AAAA-MM-DD

fecha es obligatoria. Sin ella lo natural sería devolver «todas», y en un local con dos años de historial eso son decenas de miles de filas con datos personales por una llamada distraída. Filtros opcionales: sede y estado.

PATCH /api/v1/reservations/{id}

Hace dos cosas distintas, y por eso no se pueden pedir juntas:

{ "estado": … } Marcar. sentada, no_show o completada
{ "fecha", "hora", "personas" } Reprogramar. Lo que no mandes se queda como está

Mandar las dos cosas a la vez responde 422. No es rigidez: el resultado dependería del orden en que se aplicaran —¿se mueve y luego se sienta, o se sienta y luego se mueve una reserva ya sentada?— y ese orden no está definido en ninguna parte. Que lo decida quien llama, en dos peticiones.

La frontera de lo que puedes cambiar no es «reservas sí, reclamos no»: es si la acción se puede deshacer. Marcar que alguien llegó se corrige diciendo lo contrario y no sale del local. Cancelar, no — por eso tiene su propio verbo y no se puede hacer sin querer con un PATCH.

Reprogramar

Cambiar la hora es una cosa; cambiar cuántos son es otra, y las dos van por aquí. Una reserva de dos que pasa a cuatro necesita otra mesa, así que el sistema replanifica desde cero: suelta el cupo viejo y busca sitio para el grupo nuevo. Puede acabar en un tamaño de mesa distinto del que tenía.

Si no cabe, no se mueve nada

Responde 409 con motivo: "sin_cupo" y la reserva se queda exactamente donde estaba, con su mesa. La operación es atómica: nunca se queda a medias, sin horario viejo ni nuevo.

Si mandas la misma fecha, hora y grupo que ya tenía, responde 200 con "sin_cambios": true y no toca nada.

Al comensal no se le avisa. Es lo mismo que hace el panel cuando reprograma el local: quien mueve una reserva suele estar hablando con él por teléfono, y un correo automático llegaría tarde y contradiciendo lo acordado. Si tu sistema mueve reservas sin hablar con nadie, avísale tú.

Es PATCH y no PUT porque no se reemplaza el recurso: se cambian los campos que mandas. Un PUT prometería poder enviar la reserva entera, incluidos el código y el estado, y eso no es cierto.

DELETE /api/v1/reservations/{id}

Cancela; no borra. La reserva queda como cancelada y se sigue pudiendo consultar: es lo que distingue «se canceló» de «nunca existió». Acepta ?motivo=…, en la query y no en el cuerpo, porque un cuerpo en un DELETE lo descartan muchos proxies sin avisar.

Manda un correo al comensal, y ese correo no se retira. Cancelar dos veces es 200 y no avisa dos veces: puedes reintentar tras un fallo de red sin miedo. Una mesa ya atendida (completada o no_show) responde 409: no se anula lo que ya ocurrió.

Sedes

La numeración correlativa es por establecimiento, así que sede es obligatoria si el proveedor tiene más de uno. Nunca elegimos una por ti: un reclamo en el libro equivocado deja mal numerados los dos.

Si la omites con varias sedes, la respuesta es 422 con la lista de slugs disponibles.

Reservas

Dos llamadas: preguntar qué hay libre y reservar. El cupo, las mesas, los recordatorios y los avisos al local corren por nuestra cuenta.

Requiere el módulo de Reservas contratado; si no, las dos responden 403.

Qué hay libre

GET /api/v1/reservations/availability

Devuelve las reglas del local —tamaños de grupo, hasta qué fecha se puede reservar, cuántas horas antes se cancela— y, si mandas fecha, también las horas libres. Sin fecha solo las reglas: es la llamada que hace tu formulario al cargarse.

Pide las reglas en vez de codificarlas a mano. El día que el local cambie su configuración desde el panel, tu formulario se entera; si las escribiste tú, mentirá.

curl "https://app.dbtech.cloud/api/v1/reservations/availability?sede=miraflores&fecha=2026-10-30&personas=4" \
  -H "Authorization: Bearer pk_live_TU_CLAVE" \
  -H "Origin: https://www.tu-restaurante.pe"

Respuesta 200

{
  "sede": "miraflores",
  "nombre": "Miraflores",
  "acepta_reservas": true,
  "grupo_min": 1,
  "grupo_max": 8,
  "aforo_maximo": 8,
  "fecha_min": "2026-09-01",
  "fecha_max": "2026-10-31",
  "anticipacion_min_horas": 2,
  "cancelacion_horas": 2,
  "confirmacion_automatica": true,
  "fecha": "2026-10-30",
  "personas": 4,
  "horarios": [
    { "hora": "19:00", "mesas_libres": 3, "mesa": "Mesa para 4", "capacidad": 4 },
    { "hora": "19:30", "mesas_libres": 2, "mesa": "Mesa para 4", "capacidad": 4 }
  ]
}

Para el selector de comensales, usa aforo_maximo

Son dos límites distintos y el que manda es el menor. grupo_max es lo que el local admite por política; aforo_maximo es lo más grande que de verdad cabe juntando sus mesas. Un local con mesas de cuatro y grupo_max de 20 puede tener aforo_maximo de 12 si no tiene más mesas.

Ofrece hasta aforo_maximo: pedir por encima se rechaza, y ofrecer por debajo te hace perder reservas que el local sí quería. Si vale 0, la sede no ha declarado mesas y no puede aceptar a nadie todavía.

Esto es una foto, no una reserva

Nada queda apartado. Entre que pintas las horas y alguien pulsa el botón pueden pasar minutos, y el cupo lo decide la base de datos al insertar, de forma atómica. Un alta puede responder sin_cupo aunque esta llamada dijera que sí — tu formulario tiene que saber recuperarse de eso, volviendo a pedir las horas.

Si la sede todavía no acepta reservas en línea, responde 200 con acepta_reservas: false — no un error. Así puedes enseñar «reserva por teléfono» en vez de romperte.

Reservar

POST /api/v1/reservations

curl -X POST https://app.dbtech.cloud/api/v1/reservations \
  -H "Authorization: Bearer pk_live_TU_CLAVE" \
  -H "Origin: https://www.tu-restaurante.pe" \
  -H "Content-Type: application/json" \
  -d '{
    "sede": "miraflores",
    "fecha": "2026-10-30",
    "hora": "19:30",
    "personas": 4,
    "nombre": "Ana Torres",
    "telefono": "987654321",
    "email": "ana@ejemplo.pe",
    "notas": "Alergia a los frutos secos",
    "turnstile_token": "…"
  }'

Respuesta 201

{
  "id": "ab56292a-3730-4c7b-b2e3-58ca0bdfee0f",
  "codigo": "MIR-78761D",
  "estado": "confirmada",
  "sede": "miraflores",
  "gestion_url": "https://app.dbtech.cloud/reserva/d39cdebf…"
}

telefono es obligatorio y email no — al revés que en el Libro. Si el local necesita avisar de un imprevisto una hora antes, el correo no sirve.

Manda al comensal a gestion_url: ahí ve su reserva y puede cancelarla. Es la llave — quien tenga el enlace puede cancelar, así que se le entrega a él y no se publica.

Cuando no se puede

El cuerpo trae motivo, un código estable. Úsalo tú, no el texto: «esa hora acaba de ocuparse» no se comprueba con un if.

{
  "error": "Esa hora acaba de ocuparse. Vuelve a elegir entre las horas disponibles.",
  "motivo": "sin_cupo",
  "campos": { "hora": ["Esa hora acaba de ocuparse. …"] }
}
motivo HTTP Qué pasó
sin_cupo 409 La mesa se ocupó entre la consulta y el envío. Al reprogramar: no cabe, y la reserva no se movió
estado_no_permite 409 Al reprogramar: la reserva ya está cancelada, sentada o cerrada
horario_no_disponible 409 El local no abre a esa hora ese día
fecha_fuera_de_rango 422 Más allá de la anticipación máxima
anticipacion_insuficiente 422 Demasiado justo: falta menos de lo que pide el local
grupo_fuera_de_rango 422 Más o menos comensales de los que admite
sede_sin_reservas 409 La sede no tiene las reservas en línea activadas
sede_inactiva 404 La sede no existe o está desactivada

Límites: 60 altas por minuto y clave. Con claves publishable, 5 cada diez minutos por IP —el mismo tope que el formulario hospedado—, contadas antes que la clave, que pasa a ser un techo de 600. Una reserva falsa no llena una tabla: deja una mesa vacía toda la noche.

Beneficios: el club en tu web

Lo que hace Mi club, para pintarlo con tu diseño: el socio escribe su documento, le llega un código a su correo, y con el código recibes su pase de 30 minutos. Con el pase, en el cuerpo de cada llamada, ves lo suyo y su QR que cambia. Requiere el módulo de Beneficios; si no, 403.

  • La respuesta a pedir el código es siempre la misma, sea socio o no: píntala tal cual.
  • El pase, en memoria: nunca en localStorage ni en una cookie. Al salir, llama a salir y deja de servir.
  • El QR vence en expira_en y es de un solo uso: pide otro antes.
  • Google Wallet: wallet devuelve la dirección que guarda el pase; ábrela en otra pestaña. Sin Wallet activado, 503.
  • Con clave pk_, pedir el código lleva captcha en turnstile_token, y como mucho 5 pedidos cada diez minutos por IP; la base calla al sexto de un mismo socio en la hora. Con sk_, ni captcha ni tope por IP: el captcha lo pone tu formulario.
  • Con una clave de prueba, pedir el código valida y no manda nada; lo demás responde 403.

El paso a paso, con ejemplos, está en la guía de integraciones. Si prefieres no programar, incrusta Mi club como el formulario: /club/<tu-empresa>?embed=1.

Errores

Código Significa
401Falta la clave, o es inválida o revocada
403Dominio no autorizado para esa clave publishable
404Sede inexistente o inactiva
422Datos inválidos. El cuerpo trae campos con el detalle
400Captcha no verificado (solo claves publishable)
404En /lookup: no hay ninguna hoja con ese número y ese documento
429Límite de peticiones. Espera lo que diga retry_after_seconds