Serviços

Catálogo de serviços agendáveis. Cada serviço define duração, preço padrão e templates de lembrete automático que são materializados no momento do agendamento.

Licença e permissão

Serviços está incluído em toda licença NooviChat válida. Qualquer membro autenticado da conta pode listar e consultar. Criar, atualizar e remover exige administrador ou a permissão customizada appointment_manage.

Sem autenticação a API responde 401; um membro sem permissão de gerenciamento recebe 403; um ID inexistente ou de outra conta recebe 404.

Templates de Lembrete

Cada serviço pode ter N templates de lembrete configurados (ex: "1 dia antes", "3 horas antes"). Ao criar um atendimento, somente templates ativos, configurados para WhatsApp e com send_at ainda no futuro são materializados como lembretes pendentes. O despacho posterior exige que o contato tenha um vínculo contact_inbox com canal Channel::Whatsapp da mesma conta ou Channel::Api da mesma conta respaldado por uma configuração WAHA, Uazapi ou NooviConnect; uma falha pode manter o lembrete pendente para nova tentativa ou marcá-lo como falho. Não existe filtro por uma flag active do inbox nessa seleção.

Ao materializar o lembrete, o corpo faz uma única passagem de substituição literal apenas pelos tokens exatos: {{paciente}}, {{cliente}}, {{profissional}}, {{servico}}, {{data}}, {{hora}}, {{duracao}}, {{valor}} e {{empresa}}. Um token desconhecido permanece literal. Um texto com aparência de token que venha dentro de um valor inserido não passa por uma segunda renderização.

Data, hora e valor usam as configurações da conta

{{data}} e {{valor}} são formatados com o locale da conta; {{data}} e {{hora}} usam o fuso efetivo de agendamento: primeiro account.reporting_timezone, depois account.custom_attributes.timezone gravado pelo onboarding e, sem uma configuração válida, America/Sao_Paulo. Se o locale não estiver disponível, o backend usa o locale padrão da instalação quando ele estiver disponível e, por fim, inglês.

{{valor}} usa primeiro o preço e a moeda gravados no atendimento. Quando um deles não foi gravado, somente esse campo recorre ao preço ou à moeda padrão do serviço. O resultado tem duas casas decimais; sem preço ou sem moeda, o token vira texto vazio.

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

Lista todos os serviços ativos do catálogo da conta (não-descartados), ordenados por nome, com reminder_templates embutidos. Qualquer membro da conta pode visualizar.

Sem filtros nem paginação

Esta listagem retorna sempre todos os serviços ativos da conta — não aceita parâmetros de query de filtro ou paginação.

bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/services" \
  -H "api_access_token: YOUR_TOKEN" | jq .
200Lista de serviços
json
{
  "data": [
    {
      "id": 7,
      "name": "Limpeza Dental",
      "description": "Profilaxia e polimento dental completo.",
      "duration_minutes": 60,
      "default_price_cents": 20000,
      "currency": "BRL",
      "color": "#10B981",
      "online_available": true,
      "active": true,
      "custom_attributes": {
        "external_code": "PROC-001"
      },
      "reminder_templates": [
        {
          "id": 5,
          "label": "1 dia antes",
          "days_before": 1,
          "hours_before": 0,
          "minutes_before": 0,
          "body_template": "Ola {{paciente}}, lembrando do seu {{servico}} amanha as {{hora}}.",
          "active": true,
          "send_via": "whatsapp",
          "whatsapp_template_id": null
        }
      ]
    }
  ]
}
POST/api/v1/accounts/{account_id}/services

Cria novo serviço no catálogo. Exige administrador ou a permissão appointment_manage.

Body (service)

NomeTipoObrigatorioDescricao
namestringSimNome do serviço
duration_minutesintegerSimDuração em minutos (mínimo 1)
descriptionstringNaoDescrição do serviço
default_price_centsintegerNaoPreço padrão em centavos (padrão 0)
currencystringNaoMoeda (3 letras, padrão BRL)
colorstringNaoCor hex para o calendário
online_availablebooleanNaoDisponível no widget público (padrão true)
activebooleanNaoInclui o serviço nas listagens ativas (padrão true)
custom_attributesobjectNaoMetadados livres persistidos no serviço (padrão {})
bash
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/services" \
  -H "api_access_token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "service": {
      "name": "Clareamento Dental",
      "duration_minutes": 90,
      "default_price_cents": 45000,
      "currency": "BRL",
      "online_available": true
    }
  }'
201Serviço criado
json
{ "data": { "id": 8, "name": "Clareamento Dental", "duration_minutes": 90 } }
403Usuário autenticado sem permissão de gerenciamento.
422Dados do serviço ou templates de lembrete inválidos.
GET/api/v1/accounts/{account_id}/services/{id}

Retorna detalhes de um serviço incluindo templates de lembrete.

bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/services/7" \
  -H "api_access_token: YOUR_TOKEN" | jq .
200Serviço da conta com reminder_templates embutidos.
404Serviço não encontrado nesta conta.
PATCH/api/v1/accounts/{account_id}/services/{id}

Atualiza dados do serviço. Exige administrador ou a permissão appointment_manage.

Body (service; todos opcionais no PATCH)

