¡Guau! Crea tu integración de WhatsApp. Empieza gratis. Regístrate ahora
Reserva de citas · caso de uso WhatsApp API

Deja que los clientes elijan una hora de visita en WhatsApp desde una lista

Los clientes reservan más rápido cuando tocan una hora clara en lugar de escribir ida y vuelta. Cuando tu flujo de reserva llega al paso de elegir hora, tu backend puede hacer POST de una lista interactiva al chat del cliente con Whapi.Cloud. Este caso de uso muestra a integradores cómo implementar ese flujo con la WhatsApp API.

type list con hasta diez filas de slot La elección llega como list_reply en messages.post
Panel de desarrollo junto a un teléfono con un chat de reserva en WhatsApp y una lista interactiva abierta de horarios de cita.
Caso de uso

Cuándo usar este flujo

Los slots libres ya están en tu agenda

Usa este flujo cuando own_backend ya sabe qué horas de visita están libres y solo necesitas que el cliente elija una en el chat.

La reserva sigue en un chat privado de WhatsApp

Usa este flujo cuando el cliente reserva en un direct_chat con tu número de negocio, no en un grupo ni en un canal.

Quieres un toque estructurado en lugar de texto libre

Usa este flujo cuando escribir «martes 15:00» provoca errores y prefieres una fila de lista con un id de slot estable que tu backend pueda reservar.

Chat de WhatsApp en un teléfono con una lista interactiva de horas de visita y el slot elegido resaltado como respuesta enviada.
Experiencia del cliente

El cliente toca un slot libre

Antes de automatizar, los clientes adivinan horas en texto libre y el personal vuelve a revisar la agenda a mano.

Cuando llega la lista, el cliente abre el menú, toca una fila y ve su elección enviada en el mismo chat. Los integradores construyen este resultado con la WhatsApp API.

Flujo

De slots libres a un id capturado

1

La reserva necesita elegir hora

Tu lógica de agenda en own_backend llega al paso en que el cliente debe elegir entre las visitas libres actuales.

2

Envía el mensaje de lista de slots

POST /messages/interactive

POST a /messages/interactive con type list, textos de header/body/footer y filas en action.list.sections (title, id, description) desde slots libres—normalmente hasta diez filas en una sección. Mapea cada id de fila a una clave de slot que tu backend pueda reservar (por ejemplo slot-2026-04-02-10-00).

¿Necesitas la pulsación del cliente en tu webhook? Después de enviar la lista, escucha la respuesta entrante en el webhook del canal. Más sobre el estado de entrega →

Requisitos previos

Antes de ejecutar el flujo

Conecta el número de WhatsApp que enviará listas de reserva a Whapi.Cloud y usa el token API de ese canal en sendMessageInteractive. Los eventos list_reply entrantes requieren una URL de webhook en el mismo canal.

Opcional — profundiza en este flujo

Implementación de API

Construye la petición de lista paso a paso

Un POST entrega el menú. Pon type en list, rellena header, body y footer, luego action.list con etiqueta del botón y sections[].rows—cada fila necesita title, id y description opcional ligados a un slot libre de tu backend.

Obtén tu token de API gratis tras registrarte en whapi.cloud y úsalo en el código en lugar de Your_Token de los ejemplos.

Envía el mensaje de lista de slots

Envía una lista interactiva (type list) al cliente con hasta diez filas de slot; el cliente abre el menú con action.list.label y toca una fila.

