Atendimentos

API completa de agendamento integrada ao chat. Gerencie profissionais, servicos, lembretes automaticos e agenda multi-canal.

Feature Flag

Este modulo requer a feature appointments_module habilitada na conta. Disponivel em todos os planos pagos.

Base URL

Todos os endpoints usam o prefixo /api/v1/accounts/{account_id}

Visao Geral

O modulo de Atendimentos expoe a mesma logica de negocio em 4 canais de consumo:

  • Dashboard NooviChat — operadores criam e gerenciam atendimentos pelo chat
  • n8n workflows — automacao server-to-server via api_access_token
  • Widget publico — clientes agendam online via iframe (requer appointments_public_widget)
  • Captain AI — bot 24/7 agenda conversacionalmente (requer appointments_captain_booking)

Ao criar um atendimento, lembretes automaticos sao materializados com base nos templates configurados no servico. Os lembretes sao enviados via WhatsApp no tempo certo.

GET/api/v1/accounts/{account_id}/appointments

Retorna lista paginada de atendimentos com filtros opcionais por data, profissional, status e contato.

Parametros de Query

NomeTipoObrigatorioDescricao
from(query)string (date-time)NaoInicio do intervalo (ISO 8601 UTC). Ex: 2026-06-01T00:00:00Z
to(query)string (date-time)NaoFim do intervalo (ISO 8601 UTC)
professional_id(query)integerNaoFiltrar por profissional
status(query)stringNaoscheduled | confirmed | completed | cancelled | no_show. Aceita multiplos separados por virgula (ex: scheduled,confirmed).
contact_id(query)integerNaoFiltrar por contato
pipeline_card_id(query)integerNaoFiltrar atendimentos vinculados a um card especifico do Pipeline Pro (Feature C)
page(query)integerNaoPagina (padrao 1) — 50 registros por pagina (fixo, BACK-011)
curl -s "https://chat.seudominio.com/api/v1/accounts/1/appointments?from=2026-06-01T00:00:00Z&to=2026-06-30T23:59:59Z&status=scheduled" \
  -H "api_access_token: YOUR_TOKEN" | jq .
200Lista de atendimentos (50 por pagina, ordenada por scheduled_at). A resposta contem apenas a chave data — nao ha objeto meta de paginacao.
json
{
  "data": [
    {
      "id": 42,
      "public_id": "550e8400-e29b-41d4-a716-446655440000",
      "contact_id": 101,
      "contact": { "id": 101, "name": "Maria Silva" },
      "professional_id": 3,
      "professional": { "id": 3, "name": "Dr. Santos" },
      "service_id": 7,
      "service": { "id": 7, "name": "Limpeza Dental", "duration_minutes": 60 },
      "scheduled_at": "2026-06-15T10:00:00Z",
      "ends_at": "2026-06-15T11:00:00Z",
      "status": "scheduled",
      "price_cents": 20000,
      "currency": "BRL",
      "created_at": "2026-06-01T09:30:00Z"
    }
  ]
}
POST/api/v1/accounts/{account_id}/appointments

Cria novo atendimento e materializa lembretes automaticos a partir dos templates do servico.

Body (appointment)

NomeTipoObrigatorioDescricao
contact_idintegerSimID do contato (paciente/cliente)
professional_idintegerSimID do profissional
service_idintegerSimID do servico
scheduled_atstring (date-time)SimData/hora de inicio (ISO 8601 UTC)
partner_idintegerNaoID do convenio/plano de saude
ends_atstring (date-time)NaoData/hora de fim. Se omitido, calculado automaticamente pela duracao do servico.
notesstringNaoObservacoes do atendimento
conversation_display_idintegerNaoVincular a conversa existente (display_id da conta, per-account)
pipeline_card_idintegerNaoVincular a um card existente do Pipeline Pro (resolvido dentro da conta)
custom_attributesobjectNaoAtributos personalizados (JSONB)
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/appointments" \
  -H "api_access_token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "appointment": {
      "contact_id": 101,
      "professional_id": 3,
      "service_id": 7,
      "scheduled_at": "2026-06-15T10:00:00Z",
      "notes": "Primeira consulta"
    }
  }'
201Atendimento criado. Lembretes sao materializados em background a partir dos templates do servico.
json
{
  "data": {
    "id": 42,
    "public_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "scheduled",
    "scheduled_at": "2026-06-15T10:00:00Z",
    "ends_at": "2026-06-15T11:00:00Z",
    "price_cents": 20000,
    "currency": "BRL",
    "contact": { "id": 101, "name": "Maria Silva" },
    "professional": { "id": 3, "name": "Dr. Santos" },
    "service": { "id": 7, "name": "Limpeza Dental", "duration_minutes": 60 },
    "partner": null
  }
}

Lembretes nao vem no corpo da resposta

A resposta de criacao/leitura inclui apenas contact, professional, service e partner. Os lembretes sao materializados de forma assincrona a partir dos templates do servico — nao sao retornados no corpo do POST.