NomeTipoObrigatorioDescricao
namestringNaoNovo nome do serviço
descriptionstring | nullNaoNova descrição
duration_minutesintegerNaoDuração em minutos (mínimo 1)
default_price_centsintegerNaoPreço padrão em centavos (mínimo 0)
currencystringNaoCódigo de moeda com 3 letras
colorstringNaoCor hex para o calendário
online_availablebooleanNaoDisponibilidade no widget público
activebooleanNaoAtiva ou desativa o serviço sem fazer soft-delete
custom_attributesobjectNaoSubstitui o objeto de metadados livres enviado para o serviço
bash
curl -X PATCH "https://chat.seudominio.com/api/v1/accounts/1/services/7" \
  -H "api_access_token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "service": { "duration_minutes": 75, "default_price_cents": 22000 } }'
200Serviço atualizado; o corpo retorna { data: service }.
403Usuário autenticado sem permissão de gerenciamento.
422Dados do serviço ou templates de lembrete inválidos; a transação é revertida.
404Serviço não encontrado nesta conta.
DELETE/api/v1/accounts/{account_id}/services/{id}

Remove o serviço por soft-delete. Exige administrador ou appointment_manage; o histórico de atendimentos é preservado.

bash
curl -X DELETE "https://chat.seudominio.com/api/v1/accounts/1/services/7" \
  -H "api_access_token: YOUR_TOKEN"
204Serviço removido com sucesso. A resposta não possui corpo.
403Usuário autenticado sem permissão de gerenciamento.
404Serviço não encontrado nesta conta.

Templates de Lembrete

Não há endpoints REST dedicados para reminder_templates

Os templates de lembrete não têm rotas próprias (/services/{id}/reminder_templates não existe). Eles são gerenciados como um campo aninhado no próprio recurso de serviço: envie o array reminder_templates no body do POST ou PATCH de /services. O backend faz replace-all — a lista enviada substitui integralmente os templates atuais do serviço.

O GET /services/{id} já retorna os reminder_templatesembutidos. Para criar, atualizar ou remover, envie o array completo no body do serviço. Omitir o campo preserva os templates existentes; enviar [] remove todos.

Somente WhatsApp e campo Meta legado

O único canal de entrega implementado é whatsapp. Lembretes legados com outro canal falham de forma fechada antes de qualquer requisição externa. O campo whatsapp_template_id permanece temporariamente no contrato apenas para compatibilidade com clientes antigos: o backend aceita e ignora o valor, não o utiliza para selecionar um template Meta e sempre o retorna como null.

Novas gravações aceitam corpos de até 4096 caracteres. Uma resposta de leitura ainda pode conter um body_template legado maior enquanto ele não for editado. Se a substituição dos tokens gerar um corpo acima desse limite, o lembrete materializado fica com status de falha e não é enviado.

Campos de cada item de reminder_templates

NomeTipoObrigatorioDescricao
body_templatestringSimCorpo de até 4096 caracteres, com uma única passagem de substituição literal dos nove tokens exatos documentados acima
labelstring | nullNaoLabel descritivo (ex: 1 dia antes)
days_beforeintegerNaoDias antes do atendimento, de 0 a 1491308 (padrão 0)
hours_beforeintegerNaoHoras antes do atendimento, de 0 a 35791394 (padrão 0)
minutes_beforeintegerNaoMinutos antes do atendimento, de 0 a 2147483647 (padrão 0)
activebooleanNaoSe o lembrete está ativo (padrão true)
send_viastringNaoUse whatsapp, único canal de entrega implementado (padrão whatsapp)
whatsapp_template_idinteger | nullNaoDeprecated: compatibilidade de rollout. O valor é ignorado e a resposta sempre retorna null; não seleciona template Meta

Limites da antecedência

Os offsets devem ser inteiros: days_before entre 0 e 1491308, hours_before entre 0 e 35791394, e minutes_before entre 0 e 2147483647. Pelo menos um deles deve ser maior que zero. A antecedência normalizada, calculada como days_before * 1440 + hours_before * 60 + minutes_before, também deve ser menor ou igual a 2147483647 minutos.

bash
# Definir os lembretes de um servico via PATCH (replace-all):
curl -X PATCH "https://chat.seudominio.com/api/v1/accounts/1/services/7" \
  -H "api_access_token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "service": {
      "reminder_templates": [
        {
          "label": "1 dia antes",
          "days_before": 1,
          "body_template": "Ola {{paciente}}, lembrando do seu {{servico}} amanha as {{hora}}.",
          "send_via": "whatsapp",
          "active": true
        },
        {
          "label": "3 horas antes",
          "hours_before": 3,
          "body_template": "Ola {{paciente}}! Seu {{servico}} e hoje as {{hora}}.",
          "send_via": "whatsapp"
        }
      ]
    }
  }'
422Template inválido, corpo acima de 4096 caracteres, offset fora do intervalo, delta sem antecedência positiva ou soma normalizada acima de 2147483647 minutos.

Lembretes já materializados

Alterar ou remover um template via replace-all não reescreve nem cancela imediatamente os lembretes já materializados. Ao destruir um template, o backend preserva esses registros e apenas define seu service_reminder_template_id como null. No próximo reagendamento do atendimento, os lembretes pendentes órfãos ou que deixaram de ser entregáveis são cancelados, e os templates entregáveis da configuração atual são reconciliados ou materializados para o novo horário.