curl --request POST \
     --url https://gate.whapi.cloud/messages/interactive \
     --header 'accept: application/json' \
     --header 'authorization: Bearer Your_Token' \
     --header 'content-type: application/json' \
     --data '
{
  "to": "447123456789",
  "type": "list",
  "header": {
    "text": "Choose a visit time"
  },
  "body": {
    "text": "Tap View times and pick an open slot for your visit."
  },
  "footer": {
    "text": "Clinic bookings via WhatsApp"
  },
  "action": {
    "list": {
      "label": "View times",
      "sections": [
        {
          "title": "Available slots",
          "rows": [
            {
              "id": "slot-2026-04-02-10-00",
              "title": "Wed 10:00",
              "description": "Open \u00b7 Dr. Lee"
            },
            {
              "id": "slot-2026-04-02-11-30",
              "title": "Wed 11:30",
              "description": "Open \u00b7 Dr. Lee"
            },
            {
              "id": "slot-2026-04-02-14-00",
              "title": "Wed 14:00",
              "description": "Open \u00b7 Dr. Lee"
            }
          ]
        }
      ]
    }
  }
}
'
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://gate.whapi.cloud/messages/interactive",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\"to\": \"447123456789\", \"type\": \"list\", \"header\": {\"text\": \"Choose a visit time\"}, \"body\": {\"text\": \"Tap View times and pick an open slot for your visit.\"}, \"footer\": {\"text\": \"Clinic bookings via WhatsApp\"}, \"action\": {\"list\": {\"label\": \"View times\", \"sections\": [{\"title\": \"Available slots\", \"rows\": [{\"id\": \"slot-2026-04-02-10-00\", \"title\": \"Wed 10:00\", \"description\": \"Open \\u00b7 Dr. Lee\"}, {\"id\": \"slot-2026-04-02-11-30\", \"title\": \"Wed 11:30\", \"description\": \"Open \\u00b7 Dr. Lee\"}, {\"id\": \"slot-2026-04-02-14-00\", \"title\": \"Wed 14:00\", \"description\": \"Open \\u00b7 Dr. Lee\"}]}]}}}",
  CURLOPT_HTTPHEADER => [
    "accept: application/json",
    "authorization: Bearer Your_Token",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
curl_close($curl);
echo $response;
import requests

url = "https://gate.whapi.cloud/messages/interactive"
payload = {
    "to": "447123456789",
    "type": "list",
    "header": {
        "text": "Choose a visit time"
    },
    "body": {
        "text": "Tap View times and pick an open slot for your visit."
    },
    "footer": {
        "text": "Clinic bookings via WhatsApp"
    },
    "action": {
        "list": {
            "label": "View times",
            "sections": [
                {
                    "title": "Available slots",
                    "rows": [
                        {
                            "id": "slot-2026-04-02-10-00",
                            "title": "Wed 10:00",
                            "description": "Open \u00b7 Dr. Lee"
                        },
                        {
                            "id": "slot-2026-04-02-11-30",
                            "title": "Wed 11:30",
                            "description": "Open \u00b7 Dr. Lee"
                        },
                        {
                            "id": "slot-2026-04-02-14-00",
                            "title": "Wed 14:00",
                            "description": "Open \u00b7 Dr. Lee"
                        }
                    ]
                }
            ]
        }
    }
}
headers = {
    "accept": "application/json",
    "authorization": "Bearer Your_Token",
    "content-type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)
print(response.text)
const response = await fetch('https://gate.whapi.cloud/messages/interactive', {
  method: 'POST',
  headers: {
    accept: 'application/json',
    authorization: 'Bearer Your_Token',
    'content-type': 'application/json',
  },
  body: JSON.stringify({"to": "447123456789", "type": "list", "header": {"text": "Choose a visit time"}, "body": {"text": "Tap View times and pick an open slot for your visit."}, "footer": {"text": "Clinic bookings via WhatsApp"}, "action": {"list": {"label": "View times", "sections": [{"title": "Available slots", "rows": [{"id": "slot-2026-04-02-10-00", "title": "Wed 10:00", "description": "Open \u00b7 Dr. Lee"}, {"id": "slot-2026-04-02-11-30", "title": "Wed 11:30", "description": "Open \u00b7 Dr. Lee"}, {"id": "slot-2026-04-02-14-00", "title": "Wed 14:00", "description": "Open \u00b7 Dr. Lee"}]}]}}}),
});

console.log(await response.text());
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"to\": \"447123456789\", \"type\": \"list\", \"header\": {\"text\": \"Choose a visit time\"}, \"body\": {\"text\": \"Tap View times and pick an open slot for your visit.\"}, \"footer\": {\"text\": \"Clinic bookings via WhatsApp\"}, \"action\": {\"list\": {\"label\": \"View times\", \"sections\": [{\"title\": \"Available slots\", \"rows\": [{\"id\": \"slot-2026-04-02-10-00\", \"title\": \"Wed 10:00\", \"description\": \"Open \\u00b7 Dr. Lee\"}, {\"id\": \"slot-2026-04-02-11-30\", \"title\": \"Wed 11:30\", \"description\": \"Open \\u00b7 Dr. Lee\"}, {\"id\": \"slot-2026-04-02-14-00\", \"title\": \"Wed 14:00\", \"description\": \"Open \\u00b7 Dr. Lee\"}]}]}}}");
Request request = new Request.Builder()
  .url("https://gate.whapi.cloud/messages/interactive")
  .post(body)
  .addHeader("accept", "application/json")
  .addHeader("authorization", "Bearer Your_Token")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
using RestSharp;

var options = new RestClientOptions("https://gate.whapi.cloud/messages/interactive");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("accept", "application/json");
request.AddHeader("authorization", "Bearer Your_Token");
request.AddHeader("content-type", "application/json");
request.AddJsonBody(System.Text.Json.JsonSerializer.Deserialize<object>(@"{\"to\": \"447123456789\", \"type\": \"list\", \"header\": {\"text\": \"Choose a visit time\"}, \"body\": {\"text\": \"Tap View times and pick an open slot for your visit.\"}, \"footer\": {\"text\": \"Clinic bookings via WhatsApp\"}, \"action\": {\"list\": {\"label\": \"View times\", \"sections\": [{\"title\": \"Available slots\", \"rows\": [{\"id\": \"slot-2026-04-02-10-00\", \"title\": \"Wed 10:00\", \"description\": \"Open \\u00b7 Dr. Lee\"}, {\"id\": \"slot-2026-04-02-11-30\", \"title\": \"Wed 11:30\", \"description\": \"Open \\u00b7 Dr. Lee\"}, {\"id\": \"slot-2026-04-02-14-00\", \"title\": \"Wed 14:00\", \"description\": \"Open \\u00b7 Dr. Lee\"}]}]}}}"));
var response = await client.PostAsync(request);

Console.WriteLine("{0}", response.Content);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	body := []byte(`{"to": "447123456789", "type": "list", "header": {"text": "Choose a visit time"}, "body": {"text": "Tap View times and pick an open slot for your visit."}, "footer": {"text": "Clinic bookings via WhatsApp"}, "action": {"list": {"label": "View times", "sections": [{"title": "Available slots", "rows": [{"id": "slot-2026-04-02-10-00", "title": "Wed 10:00", "description": "Open \u00b7 Dr. Lee"}, {"id": "slot-2026-04-02-11-30", "title": "Wed 11:30", "description": "Open \u00b7 Dr. Lee"}, {"id": "slot-2026-04-02-14-00", "title": "Wed 14:00", "description": "Open \u00b7 Dr. Lee"}]}]}}}`)
	req, _ := http.NewRequest("POST", "https://gate.whapi.cloud/messages/interactive", bytes.NewReader(body))
	req.Header.Set("accept", "application/json")
	req.Header.Set("authorization", "Bearer Your_Token")
	req.Header.Set("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
Elección entrante

Lee el slot desde list_reply

Los webhooks de estado de entrega siguen aplicando a la lista saliente. La decisión de reserva llega aparte en messages.post cuando el cliente toca una fila—busca reply.type list_reply y reply.list_reply.id (más title/description) para mapear a tu id de fila.

  • Configura el webhook del canal antes de probar toques; el payload incluye event.type messages y event.event post.

  • Haz coincidir reply.list_reply.id con el id de fila enviado (Whapi puede prefijar ids como ListV3:r1—normaliza en own_backend).

  • Si el slot ya no está libre cuando llega el webhook, no escribas en el calendario; reenvía una lista actualizada o pasa a personal según tu failure_mode.

Qué leer sobre entrega y webhooks

Contacta con soporte si algo salió mal
Cuando algo falla

Trata los fallos en cada paso

Si sendMessageInteractive falla tras tu disparador de reserva, el cliente nunca ve la lista de slots—reintenta o avisa al personal antes de asumir que ya eligió hora.

Cuando la lista nunca llega al cliente

Un envío fallido, un destinatario prohibido o una referencia de media ausente dejan al cliente sin filas que tocar. Reintenta con parámetros válidos, confirma el chat id en to y vuelve a agenda manual si cierra la ventana de slots. Contacta soporte Whapi si los envíos fallan con 401 o 500 repetidos en un canal sano.

Contacta con soporte si algo salió mal

Diagnostica por paso de API

Estado Qué significa Qué hacer
400 Wrong request parameters Corrige la forma del payload list—type list, body.text, action.list.label y al menos una fila con title e id según OpenAPI.
401 Need channel authorization for send message Envía el Bearer token del canal; confirma que el número sigue emparejado en el panel.
402 Trial version limit exceeded
403 It is forbidden to send to this group/recipient Confirma que el destinatario está permitido en este canal y que el chat id es correcto.
429 Too many requests Espera y reintenta; evita ráfagas reenviando la misma lista de slots.
500 Internal Error
Preguntas frecuentes

Slots en lista e ids de webhook

Planifica hasta diez filas en total en tus secciones—suficiente para un menú del mismo día o cercano. Si tienes más slots libres, pagina con una nueva lista tras elegir fecha o pide a own_backend que reduzca el conjunto antes de enviar.
Pon type en list, objetos de texto en header, body y footer, y anida action.list con label más sections[].rows (cada fila necesita title, id y description opcional). Apunta al teléfono del cliente en to—la misma forma que el curl de referencia del operador para /messages/interactive.
En messages.post, lee reply.list_reply.id y reply.list_reply.title. Mapea id al id de fila que asignaste al construir la lista y reserva o retiene ese slot en own_backend.
Trátalo como tu failure_mode del catálogo—reenvía una lista actualizada, caduca ids viejos o deriva el chat al personal. No confirmes una visita en tu calendario sin un list_reply reciente o una comprobación manual.
Funciones compatibles de WhatsApp

Todo lo que necesitas para crear automatizaciones en WhatsApp

Whapi.Cloud te da acceso a un conjunto completo de funciones de WhatsApp — mensajería, multimedia, grupos, canales, webhooks y más. Crea bots, automatizaciones e integraciones sin limitaciones de funciones.

Elegido por desarrolladores

Confiado por más de 3.300 desarrolladores y equipos SaaS

Desarrolladores de todo el mundo confían en Whapi.Cloud para crear automatizaciones, bots, integraciones con CRM, herramientas de mensajería masiva y flujos avanzados de WhatsApp. Nuestra infraestructura procesa millones de solicitudes API con alta estabilidad, y este número crece cada mes.

Icono de característica

5,5K+

canales API activos

Icono de característica

21M+

solicitudes API al mes

“Todo es simple, intuitivo y fácil de usar. La documentación es clara y toda la información está justo donde la necesito. Excelente trabajo.”

Roberto K. / Desarrollador Web

“Conectamos WhatsApp a nuestros flujos en n8n en solo unos minutos. Sin código, sin complicaciones — todo funcionó a la primera.”

Martin P. / Desarrollador

“Whapi.Cloud se convirtió en una parte clave de nuestra comunicación con clientes. Automatizamos actualizaciones de productos, notificaciones de pedidos y mensajes de soporte — sin plantillas ni aprobaciones de Meta.”

Oscar T. / CTO

Burbuja decorativa