422Conflito de horario, erro de validacao ou data no passado (BACK-003)
json
// Data no passado (BACK-003):
{
  "errors": {
    "scheduled_at": ["must be in the future"]
  }
}

// Conflito de horario:
{
  "errors": {
    "slot": ["conflito de horario"]
  }
}

// Conflito com atendimento existente:
{
  "errors": {
    "scheduled_at": ["conflicts with an existing appointment for this professional"]
  }
}
GET/api/v1/accounts/{account_id}/appointments/{appointment_id}

Retorna detalhes de um atendimento especifico com contact, professional, service e partner embutidos.

Parametros de Path

NomeTipoObrigatorioDescricao
account_id(path)integerSimID da conta
appointment_id(path)integerSimID do atendimento
bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/appointments/42" \
  -H "api_access_token: YOUR_TOKEN" | jq .
200Detalhes do atendimento
json
{
  "data": {
    "id": 42,
    "public_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "confirmed",
    "contact": { "id": 101, "name": "Maria Silva", "email": "maria@email.com", "phone_number": "+5511999990000" },
    "professional": { "id": 3, "name": "Dr. Santos", "specialty": "Odontologia", "color": "#3B82F6" },
    "service": { "id": 7, "name": "Limpeza Dental", "duration_minutes": 60, "default_price_cents": 20000, "currency": "BRL" },
    "partner": null,
    "scheduled_at": "2026-06-15T10:00:00Z",
    "ends_at": "2026-06-15T11:00:00Z",
    "price_cents": 20000,
    "currency": "BRL"
  }
}
PATCH/api/v1/accounts/{account_id}/appointments/{appointment_id}

Atualiza campos do atendimento. Para mudar status use os endpoints de acao (confirm, complete, no_show).

Campos atualizaveis sao limitados

O PATCH so permite scheduled_at, ends_at, notes, partner_id e custom_attributes. Nao e possivel trocar professional_id, service_id ou contact_id via PATCH — esses campos sao definidos apenas na criacao. Para mudar status use os endpoints de acao (/confirm, /complete, /no_show).

Body (appointment)

NomeTipoObrigatorioDescricao
scheduled_atstring (date-time)NaoReagendar inicio. Conflito de slot retorna 422.
ends_atstring (date-time)NaoAjustar horario de fim
partner_idintegerNaoTrocar convenio
notesstringNaoAtualizar observacoes
custom_attributesobjectNaoAtributos personalizados (JSONB)
bash
curl -X PATCH "https://chat.seudominio.com/api/v1/accounts/1/appointments/42" \
  -H "api_access_token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "appointment": { "scheduled_at": "2026-06-16T14:00:00Z", "notes": "Reagendado a pedido da paciente" } }'
DELETE/api/v1/accounts/{account_id}/appointments/{appointment_id}

Cancela o atendimento (status muda para cancelled via servico de cancelamento). O registro e preservado para historico — nao e um hard delete.

Escopo de conta

O appointment_id e resolvido dentro da conta autenticada (Current.account.appointments.find). Nao e um ID global — tentar cancelar um atendimento de outra conta retorna 404.

Body (opcional)

NomeTipoObrigatorioDescricao
reasonstringNaoMotivo do cancelamento
bash
curl -X DELETE "https://chat.seudominio.com/api/v1/accounts/1/appointments/42" \
  -H "api_access_token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Paciente solicitou via WhatsApp" }'
POST/api/v1/accounts/{account_id}/appointments/{appointment_id}/confirm

Confirma o atendimento (scheduled → confirmed). Dispara webhook appointment.confirmed.

bash
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/appointments/42/confirm" \
  -H "api_access_token: YOUR_TOKEN"
200Atendimento confirmado
json
{ "data": { "id": 42, "status": "confirmed" } }
POST/api/v1/accounts/{account_id}/appointments/{appointment_id}/complete

Marca o atendimento como concluido (status -> completed). Nao aceita body — transicoes invalidas retornam 422.

bash
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/appointments/42/complete" \
  -H "api_access_token: YOUR_TOKEN"
POST/api/v1/accounts/{account_id}/appointments/{appointment_id}/no_show

Registra no-show (paciente nao compareceu). Dispara webhook appointment.no_show.

bash
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/appointments/42/no_show" \
  -H "api_access_token: YOUR_TOKEN"
GET/api/v1/accounts/{account_id}/appointments/availability

Retorna slots disponiveis para um profissional em uma data, considerando horario de trabalho, buffer e consultas existentes.

Parametros de Query

NomeTipoObrigatorioDescricao
professional_id(query)integerSimID do profissional. Obrigatorio — sem ele retorna 404.
date(query)string (date)SimData a verificar (YYYY-MM-DD)
service_id(query)integerNaoServico para calcular a duracao dos slots
duration_minutes(query)integerNaoDuracao do slot em minutos quando service_id nao for informado (padrao 60)
bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/appointments/availability?date=2026-06-15&professional_id=3&service_id=7" \
  -H "api_access_token: YOUR_TOKEN" | jq .
