Como funciona uma Página de Agendamento (SchedulingPage)

Última atualização em 10 de Março de 2026


Página de Agendamento (SchedulingPage)

Este guia explica em detalhes o que é uma SchedulingPage, quais configurações precisam ser feitas antes de usá-la, como criar e usar a API Key da organização, e como funciona a consulta de disponibilidade — incluindo todos os parâmetros e filtros levados em conta.


O que é uma SchedulingPage

Uma SchedulingPage é uma página de agendamento vinculada à organização. Ela possui um slug único (ex.: stelis-diagnostico) e define as regras para que visitantes ou integrações externas possam consultar horários disponíveis e criar agendamentos.

Principais propriedades da página:

Propriedade Descrição
Name Nome da página (ex.: "Diagnóstico Gratuito - Atendimento").
Slug Identificador único na URL (ex.: minha-org-atendimento).
DurationMinutes Duração de cada agendamento em minutos (15, 30, 60, etc.).
BufferBeforeMinutes Minutos de "respiro" antes de cada reunião (bloqueia o slot antes).
BufferAfterMinutes Minutos de "respiro" após cada reunião (bloqueia o slot depois).
MaxAdvanceDays Máximo de dias no futuro em que é possível agendar (ex.: 60).
MinNoticeHours Antecedência mínima em horas (ex.: 4 = não mostra slots nas próximas 4 h).
AssignmentMode Como o atendente é escolhido: Específico, Round Robin ou Primeiro Disponível.
IsActive Se a página está ativa para agendamentos.
MeetingType Tipo de reunião: Physical (presencial), CustomUrl (URL fixa de Zoom, Whereby, etc.) ou Online (link de Meet/Teams gerado automaticamente conforme o calendário de cada membro).
MeetingLocation Endereço físico (Physical) ou URL fixa (CustomUrl). Ignorado quando Online.
ConfirmationEmailEnabled Se envia e-mail de confirmação após o book.

A página tem membros (SchedulingPageMember): usuários da organização que podem ser escolhidos ou atribuídos automaticamente para os agendamentos. Cada membro tem prioridade (para Round Robin) e pode ter vínculo com um calendário externo (OrganizationCalendarConnection). O tipo de reunião é definido na página e o provedor de reunião online é determinado por membro, conforme o calendário conectado (Google → Meet, Outlook → Teams).


Configurações necessárias antes de usar a página

Para que a página de agendamento funcione corretamente, é necessário configurar por membro (cada pessoa que atenderá agendamentos):

1. Horários de disponibilidade (MemberBusinessHours)

Cada membro vinculado à página deve ter horários de disponibilidade por dia da semana:

  • MemberBusinessHours: um registro por dia da semana (segunda a domingo) por membro.
  • Para cada dia é possível:
    • IsClosed = true: não trabalha nesse dia (nenhum slot).
    • IsOpen24Hours = true: disponível o dia todo (slots de 00:00 a 23:59).
    • Caso contrário: usar MemberBusinessHoursInterval com OpeningTime e ClosingTime (formato HH:mm), em um ou mais intervalos (ex.: 09:00–12:00 e 14:00–18:00).

Apenas intervalos com IsAvailableForScheduling = true geram slots de agendamento. Intervalos apenas para chat não entram no cálculo.

Sem business hours configurados para um dia, o membro não terá slots naquele dia.

2. Calendários externos e vínculo com a página

O papel do calendário externo depende do MeetingType da página.

2a. Bloqueio de agenda (qualquer MeetingType)

Se o membro usa Google Calendar, Outlook ou feed iCal e quer que eventos desses calendários bloqueiem horários na disponibilidade:

  • O usuário deve conectar o calendário (UserExternalCalendar) à conta.
  • Deve existir um OrganizationCalendarConnection vinculando esse calendário à organização e ao membro (OrganizationUserId).
  • Na conexão, BlockSchedule = true: os eventos desse calendário passam a ser considerados como "ocupado" no cálculo de slots.

O sistema usa cache (Redis, TTL 5 min) para consultar os blocos "busy" dos calendários. Apenas conexões com BlockSchedule = true entram nesse cálculo.

2b. Criação de eventos e links de reunião (conforme MeetingType)

O comportamento do vínculo calendário-membro (OrganizationCalendarConnectionId no SchedulingPageMember) varia conforme o tipo de reunião da página:

MeetingType Conexão de calendário Comportamento
Online Obrigatória. A conexão deve ter CanCreateOrganizationEvents = true e o UserExternalCalendar deve ter CanCreateMeeting = true e IsActive = true. Cria evento no calendário e gera link de reunião (Google → Meet, Outlook → Teams). Cada membro pode usar um provedor diferente.
Physical Opcional. Se houver conexão com CanCreateOrganizationEvents = true, cria evento no calendário (sem link de reunião). Usa MeetingLocation da página como local.
CustomUrl Opcional. Se houver conexão com CanCreateOrganizationEvents = true, cria evento no calendário (sem link de reunião). Usa MeetingLocation da página como URL.
Undefined Não utilizada. Nenhuma ação de calendário.

2c. Falha de calendário e notificação

