Wow! Redução de 65% no preço para clientes brasileiros! Registre-se agora
Marcação de consultas · caso de uso WhatsApp API

Deixe os clientes escolher um horário de visita no WhatsApp a partir de uma lista

Os clientes concluem a reserva mais depressa quando tocam num horário claro em vez de escrever ida e volta. Quando o fluxo de reserva chega ao passo de escolher hora, o backend pode fazer POST de uma lista interactiva para o chat do cliente via Whapi.Cloud. Este caso de uso mostra aos integradores como implementar esse fluxo com a WhatsApp API.

type list com até dez linhas de slot A escolha chega como list_reply em messages.post
Painel de desenvolvimento junto a um telemóvel com um chat de reserva no WhatsApp e uma lista interactiva aberta de horários de consulta.
Cenário de uso

Quando usar este fluxo

Os slots livres já estão no seu calendário

Use este fluxo quando own_backend já sabe que horários de visita estão livres e só precisa que o cliente escolha um no chat.

A reserva continua num chat privado do WhatsApp

Use este fluxo quando o cliente reserva num direct_chat com o número da empresa, não num grupo nem num canal.

Quer um toque estruturado em vez de texto livre

Use este fluxo quando escrever «terça 15:00» gera erros e prefere uma linha de lista com um id de slot estável que o backend possa reservar.

Chat do WhatsApp num telemóvel com uma lista interactiva de horários de visita e o slot escolhido destacado como resposta enviada.
Experiência do cliente

O cliente toca num slot livre

Antes da automatização, os clientes adivinham horários em texto livre e a equipa volta a verificar o calendário à mão.

Quando a lista chega, o cliente abre o menu, toca numa linha e vê a escolha enviada no mesmo chat. Os integradores constroem este resultado com a WhatsApp API.

Fluxo

De slots livres a um id capturado

1

A reserva precisa de escolher hora

A lógica de agenda em own_backend chega ao passo em que o cliente deve escolher entre os horários de visita livres actuais.

2

Envie a mensagem de lista de slots

POST /messages/interactive

POST a /messages/interactive com type list, textos de header/body/footer e linhas em action.list.sections (title, id, description) a partir de slots livres—normalmente até dez linhas numa secção. Mapeie cada id de linha para uma chave de slot que o backend possa reservar (por exemplo slot-2026-04-02-10-00).

Precisa do toque do cliente no webhook? Depois de enviar a lista, escute a resposta entrante no webhook do canal. Saiba mais sobre o estado de entrega →

Pré-requisitos

Antes de executar o fluxo

Ligue o número de WhatsApp que enviará listas de reserva ao Whapi.Cloud e use o token API desse canal em sendMessageInteractive. Eventos list_reply entrantes exigem um URL de webhook no mesmo canal.

Opcional — aprofunde este fluxo

  • Envie mensagens interactivas de lista e botões: Abra o guia
  • Formato de payload de webhook para mensagens entrantes: Ver os formatos
  • Padrão evento-reacção para integrações WhatsApp: Ler o artigo
Implementação da API

Construa o pedido de lista passo a passo

Um POST entrega o menu. Defina type como list, preencha header, body e footer, depois action.list com etiqueta do botão e sections[].rows—cada linha precisa de title, id e description opcional ligados a um slot livre do backend.

Obtenha o token de API grátis após registar-se em whapi.cloud e use-o no código em vez de Your_Token dos exemplos.

Envie a mensagem de lista de slots

Envie uma lista interactiva (type list) ao cliente com até dez linhas de slot; o cliente abre o menu com action.list.label e toca numa linha.

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))
}
Escolha recebida

Leia o slot a partir de list_reply

Os webhooks de estado de entrega continuam a aplicar-se à lista enviada. A decisão de reserva chega à parte em messages.post quando o cliente toca numa linha—procure reply.type list_reply e reply.list_reply.id (mais title/description) para mapear ao id de linha.

  • Configure o webhook do canal antes de testar toques; o payload inclui event.type messages e event.event post.

  • Faça corresponder reply.list_reply.id ao id de linha enviado (Whapi pode prefixar ids como ListV3:r1—normalize em own_backend).

  • Se o slot já não estiver livre quando o webhook chegar, não escreva no calendário; reenvie uma lista actualizada ou passe para a equipa conforme o failure_mode.