200Slots disponiveis (apenas inicios livres, como array de timestamps ISO)
json
{
  "data": {
    "date": "2026-06-15",
    "professional_id": 3,
    "slots": [
      "2026-06-15T08:00:00Z",
      "2026-06-15T10:00:00Z",
      "2026-06-15T11:00:00Z"
    ]
  }
}

Slots ja conflitantes nao aparecem

A resposta lista apenas os horarios de inicio disponiveis (array de timestamps ISO 8601). Horarios ocupados sao omitidos — nao ha campo available: false.

GET/api/v1/accounts/{account_id}/appointments/metrics

Retorna metricas agregadas de atendimentos para um intervalo de datas. Usado pela aba Relatorios no dashboard.

BACK-002 — Validacao de data

Passar uma string de data invalida (ex: from=invalid) agora retorna 422 em vez de 200 com dados vazios.

Parametros de Query

NomeTipoObrigatorioDescricao
from(query)string (date-time)NaoInicio do periodo (ISO 8601 UTC). Padrao: 30 dias atras. Deve ser uma data valida — invalido retorna 422.
to(query)string (date-time)NaoFim do periodo (ISO 8601 UTC). Padrao: hoje. Deve ser uma data valida — invalido retorna 422.
bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/appointments/metrics?from=2026-04-01T00:00:00Z&to=2026-04-30T23:59:59Z" \
  -H "api_access_token: YOUR_TOKEN" | jq .
200Metricas agregadas do periodo
json
{
  "data": {
    "total": 142,
    "completed": 87,
    "cancelled": 12,
    "no_show": 8,
    "confirmed": 35,
    "revenue_cents": 2180000,
    "currency": "BRL"
  }
}
422String de data invalida (BACK-002)
json
{ "error": "argument out of range" }
POST/api/v1/accounts/{account_id}/appointments/bulk_action

Aplica uma acao de status a multiplos atendimentos simultaneamente.

Escopo de conta e acoes suportadas

A operacao filtra os atendimentos com policy_scope(Appointment).where(id: ids) — ou seja, apenas atendimentos da conta autenticada sao afetados; IDs de outras contas sao ignorados silenciosamente, sem vazamento cross-tenant.

Acoes suportadas: confirm, cancel, no_show. Nao existe acao complete em massa — qualquer outro valor retorna 422.

Body

NomeTipoObrigatorioDescricao
idsarray[integer]SimIDs dos atendimentos (escopados a conta)
actionstringSimconfirm | cancel | no_show
reasonstringNaoMotivo do cancelamento (usado quando action=cancel)
bash
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/appointments/bulk_action" \
  -H "api_access_token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "ids": [42, 43, 44], "action": "confirm" }'
200Resultado da acao em massa
json
{ "data": { "count": 3, "action": "confirm" } }

Endpoint ainda nao implementado

A rota GET /appointments/export.csv existe mas atualmente retorna 501 Not Implemented. A exportacao CSV esta planejada para um milestone futuro — nao dependa deste endpoint em integracoes ainda.

GET/api/v1/accounts/{account_id}/appointments/export.csv

Exportacao CSV de atendimentos. Reservado — retorna 501 Not Implemented na versao atual.

501Funcionalidade ainda nao disponivel
json
// resposta vazia, HTTP 501 Not Implemented

Transicoes de Status (BACK-010)

O modelo Appointment implementa um guard de transicao de status. Apenas as transicoes listadas abaixo sao permitidas. Qualquer outra tentativa retorna 422 com:

{{"errors":{"status":["cannot transition from X to Y"]}}}
Status atualPode transicionar para
scheduledconfirmed, cancelled, no_show, completed
confirmedcompleted, cancelled, no_show
completed— (terminal)
cancelled— (terminal)
no_show— (terminal)

Endpoints de acao

Use os endpoints dedicados /confirm, /complete, /no_show para transicionar status. O endpoint PATCH so atualiza campos de dados (scheduled_at, notes, partner_id, custom_attributes).

Auditoria de Mudancas (Feature E)

O modelo Appointment utiliza a gem Audited para registrar automaticamente todas as mudancas nos campos criticos.

Campo auditadoDescricao
statusToda transicao de status e registrada com timestamp e user_id
scheduled_atReagendamentos — valor anterior e novo
ends_atAlteracao de horario de fim
notesEdicoes nas observacoes
price_centsAjustes de preco
cancellation_reasonMotivo de cancelamento
cancelled_by_idQuem cancelou (user ID)
pipeline_card_idVinculacao/desvinculacao de card Pipeline Pro

O historico de auditoria pode ser consultado internamente via Rails console:

ruby
# Listar todas as mudancas de um atendimento
appt = Appointment.find(42)
appt.audits.order(:created_at).each do |audit|
  puts "#{audit.created_at} — #{audit.user_id} — #{audit.audited_changes}"
end

# Filtrar apenas mudancas de status
appt.audits.select { |a| a.audited_changes.key?('status') }

API de auditoria

Nao existe endpoint REST publico para consultar audits de atendimentos nesta versao. O log esta disponivel apenas via Rails console ou integracao direta com o banco de dados (tabela audits).