Agendamentos
Liste, consulte, crie e atualize agendamentos pela API pública v1 da OctoSolve.
Agendamentos
O recurso /appointments permite ler, criar e atualizar agendamentos da própria conta. Não existe DELETE na v1. Para cancelar, envie PATCH com status: "cancelado".
Campos
| Campo | Tipo | Leitura | Escrita | Regras |
|---|---|---|---|---|
id | uuid | Sim | Não | Identificador do agendamento |
contact_id | uuid | Sim | Apenas na criação | Obrigatório e precisa identificar um contato da mesma conta |
servico_id | uuid ou null | Sim | Apenas na criação | Obrigatório ao criar. Não pode ser trocado pela API |
profissional_id | uuid ou null | Sim | Criação e atualização | Precisa estar ativo, com agenda ativa e atender ao serviço. No PATCH, null explícito é recusado |
data_hora | datetime | Sim | Criação e atualização | Na requisição, aceita ISO 8601 com fuso, sem fuso ou em UTC. Sem fuso, a API assume BRT, UTC-3. Na resposta, vem em UTC e termina em Z |
duracao_minutos | inteiro ou null | Sim | Criação e atualização | De 5 a 600. Na criação, o padrão é a duração do serviço. No PATCH, null explícito é recusado |
status | enum ou null | Sim | Apenas na atualização | Segue a máquina de estados desta página |
valor | número ou null | Sim | Criação e atualização | Na criação, o padrão é o preço do serviço |
pago | booleano ou null | Sim | Não | Alterado pelo fluxo de pagamento |
forma_pagamento | string ou null | Sim | Não | Forma registrada pelo fluxo de pagamento |
motivo_cancelamento | string ou null | Sim | Apenas na atualização | Máximo de 500 caracteres e somente junto de status: "cancelado" |
notas | string ou null | Sim | Criação e atualização | Máximo de 2.000 caracteres |
meet_link | string ou null | Sim | Não | Link de reunião, quando existir |
contact_package_id | uuid ou null | Sim | Não | Pacote usado pelo agendamento |
recurrence_id | uuid ou null | Sim | Não | Quando preenchido, indica uma ocorrência de série |
agendado_por | enum ou null | Sim | Não | agent ou human. A API cria como human |
created_at | datetime ou null | Sim | Não | Data de criação |
notificar_whatsapp | booleano | Não | Apenas na criação | Padrão false. Com true, envia a confirmação e consome a cota de mensagens |
Como toda data de resposta da API, data_hora vem em UTC e termina em Z. Ao exibir para o cliente final, converta o valor para o fuso local. Sem essa conversão, no horário de Brasília a hora aparece 3 horas adiantada.
Nos exemplos de requisição desta página, o valor com -03:00 é mantido para mostrar uma entrada no horário de Brasília.
Agendamento de serviço online criado pela API nasce sem link de reunião. O campo meet_link vem null. Hoje somente o agente gera o link, portanto sua integração não deve prometer um link de Google Meet ao criar pela API.
Endpoints
| Método | Caminho | Escopo | Resultado |
|---|---|---|---|
GET | /appointments | read ou write | Lista paginada em ordem cronológica |
GET | /appointments/{id} | read ou write | Um agendamento |
POST | /appointments | write | Cria e responde 201 |
PATCH | /appointments/{id} | write | Atualiza os campos enviados |
Listar agendamentos
Filtros disponíveis:
| Filtro | Regra |
|---|---|
contact_id | Agendamentos de um contato |
profissional_id | Agendamentos de um profissional |
servico_id | Agendamentos de um serviço |
status | Repetível, como status=agendado&status=confirmado |
de | Data ou datetime ISO 8601, inclusive |
ate | Data ou datetime ISO 8601, exclusivo |
limit | De 1 a 100, padrão 50 |
cursor | Valor opaco retornado pela página anterior |
Sem de e ate, a consulta começa 30 dias antes do momento atual e segue para o futuro. Isso evita percorrer anos de histórico por engano. A ordem é data_hora crescente, com id como desempate. O cursor depende dos filtros e responde 400 invalid_cursor se for reutilizado com outra consulta. A janela resolvida na primeira página fica congelada no cursor durante toda a paginação, então é seguro continuar somente com o cursor, mesmo sem informar de. O parâmetro de continua opcional. Se de for posterior a ate, a resposta é 422 validation_error.
Consultar um agendamento
Um UUID inexistente ou pertencente a outra conta responde o mesmo 404 not_found.
Criar um agendamento
contact_id, servico_id e data_hora são obrigatórios. profissional_id, duracao_minutos, valor, notas e notificar_whatsapp são opcionais.
Data no passado é aceita. Isso permite importar histórico de outro sistema. A proteção contra horários sobrepostos continua valendo.
A criação recusa com 422 validation_error:
- Serviço inativo.
- Serviço que exige pagamento antecipado. Esse fluxo precisa continuar no agente ou no painel.
- Contato, serviço ou profissional de outra conta ou inexistente.
- Profissional inativo, sem agenda ativa ou que não atende ao serviço.
GET /availability ajuda a escolher um horário, mas não reserva nada. A confirmação acontece somente no POST /appointments. Se outra pessoa ocupar o intervalo antes da criação, a resposta será 409 slot_conflict.
Use Idempotency-Key para repetir com segurança o mesmo pedido após timeout. Uma tentativa que falha libera a chave. Se o horário estiver ocupado, escolha outro horário e repita o pedido com a mesma chave ou com outra chave estável para a nova operação.
Se o Google Calendar falhar durante a criação, o agendamento ainda é criado na agenda da OctoSolve. A API não expõe o estado de sincronização nesta versão.
Atualizar um agendamento
No PATCH, campo ausente não muda. null explícito limpa valor, motivo_cancelamento ou notas, mas profissional_id: null e duracao_minutos: null respondem 422 validation_error.
Ocorrência com recurrence_id preenchido pode ser consultada, mas não atualizada pela API. O PATCH responde 409 conflict e a série deve ser editada pelo painel.
Trocar servico_id também não faz parte do PATCH. Trocar o serviço altera preço, duração, agente e título no Google, portanto o caminho seguro é cancelar e criar outro agendamento.
Remarcar por data_hora só é permitido quando o estado final da chamada é agendado ou confirmado. Ao enviar status e data_hora juntos, a API valida primeiro a transição de status e depois a remarcação. Atualizações concorrentes seguem a última gravação, sem bloqueio otimista e sem updated_at. Use webhooks para acompanhar mudanças.
Máquina de estados
A API é mais rígida que a tela, de propósito. A tela é operada por pessoas e continua permitindo correções manuais. A API bloqueia regressões que poderiam fazer a agenda mentir.
| Estado atual | Próximos estados permitidos pela API |
|---|---|
agendado | confirmado, realizado, no_show, cancelado |
confirmado | realizado, no_show, cancelado |
realizado | Nenhum, estado final |
no_show | Nenhum, estado final |
cancelado | Nenhum, estado final |
aguardando_pagamento | Nenhum pela API, o fluxo de cobrança controla a transição |
cancelado_por_nao_pagamento | Nenhum, estado final |
cancelado_estornado | Nenhum, estado final |
cancelado_lgpd | Nenhum, estado final |
Enviar o mesmo status atual é uma operação sem mudança e responde 200. Transição fora da tabela responde 409 invalid_transition, com status_atual em error.details[0].
Erros próprios
| Situação | HTTP | Código | Ação recomendada |
|---|---|---|---|
| Horário ocupado | 409 | slot_conflict | Consulte a disponibilidade novamente e peça outro horário |
| Transição de status não permitida | 409 | invalid_transition | Leia details[0].status_atual e aplique a máquina de estados |
Receita para Make ou n8n
- Liste serviços ativos em
GET /servicese escolha um que não exija pagamento antecipado. - Liste profissionais com
GET /professionals?servico_id=ID_DO_SERVICO. - Consulte
GET /availabilitycom serviço, data e, se necessário, profissional. - Guarde o
iniciodo slot exatamente como veio, inclusive oZ. - Envie
POST /appointmentscom chavewrite,contact_id,servico_id,profissional_ide o slot emdata_hora. - Use uma
Idempotency-Keyestável para a mesma tentativa de origem. - Se receber
slot_conflict, consulte a disponibilidade outra vez e apresente outro horário. Não repita o mesmo horário em loop. - Para remarcar ou mudar status, envie apenas os campos necessários em
PATCH /appointments/{id}. - Em
invalid_transition, usedetails[0].status_atualpara decidir o próximo passo ou encaminhar para revisão humana.