O que ler sobre entrega e webhooks

Contacte o suporte se algo correu mal
Quando algo falha

Trate as falhas em cada passo

Se sendMessageInteractive falhar após o disparador de reserva, o cliente nunca vê a lista de slots—tente de novo ou alerte a equipa antes de assumir que já escolheu hora.

Quando a lista nunca chega ao cliente

Um envio falhado, destinatário proibido ou referência de media em falta deixam o cliente sem linhas para tocar. Tente de novo com parâmetros válidos, confirme o chat id em to e volte à agenda manual se a janela de slots fechar. Contacte o suporte Whapi se os envios falharem com 401 ou 500 repetidos num canal saudável.

Contacte o suporte se algo correu mal

Diagnostique por passo da API

Situação O que significa O que fazer
400 Wrong request parameters Corrija a forma do payload list—type list, body.text, action.list.label e pelo menos uma linha com title e id segundo OpenAPI.
401 Need channel authorization for send message Envie o Bearer token do canal; confirme que o número continua emparelhado no painel.
402 Trial version limit exceeded
403 It is forbidden to send to this group/recipient Confirme que o destinatário é permitido neste canal e que o chat id está correcto.
429 Too many requests Aguarde e tente de novo; evite rajadas reenviando a mesma lista de slots.
500 Internal Error
Perguntas frequentes

Slots em lista e ids de webhook

Planeie até dez linhas no total nas secções—suficiente para um menu do mesmo dia ou próximo. Se tiver mais slots livres, pagine com uma nova lista após escolher data ou peça a own_backend para reduzir o conjunto antes de enviar.
Defina type como list, objectos de texto em header, body e footer, e aninhe action.list com label mais sections[].rows (cada linha precisa de title, id e description opcional). Aponte para o telefone do cliente em to—a mesma forma que o curl de referência do operador para /messages/interactive.
Em messages.post, leia reply.list_reply.id e reply.list_reply.title. Mapeie id ao id de linha que atribuiu ao construir a lista e reserve ou retém esse slot em own_backend.
Trate como o failure_mode do catálogo—reenvie uma lista actualizada, expire ids antigos ou encaminhe o chat para a equipa. Não confirme uma visita no calendário sem um list_reply recente ou verificação manual.
Recursos compatíveis do WhatsApp

Tudo o que você precisa para criar automações no WhatsApp

O Whapi.Cloud oferece acesso a um conjunto completo de recursos do WhatsApp — mensagens, mídia, grupos, canais, webhooks e muito mais. Crie bots, automações e integrações sem limitações de funcionalidades.

Escolhido por desenvolvedores

Confiado por mais de 3.300 desenvolvedores e equipes SaaS

Desenvolvedores no mundo inteiro utilizam o Whapi.Cloud para criar automações, bots, integrações com CRM, ferramentas de envio em massa e fluxos avançados de WhatsApp. Nossa infraestrutura processa milhões de requisições API com alta estabilidade — e esse número cresce a cada mês.

Ícone de recurso

5,5K+

canais de API ativos

Ícone de recurso

21M+

requisições API por mês

“Tudo é simples, intuitivo e fácil de navegar. A documentação é clara e todas as informações estão bem à mão. Trabalho excelente.”

Roberto K. / Desenvolvedor Web

“Conectamos o WhatsApp aos nossos fluxos usando o n8n em apenas alguns minutos. Sem código, sem complicações — funcionou de primeira.”

Martin P. / Desenvolvedor

“O Whapi.Cloud se tornou uma parte essencial da nossa comunicação com clientes. Automatizamos atualizações de produtos, notificações de pedidos e mensagens de suporte — tudo sem templates nem aprovações da Meta.”

Oscar T. / CTO

Balão decorativo