Última atualização em 10 de Março de 2026
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.
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).
Para que a página de agendamento funcione corretamente, é necessário configurar por membro (cada pessoa que atenderá agendamentos):
Cada membro vinculado à página deve ter horários de disponibilidade por dia da semana:
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.
O papel do calendário externo depende do MeetingType da página.
Se o membro usa Google Calendar, Outlook ou feed iCal e quer que eventos desses calendários bloqueiem horários na disponibilidade:
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.
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. |
Quando o token OAuth de um calendário expira ou é revogado:
UserExternalCalendar.IsActive = false.IsActive volta a true e o membro reaparece automaticamente nos slots.Para bloquear períodos pontuais (férias, feriado, consulta médica, licença):
Não é obrigatório; se não houver interrupções, apenas business hours e outros bloqueios (appointments, calendário) são considerados.
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:
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.
Base URL dos endpoints: integration/v1/scheduling/{slug}.
Todos exigem os headers:
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.
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:
memberId -> lista de horários "HH:mm"."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.
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. |
A consulta de disponibilidade e a validação no book seguem a mesma lógica. Abaixo, o fluxo em linhas gerais.
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:
OrganizationCalendarConnectionId preenchido.CanCreateOrganizationEvents = true.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.
Para cada dia no intervalo solicitado (start a end), o sistema gera slots candidatos para cada membro elegível:
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.
Antes de aplicar bloqueios, são descartados slots que:
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.
Depois de calcular os slots de cada membro:
Ao criar um agendamento (POST book), o sistema:
Assim, mesmo com dados em cache no front, o backend garante que só cria agendamento em horário realmente disponível.
| 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.