Quando o token OAuth de um calendário expira ou é revogado:

  1. O sistema detecta a falha ao tentar renovar o token e marca UserExternalCalendar.IsActive = false.
  2. O usuário recebe uma notificação (InWebAndPushWeb) informando qual calendário falhou e orientando a reconectar.
  3. Em páginas Online, membros com calendário inativo deixam de aparecer nos slots de disponibilidade — não são exibidos horários para quem não pode gerar a reunião.
  4. Quando o usuário reconecta o calendário, IsActive volta a true e o membro reaparece automaticamente nos slots.

3. Interrupções (MemberInterruption)

Para bloquear períodos pontuais (férias, feriado, consulta médica, licença):

  • Cadastrar MemberInterruption para o membro com StartsAt e EndsAt.
  • Qualquer horário dentro desse intervalo deixa de gerar slots para esse membro.

Não é obrigatório; se não houver interrupções, apenas business hours e outros bloqueios (appointments, calendário) são considerados.

4. API Key da organização (para as APIs de Integration)

As APIs de agendamento (dados da página, disponibilidade e book) estão na área Integration e exigem autenticação por API Key da organização:

  • client-id: ID da organização (GUID).
  • api-key: chave gerada e vinculada à organização (ex.: via área administrativa / Organization API Keys).

Ambos devem ser enviados nos headers de cada requisição. Sem eles, a API retorna 401.

A criação e gestão de API Keys são feitas pela organização (ex.: tela de configurações ou endpoints de administração de OrganizationApiKey). O front ou a integração externa deve armazenar e enviar esses valores de forma segura.


APIs disponíveis (Integration)

Base URL dos endpoints: integration/v1/scheduling/{slug}.

Todos exigem os headers:

  • client-id: GUID da organização.
  • api-key: API Key da organização.

GET integration/v1/scheduling/{slug}

Retorna os dados públicos da página (nome, descrição, duração, local, se exibe membros e lista de membros quando aplicável). Útil para montar a tela de agendamento no front.

GET integration/v1/scheduling/{slug}/availability

Retorna a disponibilidade de slots no período.

Query params:

Parâmetro Tipo Obrigatório Descrição
start DateTime (date) Não Data inicial do período. Padrão: hoje.
end DateTime (date) Não Data final do período. Padrão: start + 7 dias.

Regra: end deve ser igual ou posterior a start. O período efetivo é limitado a no máximo 31 dias (se end - start for maior, o fim é ajustado).

Filtro de membros em páginas Online: antes de calcular os slots, membros cuja conexão de calendário esteja inativa (UserExternalCalendar.IsActive = false) ou sem permissão de criação de reunião (CanCreateMeeting = false) são excluídos. Se nenhum membro elegível restar, a disponibilidade retorna vazia.

Resposta: Objeto com scheduling_page (dados resumidos da página) e availability: dicionário em que a chave é a data no formato yyyy-MM-dd e o valor:

  • Se ShowMembers = true (modo Específico): mapa memberId -> lista de horários "HH:mm".
  • Se ShowMembers = false (Round Robin / Primeiro Disponível): lista única de horários "HH:mm" (união dos slots de todos os membros).

Os horários estão em UTC (ou no fuso usado pelo servidor para geração dos slots). O front deve exibir no fuso desejado.

POST integration/v1/scheduling/{slug}/book

Cria um agendamento.

Body (JSON):

Campo Tipo Obrigatório Descrição
starts_at DateTime Sim Data/hora de início do agendamento.
member_id Guid Condicional Obrigatório se a página tiver ShowMembers = true (modo Específico). ID do OrganizationUser do membro escolhido.
contact_name string Não Nome do contato.
contact_email string Não* E-mail do contato. *Obrigatório e-mail ou WhatsApp se não enviar lead_id.
contact_whatsapp string Não* WhatsApp do contato.
notes string Não Observações.
lead_id Guid Não Se informado, vincula o agendamento a um lead existente (e ao contato principal do lead).

O sistema valida se o horário starts_at está realmente disponível (mesma lógica da consulta de disponibilidade). Se não estiver (ex.: dados desatualizados no front), retorna 400 com mensagem de horário não disponível.

Após criar o agendamento, o comportamento depende do MeetingType:

MeetingType Ação pós-book
Online Usa a conexão de calendário do membro atribuído para criar evento com link de reunião (Google → Meet, Outlook → Teams). O link é salvo em Appointment.MeetingUrl.
Physical / CustomUrl Se o membro tem conexão de calendário, cria evento sem link de reunião. O MeetingLocation da página é usado como local/URL.
Undefined Nenhuma ação de calendário.

Lógica de disponibilidade — visão geral

A consulta de disponibilidade e a validação no book seguem a mesma lógica. Abaixo, o fluxo em linhas gerais.

0. Filtragem de membros elegíveis (páginas Online)

Antes de gerar slots, em páginas com MeetingType = Online, o sistema carrega a conexão de calendário de cada membro e exclui aqueles que:

  • Não possuem OrganizationCalendarConnectionId preenchido.
  • A conexão não tem CanCreateOrganizationEvents = true.
  • O UserExternalCalendar vinculado tem IsActive = false ou CanCreateMeeting = false.

