Agendamentos
A API de Agendamentos permite que você marque compromissos para seus contatos em seus tipos de evento, além de buscar, listar, atualizar, cancelar ou excluí-los. Ela também responde à pergunta que surge primeiro na maioria dos fluxos de agendamento — quais horários estão realmente livres — e cobre o lado do calendário: listar os Google Calendars que você conectou e importar eventos que já existem neles. Quando uma conexão com o Google Calendar está ativa, o evento de calendário correspondente é criado e mantido sincronizado automaticamente em segundo plano. Restaurantes que usam Zenchef, Formitable, OpenTable ou TheFork para seus próprios sistemas de reserva também podem ser verificados e conectados aqui, para que o Agente de IA reserve mesas reais em vez de agendamentos internos — e empresas de agendamento que gerenciam seus horários no Trafft podem se conectar da mesma maneira.
Todos os caminhos nesta página são relativos à URL base https://api.dmchamp.com/v1. Cada solicitação precisa da sua chave de API — consulte Autenticação para obter a lista completa de formas de enviá-la. Os exemplos abaixo usam o cabeçalho X-API-Key, com um exemplo cURL mostrando também o formato de consulta ?apiKey=.
Eventos vs. agendamentos: Um tipo de evento é uma definição de intervalo reservável (o tipo de reunião, sua duração, suas salas). Um agendamento é uma instância reservada de um tipo de evento para um contato específico. Você reserva um agendamento referenciando o contato e o tipo de evento.
O objeto de agendamento
Cada endpoint que retorna um agendamento usa a mesma estrutura:
| Campo | Descrição |
|---|---|
id |
ID exclusivo do agendamento. |
contact_id |
ID do contato com o qual o agendamento foi marcado. |
event_id |
ID do tipo de evento no qual o agendamento foi marcado. |
status |
Confirmed ou Canceled. |
start_time |
Início do agendamento, ISO 8601 em UTC. |
end_time |
Fim do agendamento, ISO 8601 em UTC. |
created_at |
Quando o agendamento foi criado. |
last_modified_at |
Quando o agendamento foi alterado pela última vez. |
room_name |
Sala ou recurso no qual o agendamento foi marcado, quando o tipo de evento usa salas. |
description |
Descrição de formato livre do agendamento. |
summary |
Resumo ou título curto. |
cancelation_reason |
Motivo fornecido quando o agendamento foi cancelado, se houver. |
google_calendar_event_id |
ID do evento vinculado do Google Agenda. Definido assim que a sincronização do calendário for concluída; null quando nenhum calendário estiver conectado ou enquanto a sincronização ainda estiver em andamento. |
calendar_synced |
true assim que o agendamento estiver vinculado a um evento de calendário. |
imported |
true quando o agendamento foi importado de um calendário externo em vez de reservado diretamente. |
is_recurring |
true quando o agendamento faz parte de uma série recorrente. |
recurrence_frequency |
Com que frequência o agendamento se repete, quando recorrente. |
recurring_event_id |
ID da série recorrente à qual este agendamento pertence. |
recurring_interval |
Intervalo entre repetições, quando recorrente. |
recurring_sequence |
Posição deste agendamento dentro de sua série recorrente. |
end_after_x_occurrences |
Número de ocorrências após as quais a série recorrente termina. |
booking_provider |
Sistema de origem de onde veio a reserva, quando feita por meio de um provedor de reserva conectado. |
Sobre a sincronização de calendário: Logo após você reservar ou alterar um agendamento,
google_calendar_event_idpode ainda sernullecalendar_syncedpode serfalseporque a sincronização é executada em segundo plano um momento depois. Busque o agendamento novamente pouco tempo depois para ver os campos de calendário preenchidos.
Encontrar horários disponíveis
GET /appointments/available-slots
Retorna os horários que estão genuinamente livres em um tipo de evento entre dois momentos. Esta é normalmente a primeira chamada em um fluxo de agendamento: mostre esses horários, deixe a pessoa escolher um e, em seguida, envie o horário escolhido para Agendar um compromisso.
A resposta já leva em conta o horário de funcionamento e a duração do intervalo do próprio tipo de evento, suas salas, compromissos que você já agendou nele e tudo o que está bloqueado nos Google Calendars conectados — portanto, um horário que aparece aqui é um que você pode reservar.
| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
event_id |
Sim | O tipo de evento a ser verificado. Deve pertencer à sua conta. |
start_time |
Sim | Início da janela para a qual você deseja horários, data e hora em ISO 8601. |
end_time |
Sim | Fim da janela, data e hora em ISO 8601. O dia final completo está incluído. |
Os resultados são retornados agrupados por dia — e, quando o tipo de evento usa salas, um grupo por sala por dia:
| Campo | Descrição |
|---|---|
date |
O dia que o grupo cobre, escrito DD/MM/YYYY. |
day |
Nome do dia da semana em letras minúsculas, por exemplo monday. |
room_name |
A sala ou recurso ao qual este grupo pertence, quando o tipo de evento usa salas. |
available_slots |
Os blocos reserváveis naquele dia, do mais cedo para o mais tarde. |
Cada entrada em available_slots possui:
| Campo | Descrição |
|---|---|
start_time |
Início do bloco como HH:mm. |
end_time |
Fim do bloco como HH:mm. |
available |
true — apenas o tempo livre é retornado. |
spots_left |
Quantas reservas ainda cabem neste bloco. Presente apenas em tipos de evento que aceitam mais de uma reserva por intervalo. |
Os horários são locais ao tipo de evento, não UTC.
date,start_timeeend_timesão valores de relógio de parede no fuso horário do próprio tipo de evento (sua substituição, ou o fuso horário da sua conta quando não houver nenhum). Agendar um compromisso espera um instante UTC em ISO 8601, portanto, converta o horário que você escolheu antes de enviá-lo.
cURL
curl "https://api.dmchamp.com/v1/appointments/available-slots?event_id=event_xyz789&start_time=2026-06-15T00:00:00.000Z&end_time=2026-06-19T00:00:00.000Z" \
-H "X-API-Key: YOUR_API_KEY"
JavaScript
const params = new URLSearchParams({
event_id: "event_xyz789",
start_time: "2026-06-15T00:00:00.000Z",
end_time: "2026-06-19T00:00:00.000Z",
});
const res = await fetch(
`https://api.dmchamp.com/v1/appointments/available-slots?${params}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.data);
Python
import requests
res = requests.get(
"https://api.dmchamp.com/v1/appointments/available-slots",
headers={"X-API-Key": "YOUR_API_KEY"},
params={
"event_id": "event_xyz789",
"start_time": "2026-06-15T00:00:00.000Z",
"end_time": "2026-06-19T00:00:00.000Z",
},
)
print(res.json()["data"])
Resposta (200 OK):
{
"success": true,
"data": [
{
"date": "15/06/2026",
"day": "monday",
"room_name": "Room A",
"available_slots": [
{ "start_time": "10:00", "end_time": "10:30", "available": true },
{ "start_time": "10:30", "end_time": "11:00", "available": true }
]
},
{
"date": "16/06/2026",
"day": "tuesday",
"room_name": "Room A",
"available_slots": [
{ "start_time": "09:00", "end_time": "09:30", "available": true, "spots_left": 2 }
]
}
]
}
Um dia sem nada livre simplesmente não aparece. A falta de event_id, start_time ou end_time retorna 400; um tipo de evento que não está na sua conta retorna 404.
Reservar um agendamento
POST /appointments
Reserva um novo agendamento para um contato em um dos seus tipos de evento. O horário de término é calculado automaticamente a partir da duração do intervalo do tipo de evento.
A reserva passa por uma verificação de conflito: se o intervalo solicitado sobrepuser um agendamento confirmado existente no mesmo tipo de evento, a solicitação falhará com um 409 e nada será criado.
| Campo | Obrigatório | Descrição |
|---|---|---|
contact_id |
Sim | ID do contato para o qual reservar. Deve pertencer à sua conta. |
event_id |
Sim | ID do tipo de evento no qual reservar. Deve pertencer à sua conta. |
start_time |
Sim | Início desejado como uma data-hora ISO 8601. |
room_name |
Não | Nome da sala ou recurso, quando o tipo de evento usa salas. |
cURL (usando o formato de consulta ?apiKey=)
curl -X POST "https://api.dmchamp.com/v1/appointments?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contact_id": "contact_abc123",
"event_id": "event_xyz789",
"start_time": "2026-06-15T10:00:00.000Z",
"room_name": "Room A"
}'
JavaScript
const res = await fetch("https://api.dmchamp.com/v1/appointments", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
contact_id: "contact_abc123",
event_id: "event_xyz789",
start_time: "2026-06-15T10:00:00.000Z",
room_name: "Room A",
}),
});
const data = await res.json();
console.log(data.appointment_id);
Python
import requests
res = requests.post(
"https://api.dmchamp.com/v1/appointments",
headers={
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"contact_id": "contact_abc123",
"event_id": "event_xyz789",
"start_time": "2026-06-15T10:00:00.000Z",
"room_name": "Room A",
},
)
print(res.json()["appointment_id"])
Resposta (201 Created):
{
"success": true,
"appointment_id": "aBcD1234eFgH5678",
"appointment": {
"id": "aBcD1234eFgH5678",
"contact_id": "contact_abc123",
"event_id": "event_xyz789",
"status": "Confirmed",
"start_time": "2026-06-15T10:00:00.000Z",
"end_time": "2026-06-15T10:30:00.000Z",
"created_at": "2026-06-10T09:00:00.000Z",
"last_modified_at": "2026-06-10T09:00:00.000Z",
"room_name": "Room A",
"google_calendar_event_id": null,
"calendar_synced": false
}
}
Obter um agendamento
GET /appointments/{appointmentId}
Retorna um único agendamento pelo seu ID, incluindo seu estado de sincronização de calendário.
cURL
curl "https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678" \
-H "X-API-Key: YOUR_API_KEY"
JavaScript
const res = await fetch(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointment);
Python
import requests
res = requests.get(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["appointment"])
Resposta (200 OK):
{
"success": true,
"appointment": {
"id": "aBcD1234eFgH5678",
"contact_id": "contact_abc123",
"event_id": "event_xyz789",
"status": "Confirmed",
"start_time": "2026-06-15T10:00:00.000Z",
"end_time": "2026-06-15T10:30:00.000Z",
"room_name": "Room A",
"google_calendar_event_id": "abc123googleevent",
"calendar_synced": true
}
}
Listar agendamentos
GET /appointments
Lista os agendamentos da sua conta, do mais recente para o mais antigo, com paginação baseada em cursor.
| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
contact_id |
Não | Retorna apenas agendamentos para este contato. Listagens filtradas por contato incluem apenas agendamentos confirmados. |
date |
Não | Retorna apenas agendamentos neste dia do calendário (YYYY-MM-DD). Requer contact_id. |
status |
Não | Filtra por Confirmed ou Canceled. Disponível apenas sem contact_id. |
limit |
Não | Tamanho da página, um número inteiro entre 1 e 100. O padrão é 50. |
cursor |
Não | O valor next_cursor de uma resposta anterior. |
Algumas regras para ter em mente:
- Sem filtros, você obtém todos os agendamentos da conta, página por página.
- Por contato — defina
contact_idpara ver os agendamentos confirmados de um contato. Você pode restringir isso a um único dia passando tambémdate. - Por status — defina
status(semcontact_id) para listar apenas agendamentosConfirmedou apenasCanceledem toda a conta. - O filtro
datesemcontact_id, oustatus=Canceledjunto comcontact_id, retorna um400.
cURL
curl "https://api.dmchamp.com/v1/appointments?contact_id=contact_abc123&date=2026-06-15" \
-H "X-API-Key: YOUR_API_KEY"
JavaScript
const params = new URLSearchParams({
contact_id: "contact_abc123",
date: "2026-06-15",
});
const res = await fetch(
`https://api.dmchamp.com/v1/appointments?${params}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointments, data.next_cursor);
Python
import requests
res = requests.get(
"https://api.dmchamp.com/v1/appointments",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"contact_id": "contact_abc123", "date": "2026-06-15"},
)
data = res.json()
print(data["appointments"], data["next_cursor"])
Resposta (200 OK):
{
"success": true,
"appointments": [
{
"id": "aBcD1234eFgH5678",
"contact_id": "contact_abc123",
"event_id": "event_xyz789",
"status": "Confirmed",
"start_time": "2026-06-15T10:00:00.000Z",
"end_time": "2026-06-15T10:30:00.000Z",
"calendar_synced": true
}
],
"next_cursor": null
}
Para navegar pelos resultados, passe o next_cursor de uma resposta como o cursor da próxima solicitação. Continue até que next_cursor seja null. Consulte Erros e Paginação para o padrão de paginação compartilhado.
Atualizar um agendamento
PUT /appointments/{appointmentId}
Remarque um agendamento ou altere seus detalhes. Envie apenas os campos que deseja alterar — pelo menos um é obrigatório. O início e o fim combinados devem permanecer em ordem cronológica (end_time deve ser posterior a start_time). As alterações são sincronizadas automaticamente com o evento de calendário vinculado.
| Campo | Descrição |
|---|---|
start_time |
Nova data e hora de início, no formato ISO 8601. |
end_time |
Nova data e hora de término, no formato ISO 8601. Deve ser posterior ao horário de início. |
room_name |
Novo nome da sala ou recurso. |
description |
Nova descrição, ou null para limpá-la. |
summary |
Novo resumo, ou null para limpá-lo. |
cURL
curl -X PUT "https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"start_time": "2026-06-16T10:00:00.000Z",
"end_time": "2026-06-16T10:30:00.000Z"
}'
JavaScript
const res = await fetch(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
{
method: "PUT",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
start_time: "2026-06-16T10:00:00.000Z",
end_time: "2026-06-16T10:30:00.000Z",
}),
}
);
const data = await res.json();
console.log(data.appointment);
Python
import requests
res = requests.put(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
headers={
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"start_time": "2026-06-16T10:00:00.000Z",
"end_time": "2026-06-16T10:30:00.000Z",
},
)
print(res.json()["appointment"])
Resposta (200 OK):
{
"success": true,
"appointment_id": "aBcD1234eFgH5678",
"appointment": {
"id": "aBcD1234eFgH5678",
"contact_id": "contact_abc123",
"event_id": "event_xyz789",
"status": "Confirmed",
"start_time": "2026-06-16T10:00:00.000Z",
"end_time": "2026-06-16T10:30:00.000Z",
"calendar_synced": true
}
}
Cancelar um agendamento
POST /appointments/{appointmentId}/cancel
Cancela um agendamento confirmado, registrando opcionalmente um motivo. O agendamento permanece em sua conta com o status Canceled, e o evento de calendário vinculado é removido automaticamente em segundo plano. Cancelar um agendamento já cancelado retorna um 400.
| Campo | Obrigatório | Descrição |
|---|---|---|
cancellation_reason |
Não | Motivo do cancelamento, armazenado no agendamento. |
cURL
curl -X POST "https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678/cancel" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"cancellation_reason": "Client asked to reschedule next month"
}'
JavaScript
const res = await fetch(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678/cancel",
{
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
cancellation_reason: "Client asked to reschedule next month",
}),
}
);
const data = await res.json();
console.log(data.success);
Python
import requests
res = requests.post(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678/cancel",
headers={
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={"cancellation_reason": "Client asked to reschedule next month"},
)
print(res.json()["success"])
Resposta (200 OK):
{
"success": true,
"appointment_id": "aBcD1234eFgH5678"
}
Excluir um agendamento
DELETE /appointments/{appointmentId}
Exclui permanentemente um agendamento e suas referências. Se você deseja apenas cancelar a reserva mantendo o registro, use cancelar em vez disso.
cURL
curl -X DELETE "https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678" \
-H "X-API-Key: YOUR_API_KEY"
JavaScript
const res = await fetch(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
{ method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.success);
Python
import requests
res = requests.delete(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
Resposta (200 OK):
{
"success": true
}
Listar seus Google Calendars conectados
GET /appointments/google-calendars
Retorna os Google Calendars disponíveis nesta conta, diretamente do Google — útil para mostrar ao titular da conta um seletor de qual calendário importar abaixo, ou apenas para confirmar que a conexão está ativa.
Isso só funciona depois que a conta tiver conectado o Google Calendar (Configurações → Integrações) com pelo menos acesso de leitura. Se não tiver, ou se o acesso concedido não incluir mais o escopo de leitura de calendário, você receberá um 400 solicitando que você o conecte (ou reconecte).
cURL
curl "https://api.dmchamp.com/v1/appointments/google-calendars" \
-H "X-API-Key: YOUR_API_KEY"
JavaScript
const res = await fetch("https://api.dmchamp.com/v1/appointments/google-calendars", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.data);
Python
import requests
res = requests.get(
"https://api.dmchamp.com/v1/appointments/google-calendars",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["data"])
Resposta (200 OK):
{
"success": true,
"data": [
{
"id": "primary",
"summary": "jane@example.com",
"timeZone": "America/New_York",
"accessRole": "owner",
"primary": true
},
{
"id": "abcdefg1234567890@group.calendar.google.com",
"summary": "Bookings",
"timeZone": "America/New_York",
"accessRole": "writer"
}
]
}
Cada entrada segue o formato CalendarListEntry do próprio Google, portanto, os nomes dos campos seguem a camelCase do Google, não o snake_case usual desta API — esses são dados do Google passados como estão, não os nossos. Uma conexão ausente ou revogada retorna 400 com um erro explicando que o Google Calendar precisa ser conectado (ou reconectado).
Importar eventos de um Google Calendar
POST /appointments/import-calendar-events
Puxa os eventos que já estão no(s) Google Calendar(s) conectado(s) de uma campanha ou Agente de IA e os transforma em agendamentos — útil na primeira vez que você conecta um calendário que já possui reservas. Isso pode levar algum tempo (cada evento passa por uma extração para descobrir para quem é), por isso nunca é executado em linha: a solicitação enfileira um trabalho em segundo plano e retorna um job_id para consulta.
| Campo | Obrigatório | Descrição |
|---|---|---|
campaign_id |
Um destes dois | A campanha cujo(s) calendário(s) conectado(s) será(ão) usado(s) para importação. |
agent_id |
Um destes dois | O Agente de IA cujo(s) calendário(s) conectado(s) será(ão) usado(s) para importação. |
identifier |
Sim | "EMAIL" ou "PHONE_NUMBER" — qual informação de contato extrair de cada evento de calendário para corresponder ou criar o contato ao qual ele pertence. |
Envie exatamente um entre campaign_id / agent_id, nunca ambos e nunca nenhum — qualquer combinação retorna um 400. O que você enviar deve pertencer à sua conta, caso contrário, você receberá um 404.
cURL
curl -X POST "https://api.dmchamp.com/v1/appointments/import-calendar-events?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent_abc123",
"identifier": "EMAIL"
}'
JavaScript
const res = await fetch("https://api.dmchamp.com/v1/appointments/import-calendar-events", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
agent_id: "agent_abc123",
identifier: "EMAIL",
}),
});
const data = await res.json();
console.log(data.job_id);
Python
import requests
res = requests.post(
"https://api.dmchamp.com/v1/appointments/import-calendar-events",
headers={
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={"agent_id": "agent_abc123", "identifier": "EMAIL"},
)
print(res.json()["job_id"])
Resposta (202 Accepted):
{
"success": true,
"job_id": "jK9mQ2xR7pL4wN1t",
"status": "queued",
"campaign_id": null,
"agent_id": "agent_abc123"
}
campaign_id e agent_id retornam o que você enviou; o outro é sempre null.
Consultar o trabalho de importação
GET /appointments/import-calendar-events/{jobId}
curl "https://api.dmchamp.com/v1/appointments/import-calendar-events/jK9mQ2xR7pL4wN1t" \
-H "X-API-Key: YOUR_API_KEY"
Resposta (200 OK):
{
"success": true,
"job_id": "jK9mQ2xR7pL4wN1t",
"status": "completed",
"message": "Imported 12 events as appointments.",
"error": null
}
status |
Significado |
|---|---|
queued |
Ainda não iniciado. Continue consultando. |
processing |
A importação está em execução. Continue consultando. |
completed |
Concluído — message contém um breve resumo legível. |
failed |
Algo deu errado — error contém o motivo. |
GET em um jobId que não existe (ou pertence a uma conta diferente) retorna 404.
Integrações de agendamento externo (Zenchef / Formitable / OpenTable / TheFork / Trafft)
Zenchef e Formitable são sistemas de reserva de restaurantes nos quais seu Agente de IA pode reservar mesas reais; o Trafft é uma plataforma de agendamento para empresas que trabalham com horários, conectada uma vez por conta, em vez de por restaurante. As duas plataformas de restaurante possuem um widget de reserva público e não autenticado (https://api.dmchamp.com/v1/zenchef-widget/... e https://api.dmchamp.com/v1/formitable-widget/...) que é renderizado dentro do chat para o cliente — essas rotas de widget são páginas HTML simples destinadas a serem abertas em um navegador, não endpoints de API JSON, portanto, não estão documentadas aqui. O que segue são os endpoints de gerenciamento de conta: verificar se um ID de restaurante pertence ao titular da conta e, em seguida, adicioná-lo, atualizá-lo ou removê-lo.
Zenchef
Conectar um restaurante Zenchef é uma verificação de duas etapas, para que o titular da conta prove que realmente administra o restaurante antes que ele seja conectado ao bot: primeiro, verifique se o ID existe (sem revelar o nome), depois peça que eles mesmos digitem o nome do restaurante e verifique se corresponde.
Etapa 1 — Verificar se um ID de restaurante existe
POST /appointments/zenchef-restaurants/check
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_id |
Sim | O ID do restaurante Zenchef a ser verificado. |
curl -X POST "https://api.dmchamp.com/v1/appointments/zenchef-restaurants/check?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "12345" }'
Resposta (200 OK):
{
"success": true,
"data": { "exists": true, "requiresNameVerification": true }
}
exists: false significa que nenhum restaurante Zenchef possui esse ID — nada mais a fazer. Limitado a 10 verificações a cada 5 minutos por conta; exceder esse limite retorna 429.
Etapa 2 — Verificar o nome do restaurante
POST /appointments/zenchef-restaurants/verify-name
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_id |
Sim | O ID do restaurante Zenchef da etapa 1. |
user_input_name |
Sim | O nome que o titular da conta digitou — comparado com o nome real do restaurante no Zenchef (insensível a maiúsculas/minúsculas e espaços em branco). |
curl -X POST "https://api.dmchamp.com/v1/appointments/zenchef-restaurants/verify-name?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "12345", "user_input_name": "The Blue Door Bistro" }'
Resposta (200 OK):
{
"success": true,
"data": {
"verified": true,
"restaurantDetails": {
"id": "12345",
"name": "The Blue Door Bistro",
"address": "1 Rue de Rivoli, Paris",
"status": "active"
}
}
}
verified: false significa que o nome não correspondeu — restaurantDetails é omitido, peça ao titular da conta que tente novamente. Limitado a 3 tentativas a cada 5 minutos (mais rigoroso que a verificação de existência, já que esta é a etapa de prova real). Um restaurant_id que não é mais resolvido no Zenchef retorna 404.
Etapa 3 — Salvar o restaurante
POST /appointments/zenchef-restaurants
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_id |
Sim | 1–64 caracteres, letras/números/sublinhado/hífen. |
restaurant_name |
Sim | O nome do restaurante verificado da etapa 2. |
curl -X POST "https://api.dmchamp.com/v1/appointments/zenchef-restaurants?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "12345", "restaurant_name": "The Blue Door Bistro" }'
Resposta (201 Created):
{ "success": true, "data": { "restaurantId": "12345" } }
Atualizar um restaurante Zenchef salvo
PUT /appointments/zenchef-restaurants/{restaurantId}
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_name |
Não | Novo nome de exibição. |
is_active |
Não | Defina false para impedir que o bot faça reservas neste restaurante sem removê-lo. |
curl -X PUT "https://api.dmchamp.com/v1/appointments/zenchef-restaurants/12345" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'
Resposta (200 OK): mesmo formato da resposta de salvamento acima.
Remover um restaurante Zenchef
DELETE /appointments/zenchef-restaurants/{restaurantId}
curl -X DELETE "https://api.dmchamp.com/v1/appointments/zenchef-restaurants/12345" \
-H "X-API-Key: YOUR_API_KEY"
Resposta (200 OK): { "success": true, "data": { "restaurantId": "12345" } }
Um restaurantId que não está atualmente na conta retorna 404 ao atualizar ou excluir.
Formitable
O Formitable não precisa da prova de nome em duas etapas que o Zenchef exige — seus IDs de restaurante já são delimitados por empresa, portanto, uma chamada de verificação é suficiente. Ele também possui uma consulta de detalhes usada para armazenar em cache a URL do site do restaurante durante a configuração.
Verificar um ID de restaurante
POST /appointments/formitable-restaurants/verify
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_id |
Sim | O ID do restaurante Formitable. |
language |
Não | Tag de idioma para a solicitação de teste. O padrão é "nl". |
curl -X POST "https://api.dmchamp.com/v1/appointments/formitable-restaurants/verify?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "the-blue-door", "language": "en" }'
Resposta (200 OK):
{
"success": true,
"data": {
"verified": true,
"restaurantDetails": {
"restaurantId": "the-blue-door",
"productCount": 4,
"sampleProductTitle": "Dinner for two",
"language": "en"
}
}
}
Um restaurant_id que o Formitable não reconhece retorna 404. Limitado a 10 tentativas a cada 5 minutos por conta.
Obter detalhes do restaurante
GET /appointments/formitable-restaurants/{restaurantId}/details?language=en
Busca o perfil público do restaurante no Formitable, incluindo seu site — usado para armazenar em cache a URL do site durante a configuração do restaurante. language é um parâmetro de consulta opcional, com padrão para "en".
curl "https://api.dmchamp.com/v1/appointments/formitable-restaurants/the-blue-door/details?language=en" \
-H "X-API-Key: YOUR_API_KEY"
Resposta (200 OK):
{
"success": true,
"data": {
"uid": "the-blue-door",
"name": "The Blue Door Bistro",
"website": "https://thebluedoorbistro.com",
"email": "info@thebluedoorbistro.com",
"telephone": "+31201234567",
"streetAddress": "Prinsengracht 1",
"zipcode": "1015 AB",
"city": "Amsterdam",
"country": "Netherlands",
"countryCode": "NL",
"currency": "EUR"
}
}
Salvar o restaurante
POST /appointments/formitable-restaurants
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_id |
Sim | 1–64 caracteres, letras/números/sublinhado/hífen. |
restaurant_name |
Sim | Nome de exibição. |
language |
Sim | Tag de idioma ISO, ex: "en" ou "en-GB". |
website_url |
Não | O site do restaurante, a partir da consulta de detalhes acima. Deve ser http(s)://. |
curl -X POST "https://api.dmchamp.com/v1/appointments/formitable-restaurants?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"restaurant_id": "the-blue-door",
"restaurant_name": "The Blue Door Bistro",
"language": "en",
"website_url": "https://thebluedoorbistro.com"
}'
Resposta (201 Created): { "success": true, "data": { "restaurantId": "the-blue-door" } }
Atualizar um restaurante Formitable salvo
PUT /appointments/formitable-restaurants/{restaurantId}
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_name |
Não | Novo nome de exibição. |
language |
Não | Nova tag de idioma ISO. |
is_active |
Não | Defina false para impedir que o bot faça reservas neste restaurante sem removê-lo. |
website_url |
Não | Nova URL do site. |
curl -X PUT "https://api.dmchamp.com/v1/appointments/formitable-restaurants/the-blue-door" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'
Resposta (200 OK): mesmo formato da resposta de salvamento acima.
Remover um restaurante Formitable
DELETE /appointments/formitable-restaurants/{restaurantId}
curl -X DELETE "https://api.dmchamp.com/v1/appointments/formitable-restaurants/the-blue-door" \
-H "X-API-Key: YOUR_API_KEY"
Resposta (200 OK): { "success": true, "data": { "restaurantId": "the-blue-door" } }
Um restaurantId que não está atualmente na conta retorna 404 ao atualizar ou excluir.
OpenTable
Os restaurantes do OpenTable são acessados por meio das credenciais de parceiro do OpenTable da plataforma, e o OpenTable só permite que essas credenciais vejam restaurantes que conectaram a listagem da plataforma dentro do Marketplace de Integrações do OpenTable. Portanto, assim como no Formitable, uma chamada de verificação é suficiente: um ID de Restaurante acessível (o “RID” numérico) prova tanto que o restaurante existe quanto que ele conectou a integração. Até que a listagem de parceiro do OpenTable seja habilitada na plataforma, a chamada de verificação responde 503.
Verificar um restaurante OpenTable
POST /appointments/opentable-restaurants/verify
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_id |
Sim | O ID do Restaurante OpenTable (RID), um número como 1038007. |
curl -X POST "https://api.dmchamp.com/v1/appointments/opentable-restaurants/verify?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "1038007" }'
Resposta (200 OK):
{
"success": true,
"data": {
"verified": true,
"restaurantDetails": {
"restaurantId": "1038007",
"diningAreaCount": 2,
"diningAreaNames": ["Main Room", "Garden"],
"tableTypes": ["default", "outdoor", "bar"]
}
}
}
verified: false significa que nenhum restaurante OpenTable possui esse ID. Um 403 significa que o restaurante existe, mas ainda não conectou a integração da plataforma dentro do OpenTable. Limitado a 10 tentativas a cada 5 minutos por conta.
Adicionar um restaurante OpenTable
POST /appointments/opentable-restaurants
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_id |
Sim | O ID do Restaurante verificado. |
restaurant_name |
Sim | Nome de exibição (um rótulo; também como a IA chama o restaurante). |
website_url |
Não | Uma URL http(s) exibida aos convidados quando a IA os encaminha para o restaurante. |
curl -X POST "https://api.dmchamp.com/v1/appointments/opentable-restaurants?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "1038007", "restaurant_name": "The Blue Door", "website_url": "https://thebluedoor.example" }'
Resposta (201 Created): { "success": true, "data": { "restaurantId": "1038007" } }
Atualizar um restaurante OpenTable salvo
PUT /appointments/opentable-restaurants/{restaurantId}
Envie qualquer um de restaurant_name, is_active (pause com false) ou website_url (uma string vazia limpa o campo); campos omitidos permanecem inalterados.
curl -X PUT "https://api.dmchamp.com/v1/appointments/opentable-restaurants/1038007" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'
Resposta (200 OK): { "success": true, "data": { "restaurantId": "1038007" } }
Remover um restaurante OpenTable
DELETE /appointments/opentable-restaurants/{restaurantId}
curl -X DELETE "https://api.dmchamp.com/v1/appointments/opentable-restaurants/1038007" \
-H "X-API-Key: YOUR_API_KEY"
Resposta (200 OK): { "success": true, "data": { "restaurantId": "1038007" } }
Um restaurantId que não está atualmente na conta retorna 404 na atualização.
TheFork
Os restaurantes do TheFork são acessados por meio das credenciais de parceiro do TheFork da plataforma, e o TheFork permite que essas credenciais vejam apenas os restaurantes que têm o parceiro da plataforma habilitado em suas contas do TheFork. Portanto, assim como no Formitable e no OpenTable, uma chamada de verificação é suficiente: um ID de restaurante acessível prova tanto que o restaurante existe quanto que o parceiro está habilitado nele. O ID é o UUID que o TheFork fornece ao restaurante no TheFork Manager, enviado como uma string. Até que o TheFork tenha aprovado a plataforma como parceira e emitido as credenciais, a chamada de verificação responde 503 — veja TheFork para saber o que isso significa hoje.
Verificar um restaurante do TheFork
POST /appointments/thefork-restaurants/verify
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_id |
Sim | O ID do restaurante no TheFork, um UUID como 9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60. |
curl -X POST "https://api.dmchamp.com/v1/appointments/thefork-restaurants/verify?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" }'
Resposta (200 OK):
{
"success": true,
"data": {
"verified": true,
"restaurantDetails": {
"restaurantId": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60",
"partySizes": [1, 2, 3, 4, 5, 6, 7, 8]
}
}
}
partySizes são os tamanhos de grupo que o restaurante aceita online nos próximos 30 dias. Um 404 ou 403 significa que o TheFork não nos forneceu um restaurante com esse ID — ou o ID está incorreto, ou o parceiro da plataforma ainda não está habilitado nesse restaurante; um 400 significa que o ID não é um UUID. Limitado a 10 tentativas a cada 5 minutos por conta.
Adicionar um restaurante do TheFork
POST /appointments/thefork-restaurants
| Campo | Obrigatório | Descrição |
|---|---|---|
restaurant_id |
Sim | O ID do restaurante verificado (UUID). |
restaurant_name |
Sim | Nome de exibição (um rótulo; também como a IA chama o restaurante). |
website_url |
Não | Uma URL http(s) exibida aos convidados quando a IA os encaminha para o restaurante. |
curl -X POST "https://api.dmchamp.com/v1/appointments/thefork-restaurants?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60", "restaurant_name": "The Blue Door", "website_url": "https://thebluedoor.example" }'
Resposta (201 Created): { "success": true, "data": { "restaurantId": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" } }
Atualizar um restaurante do TheFork salvo
PUT /appointments/thefork-restaurants/{restaurantId}
Envie qualquer um de restaurant_name, is_active (pause com false) ou website_url (uma string vazia limpa o campo); campos omitidos permanecem inalterados.
curl -X PUT "https://api.dmchamp.com/v1/appointments/thefork-restaurants/9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'
Resposta (200 OK): { "success": true, "data": { "restaurantId": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" } }
Remover um restaurante TheFork
DELETE /appointments/thefork-restaurants/{restaurantId}
curl -X DELETE "https://api.dmchamp.com/v1/appointments/thefork-restaurants/9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" \
-H "X-API-Key: YOUR_API_KEY"
Resposta (200 OK): { "success": true, "data": { "restaurantId": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" } }
Um restaurantId que não está atualmente na conta retorna 404 ao atualizar ou excluir.
Trafft
O Trafft é conectado uma vez para toda a conta, não por local: um endereço da empresa mais as credenciais da API do painel administrativo do Trafft (Features & Integrations → API & Connectors, parte do plano Business do Trafft). A chamada de conexão verifica essas credenciais no Trafft antes de armazenar qualquer coisa, portanto, um endereço incorreto, credenciais erradas ou um plano sem acesso à API falhará aqui em vez de durante uma conversa com o cliente. O segredo do cliente (client secret) é armazenado criptografado e nunca é retornado por nenhum endpoint.
Todas as três chamadas de escrita (POST, PUT, DELETE) exigem a permissão de edição de Integrações; o status GET requer a permissão de visualização de Integrações.
Conectar Trafft
POST /appointments/trafft/connect
| Campo | Obrigatório | Descrição |
|---|---|---|
subdomain |
Sim | O endereço da empresa — a parte antes de .admin.trafft.com na URL em que você faz login, por exemplo, acme. Um endereço completo é aceito e reduzido ao mesmo valor. |
client_id |
Sim | Client ID da página API & Connectors do Trafft. |
client_secret |
Sim | Client Secret da mesma página. Armazenado criptografado, nunca retornado. |
company_name |
Não | Um rótulo para sua própria lista. |
curl -X POST "https://api.dmchamp.com/v1/appointments/trafft/connect?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subdomain": "acme",
"client_id": "YOUR_TRAFFT_CLIENT_ID",
"client_secret": "YOUR_TRAFFT_CLIENT_SECRET",
"company_name": "Acme Salon"
}'
Resposta (200 OK):
{
"success": true,
"data": {
"subdomain": "acme",
"service_count": 12,
"employee_count": 4,
"location_count": 2
}
}
As contagens retornam do Trafft durante a verificação — elas são a maneira mais rápida de confirmar se as credenciais apontam para a conta que você pretendia.
Obter o status da conexão
GET /appointments/trafft
curl "https://api.dmchamp.com/v1/appointments/trafft" \
-H "X-API-Key: YOUR_API_KEY"
Resposta (200 OK):
{
"success": true,
"data": {
"connected": true,
"subdomain": "acme",
"hostname": "acme.admin.trafft.com",
"client_id": "YOUR_TRAFFT_CLIENT_ID",
"company_name": "Acme Salon",
"is_active": true,
"service_count": 12,
"employee_count": 4,
"location_count": 2
}
}
connected: false significa que nada foi configurado ainda. O segredo do cliente nunca é incluído nesta resposta.
Atualizar a conexão
PUT /appointments/trafft
| Campo | Obrigatório | Descrição |
|---|---|---|
is_active |
Não | Defina false como pausado — a IA para de agendar no Trafft, a conexão permanece. true a retoma. |
company_name |
Não | Novo rótulo. |
curl -X PUT "https://api.dmchamp.com/v1/appointments/trafft" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'
Resposta (200 OK): o mesmo objeto de conexão que o GET acima.
Desconectar Trafft
DELETE /appointments/trafft
curl -X DELETE "https://api.dmchamp.com/v1/appointments/trafft" \
-H "X-API-Key: YOUR_API_KEY"
Resposta (200 OK): { "success": true }
A desconexão remove apenas as credenciais armazenadas. Os agendamentos já existentes no Trafft permanecem inalterados.
Formato de erro em todos os endpoints Zenchef/Formitable/OpenTable/TheFork: ao contrário do restante desta página, os erros aqui contêm o status duas vezes — uma como status HTTP e outra como
error_codeno corpo — por exemplo{ "success": false, "error": "Restaurant not found", "error_code": 404 }. Trate-o da mesma forma que qualquer outro erro: verifiquesuccess, leiaerrorpara a mensagem.
Erros da API de Agendamentos
Os endpoints de agendamento retornam o envelope de erro padrão:
{
"success": false,
"error": "Appointment not found"
}
| Status | Quando ocorre em um endpoint de agendamento |
|---|---|
400 |
Um campo obrigatório está faltando ou é inválido — por exemplo, um start_time incorreto, um end_time que não é posterior a start_time, uma combinação de filtros inválida, nenhum campo para atualizar ou um agendamento já cancelado. |
404 |
O agendamento, contato ou tipo de evento não foi encontrado. |
409 |
O intervalo de tempo solicitado já está ocupado (conflito de agendamento). |
Os códigos compartilhados que todo endpoint pode retornar — 401, 403 (seu plano não inclui acesso à API), 429 (limite de taxa) e 500 — estão listados com orientações de nova tentativa em Erros e Paginação.
Use seu próprio cliente Google OAuth (tela de consentimento do Agenda)
Quando uma conta conecta o Google Agenda, a janela de login do Google nomeia o projeto do cliente OAuth — por padrão, o da plataforma. Uma agência pode registrar seu próprio cliente Google OAuth 2.0 na conta da agência; a partir de então, a conexão de agenda para essa conta e cada subconta vinculada a ela é executada por meio desse cliente, para que a tela de consentimento mostre o nome e o logotipo da agência. Nada mais muda: o fluxo de conexão, a sincronização bidirecional e os endpoints de agendamento acima funcionam exatamente como antes.
Apenas Google Agenda. O OAuth da caixa de entrada do Gmail para o canal de E-mail não é afetado.
O que seu cliente precisa primeiro
- Um cliente OAuth 2.0 do tipo Aplicativo da Web em seu projeto do Google Cloud, com a API do Google Agenda ativada nesse projeto.
- Cada entrada
redirect_uris(retornada pelos endpoints abaixo) adicionada em URIs de redirecionamento autorizados do cliente. A primeira entrada é seu domínioapi.verificado quando você tiver um — o Google só verifica uma marca cujo redirecionamento reside em um domínio que você possui — seguido pelo host neutro da plataforma como o fallback usado até então. - A tela de consentimento com sua marca, seu domínio em Domínios autorizados e os dois escopos do Agenda declarados (
scopesna resposta). Até que o aplicativo seja publicado e verificado pelo Google, os usuários verão um aviso de aplicativo não verificado e o cliente terá um limite de 100 usuários.
Salve seu cliente
PUT /account-config/google-oauth-client
| Campo | Obrigatório | Descrição |
|---|---|---|
client_id |
Sim | O ID do cliente OAuth 2.0, terminando em .apps.googleusercontent.com. |
client_secret |
Sim | O segredo do cliente. Verificado no Google antes de ser armazenado e, em seguida, criptografado. Nunca retornado por nenhum endpoint. |
curl -X PUT "https://api.dmchamp.com/v1/account-config/google-oauth-client?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_id": "123456789012-abcdefghijklmnop.apps.googleusercontent.com",
"client_secret": "GOCSPX-your-client-secret"
}'
Resposta
{
"success": true,
"configured": true,
"client_id": "123456789012-abcdefghijklmnop.apps.googleusercontent.com",
"redirect_uris": [
"https://api.youragency.com/v1/auth-google-callback",
"https://api.dmchamp.com/v1/auth-google-callback"
],
"scopes": [
"https://www.googleapis.com/auth/calendar.events",
"https://www.googleapis.com/auth/calendar.readonly"
],
"setup": ["…"]
}
Um segredo incorreto ou um ID de cliente desconhecido é recusado com 400 e o motivo do próprio Google em error, e nada é armazenado.
Leia ou remova-o
GET /account-config/google-oauth-client retorna o mesmo resumo a qualquer momento — configured: false mais o redirect_uris e scopes antes que qualquer coisa seja salva, para que você possa configurar o lado do Google primeiro. DELETE /account-config/google-oauth-client remove o cliente: novas conexões revertem para o cliente da plataforma, e calendários que foram conectados através do cliente removido devem ser reconectados, porque apenas o cliente que emitiu uma conexão pode atualizá-la.
Os membros da equipe precisam de Integrations: view para GET e Integrations: edit para PUT / DELETE.
Próximos passos
- Contatos — crie e consulte os contatos para os quais você faz reservas.
- Mensagens e Conversas — envie uma confirmação ou lembrete a um contato.
- Webhooks — receba notificações quando agendamentos forem alterados.