Se nenhum membro elegível restar, a disponibilidade retorna vazia. Para páginas Physical, CustomUrl ou Undefined este filtro não é aplicado.

1. Geração de slots candidatos

Para cada dia no intervalo solicitado (start a end), o sistema gera slots candidatos para cada membro elegível:

  • Consulta MemberBusinessHours do membro para aquele dia da semana.
  • Se IsClosed = true: nenhum slot.
  • Se IsOpen24Hours = true: gera slots de 00:00 até 23:59.
  • Caso contrário: percorre os MemberBusinessHoursInterval com IsAvailableForScheduling = true e gera slots dentro de cada intervalo (ex.: 09:00–12:00, 14:00–18:00).

Os slots são gerados a cada DurationMinutes minutos. Ex.: duração 30 min e intervalo 09:00–12:00 → slots 09:00, 09:30, 10:00, 10:30, 11:00, 11:30.

2. Filtragem por regras da página

Antes de aplicar bloqueios, são descartados slots que:

  • Estão no passado.
  • Não respeitam MinNoticeHours (ex.: se são 14h e MinNoticeHours = 4, descarta tudo antes das 18h).
  • Estão além de MaxAdvanceDays (ex.: hoje + 60 dias).

3. Remoção de slots bloqueados (conflitos)

Para cada slot candidato restante, o sistema verifica sobreposição com:

Fonte Descrição
Appointments Agendamentos já existentes (status diferente de Cancelled) do membro. A faixa bloqueada é expandida: [StartsAt - BufferBeforeMinutes, EndsAt + BufferAfterMinutes].
MemberInterruption Interrupções cadastradas do membro (férias, feriado, etc.). Bloqueiam o período exato (sem expansão de buffer).
Calendários externos Eventos dos calendários conectados à organização cuja OrganizationCalendarConnection tem BlockSchedule = true. A faixa bloqueada é expandida com BufferBeforeMinutes e BufferAfterMinutes.

Exemplo de buffer: reunião de 30 min com buffer 10 min antes e 10 min depois. O horário 09:00–09:30 bloqueia efetivamente 08:50–09:40. O próximo slot livre seria 09:50 (precisa dos 10 min de buffer antes).

Observação: Tarefas do quadro (CardActions) não bloqueiam agendamento; apenas appointments, interrupções e calendários externos (com BlockSchedule) são considerados.

4. Modo de atribuição (AssignmentMode)

Depois de calcular os slots de cada membro:

  • Específico (ShowMembers = true): a resposta traz slots por membro. O visitante escolhe o membro e o horário. No book é obrigatório enviar member_id.
  • Round Robin / Primeiro Disponível (ShowMembers = false): os slots de todos os membros são agregados em uma lista única. Se qualquer membro tem o slot livre, ele aparece. No book não se envia member_id; o sistema atribui internamente (ex.: primeiro membro por prioridade).

5. Validação no book

Ao criar um agendamento (POST book), o sistema:

  • Garante que a página pertence à organização do client-id e que IsActive está ativo.
  • Determina o membro (por member_id no modo Específico ou pelo primeiro membro no outro modo).
  • Chama a mesma lógica de disponibilidade para o horário starts_at e o membro escolhido. Se o slot não estiver livre (business hours, buffers, appointments, interrupções, calendários externos), retorna erro 400 — Horário não disponível.

Assim, mesmo com dados em cache no front, o backend garante que só cria agendamento em horário realmente disponível.


Resumo dos parâmetros e filtros na disponibilidade

Origem O que é considerado
SchedulingPage IsActive, DurationMinutes, MinNoticeHours, MaxAdvanceDays, BufferBeforeMinutes, BufferAfterMinutes, AssignmentMode, MeetingType.
SchedulingPageMember Quais membros atendem; prioridade (Round Robin); vínculo com calendário (OrganizationCalendarConnectionId). Em páginas Online, apenas membros com conexão ativa e CanCreateMeeting participam.
MemberBusinessHours Por dia da semana: IsClosed, IsOpen24Hours ou intervalos (OpeningTime/ClosingTime). Apenas intervalos com IsAvailableForScheduling = true geram slots.
MemberInterruption Períodos (StartsAt, EndsAt) em que o membro está indisponível.
Appointment Agendamentos do membro com status ≠ Cancelled; faixa expandida por BufferBefore e BufferAfter.
OrganizationCalendarConnection Conexões com BlockSchedule = true: eventos do calendário externo bloqueiam slots (faixa expandida por buffer). Conexões com CanCreateOrganizationEvents = true: criam evento no calendário ao receber agendamento.
UserExternalCalendar IsActive: se false, membro é excluído de páginas Online. CanCreateMeeting: se false, membro não pode gerar link de reunião. Provider: determina o provedor de reunião (Google → Meet, Outlook → Teams).

Com isso, você tem uma visão completa de como configurar uma SchedulingPage, o que precisa ser feito antes (business hours, calendário, interrupções, API Key) e como a consulta de disponibilidade e o book funcionam, com todos os parâmetros e filtros envolvidos.