🚀 Implantação pela API, na ordem correta

🤖 Implantando com IA? O roteiro para o implantador (preparação, frase de disparo, rotina e segurança) está em Implantação com IA. Esta página continua sendo a referência técnica da ordem e das chamadas.

Roteiro passo a passo para implantar ou migrar uma clínica pela API externa — da empresa à conferência pelo Farol — com as rotas, os campos obrigatórios e os ids que cada passo entrega ao seguinte.

✅ Novo — set/2026 Ordem oficial revisada em 24/09/2026 ✅ Novo — 26/09/2026: conferência por serviço (farol/itens/{servicoRaizId}) e GET completo para o PUT ✅ Novo — 29/09/2026: contrato de 28/09 (servicoTaxa, listas por ID ou objeto, lote no estoque), token rbt_ e conferência retroativa ✅ Novo — 30/09/2026: token rbt_ da clínica é o caminho recomendado ✅ Novo — 13/10/2026: PUT parcial, 403 com escopoFaltante, repasse geral, EAN/NCM como texto, 409 em produto/taxa em uso

⚠️ Implantou pela API antes de 28/09/2026 às 17:43? Confira uma vez

A correção geral da API externa de 28/09/2026 (em produção às 17:43, Brasília) acabou com rotas que respondiam sucesso sem gravar ou gravavam errado sem avisar. Se a implantação desta clínica gravou algo antes disso, confira: pacientes em lote (/pacientes/bulk podia responder criado sem gravar — compare a planilha com GET /pacientes); lote de preços da tabela interna (/tabelas-preco/produtos/bulk gravava sem converter para centavos — confira GET /tabelas-preco/produtos?id=…); movimentações alteradas por PUT sem duplicatas (parcelas apagadas); fornecedores alterados por PUT sem ativo (reativados); UF de operadora, empresa e colaborador enviados sem UF (gravada a primeira UF); saídas e transferências de estoque sem lote (baixa em lote qualquer). Lista completa das correções: Novidades da API.

ℹ️ Antes de começar

Pressupõe que você leu a visão geral (URLs, autenticação, permissões e convenções). Os exemplos usam homologação e duas variáveis de ambiente — troque pela produção só depois de ensaiar o roteiro inteiro:

export BASE="https://api.hmg.rabisistemas.dev/api/v1/integrations"
export TOKEN="rbt_SEU_TOKEN"   # recomendado: token rbt_ da clínica (tipo "Implantação e manutenção")
                               # alternativa: a chave rbk_ emitida pelo time Rabi (mesmo uso)

Confira que o token é do mesmo ambiente da URL (produção × homologação). Guarde-o numa variável de ambiente ou cofre — nunca no próprio script versionado.

Cada passo diz: rota(s), campos obrigatórios, ids que o próximo passo precisa, um exemplo mínimo, o lote disponível e como conferir. Os detalhes completos de cada rota estão na Referência — Cadastros.

Duas regras que valem para o roteiro inteiro (convenções do Swagger de 25/09/2026): (1) PUT ✅ Novo — 13/10/2026 — desde 01–02/10/2026, em serviços, convênios, pacientes, colaboradores, grades, orçamentos, movimentações e guias, campo omitido mantém, null limpa, lista omitida mantém e lista enviada substitui ([] apaga): envie só o que muda e, ao trocar uma lista, mande a lista inteira já corrigida (parta do GET, que devolve os mesmos nomes do PUT). Nas rotas cuja descrição não declara nada (produtos, taxas, equipamentos, locais, depósitos, fornecedores, fabricantes) e em /empresas/{id}, o PUT ainda sobrescreve: GET antes e objeto completo (ver convenções de PUT); (2) confira se o token vale até o fim da implantação — o cabeçalho X-ApiKey-Expires-At da primeira resposta mostra a validade (rbk_ e rbt_); o rbt_ a própria clínica renova; a rbk_, só o time Rabi.

1. A ordem oficial

A mesma ordem vale para a implantação pela tela e pela API — a API apenas recusa com um código de erro quando a ordem é violada, em vez de a tela "não mostrar a opção na lista".

Empresa → Depósito mínimo → Locais e tipos → Operadoras/Fornecedores → Taxas → Produtos (catálogo)
→ Equipamentos → Serviços (subserviços ANTES dos pais) → Colaboradores → Convênios (dados, depois abas)
→ Conferência pelo Farol → Financeiro/Estoque → Pacientes → Parâmetros e Permissões

1.1 As 3 dependências que estavam invertidas no manual

Verificadas na API e na tela em 24/09/2026. Se você seguia a ordem antiga, é aqui que a implantação travava:

#O manual diziaDependência realOrdem corretaO erro que aparece se violar
1Local na etapa 1; depósito só no estoque (etapa 8)POST /locais exige depositoPadraoSaidaId de um depósito ativo (a tela também pede). E POST /produtos exige depositoId.Criar um depósito mínimo por unidade logo após a empresa, antes dos locais. O resto do estoque continua depois.422 — "O depósito padrão de saída selecionado está inativo ou não existe".
2Serviços antes do catálogo de produtosServiço de medicamento/material exige o produto já cadastrado: a composição (produtoIds) aponta para o produto.Catálogo de produtos antes dos serviços. Entradas de estoque e custo podem ficar para depois.A composição não tem para onde apontar (o id do produto ainda não existe). Consertar depois exige PUT /servicos/{id} com a lista produtoIds completa (lista enviada substitui; desde 01–02/10 o resto do serviço é preservado).
3Taxa na etapa 7As abas Taxas e Serviços do convênio e a tabela de preço interna exigem as taxas cadastradas (o serviço também aponta as suas taxas em servicoTaxa).Taxas antes dos convênios e das tabelas de preço internas.Em PUT /convenios/{id}/taxas o item volta como ERRO dentro de um 207 (taxa inexistente).
⚠️ Depósitos: o Swagger publicado prevalece

Um aviso interno de 25/09 dizia que a API "não cria depósito". O Swagger oficial publicado — fonte primária — tem POST /depositos e POST /depositos/bulk (empresaId* e descricaoDeposito*; 409 se o nome repetir na unidade). Portanto: a API cria depósito. O que a API não cria são os tipos auxiliares (grupo Auxiliares, só leitura) — esses se criam pela tela e você informa os ids.

2. Passo 0 — Preparação

  1. Token e permissões. ✅ Novo — 30/09/2026 (a) Recomendado (decisão do Diretor em 30/09): um token rbt_ que a própria clínica gera em Configurações › Parâmetros › Integrações, tipo Implantação e manutenção (em produção desde 28/09/2026), só com as permissões do kit de implantação — a clínica também o renova e revoga, sem depender do time Rabi. (b) Alternativa: a chave rbk_ emitida pelo time Rabi no portal comercial com o mesmo kit — continua valendo para quem já tem; anote a data do cabeçalho X-ApiKey-Expires-At e, se ela vence antes do fim previsto da implantação, peça a nova ao time Rabi antes de começar (não há renovação automática nem autoatendimento para a rbk_). Nos dois casos, teste com GET /empresas (deve responder 200). ✅ Novo — 13/10/2026 Se faltar permissão no meio da implantação, o 403 diz qual em escopoFaltante: com o rbt_, use Editar permissões no mesmo token (o valor não muda; vale na chamada seguinte — #653). Validade do rbt_: 30, 90, 365 dias ou sem validade; Renovar gera valor novo e invalida o anterior. O token aparece uma única vez: guarde em local seguro e nunca o cole em documento, chamado, e-mail, manual ou conversa com IA que fique salva; token de uma clínica não acessa outra; suspeita de vazamento → revogue e gere outro (detalhes).
  2. Leia os auxiliares (só leitura) e monte o seu "dicionário de ids":
    for r in tipos-produto tipos-codigo tabelas-ans87 regimes-atendimento tipos-servico tipos-guia \
             tipos-taxa unidades-medida principios-ativos sexos estados-civis conselhos-profissionais \
             unidades-federativas tipos-atendimento; do
      curl -s "$BASE/auxiliares/$r" -H "Authorization: Bearer $TOKEN" > "aux-$r.json"
    done
    Faltou algum tipo (ex.: um tipo de taxa ou tipo de produto próprio da clínica)? Crie pela tela do sistema e leia de novo — a API não cria auxiliares.
  3. Ids que só existem na tela. O Swagger publicado não tem rota de listagem para: especialidades (catálogo), categorias de pagamento, contas/caixas, centros de custo, motivos de movimentação de estoque, kits de documentos, modelos de XML do convênio (xmlConsultaId/xmlSpSadtId), tipos de contrato, tipos de anexo, perfis de permissão e fontes de preço de compra. Anote esses ids pela tela antes de começar (ou peça ao implantador que os informe à IA).
  4. Planilha de origem limpa: CNPJs/CPFs únicos, nomes sem duplicidade, vazio onde não há valor (nunca 0,01 como marcador de "sem valor").
  5. Leituras paginadas ✅ Novo — 14/10/2026: a API externa devolve no máximo 200 itens por página (#669); pageSize maior é reduzido sem erro. Ao conferir o que já foi carregado, percorra todas as páginas até totalPages — ver Listagens e paginação.

3. Passo 1 — Empresa (e unidades)

POST /empresas · permissão empresa:create · sem lote (uma chamada por empresa/unidade).

Obrigatórios: razaoSocial, nomeFantasia, cnpj (único; com ou sem máscara), endereco (UF por id ou sigla). Entrega: empresaId — usado por depósitos, locais, convênios (empresaId e unidadesIds) e colaboradores (empresas).

curl -s -X POST "$BASE/empresas" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "razaoSocial": "Clínica Rabi Sistemas LTDA",
  "nomeFantasia": "Clínica Rabi Sistemas",
  "cnpj": "12345678000190",
  "endereco": { "Cep": "70000000", "Endereco": "SBS Quadra 2", "Numero": "1",
                "Bairro": "Asa Sul", "Cidade": "Brasília", "unidadeFederativaId": "DF" }
}'

Conferir: GET /empresas (sem paginação de fato — envelope com page = 1 e a lista inteira, ativas e inativas). Para corrigir depois: PUT /empresas/{id} substitui o cadastro inteiro — envie a empresa completa. 409 = CNPJ já existe (inclusive em empresa desativada): reutilize o id existente.

4. Passo 2 — Depósito mínimo por unidade dependência corrigida

POST /depositos ou POST /depositos/bulk (até 50) · deposito:create.

Obrigatórios: empresaId, descricaoDeposito. Entrega: depositoId — exigido por POST /locais (depositoPadraoSaidaId) e por POST /produtos (depositoId).

curl -s -X POST "$BASE/depositos/bulk" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "depositos": [
    { "empresaId": 54, "descricaoDeposito": "Depósito Central" }
  ]
}'

Conferir: GET /depositos?ativo=true. 409 = já existe depósito com esse nome nessa unidade. Crie só o mínimo agora; depósitos adicionais, entradas e saldos ficam para o passo 12.

5. Passo 3 — Locais (e tipos) dependência corrigida

POST /locais · local:create · sem lote.

Obrigatórios: nome, empresaId, depositoPadraoSaidaId (depósito ativo). Entrega: localId — usado nas grades e na agenda.

curl -s -X POST "$BASE/locais" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "nome": "Consultório 1", "empresaId": 54, "depositoPadraoSaidaId": 3
}'

Se violar a ordem: 422 "O depósito padrão de saída selecionado está inativo ou não existe". 409 = já existe um local com esse nome nessa empresa. Tipos: é aqui que se conferem os tipos auxiliares da clínica (passo 0) — os que faltarem, crie pela tela. Conferir: GET /locais. Corrigir: PUT /locais/{id} não declara o comportamento → envie o local completo.

6. Passo 4 — Operadoras (com planos), fornecedores e fabricantes

CadastroRota (lote)ObrigatóriosEntrega
OperadoraPOST /operadoras · /operadoras/bulk (50)nome, descricao, registroANS, cnpj, codigoCNES, endereco (cep, endereco, numero, bairro, cidade, unidadeFederativa por extenso); plano[].nome se enviar planosoperadoraId (convênio) e os ids dos planos (GET /operadoras/{id} → plano[].id), usados na aba Planos do convênio e no vínculo do paciente
FornecedorPOST /fornecedores · /fornecedores/bulk (50)nome, cnpj, inscricao, telefoneFornecedorfornecedorId (produto, estoque, financeiro)
FabricantePOST /fabricantes · /fabricantes/bulk (50)nomefabricanteId — obrigatório em POST /produtos
curl -s -X POST "$BASE/operadoras/bulk" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "operadoras": [{
    "nome": "Operadora Exemplo", "descricao": "Operadora Exemplo", "registroANS": "123456",
    "cnpj": "11222333000181", "codigoCNES": "1234567", "identificacaoPrestadorXML": "CNPJ",
    "endereco": { "cep": "70000000", "endereco": "SBS Quadra 2", "numero": "1",
                  "bairro": "Asa Sul", "cidade": "Brasília", "unidadeFederativa": "Distrito Federal" },
    "plano": [ { "nome": "Plano Ouro" }, { "nome": "Plano Prata" } ]
  }]
}'

Atenção: identificacaoPrestadorXML define como o prestador aparece no XML TISS (CNPJ ou CODIGO_NA_OPERADORA — o XSD só aceita um). Em PUT /operadoras/{id} mande as listas completas de plano e contato (o que faltar é removido). Conferir: GET /operadoras/{id}, GET /fornecedores, GET /fabricantes.

7. Passo 5 — Taxas dependência corrigida

POST /taxas · /taxas/bulk (50) · taxa:create.

Obrigatórios: taxas (o nome da taxa), codigoTaxa, tipoTaxaId (de GET /auxiliares/tipos-taxa), valor (em reais). Opcionais relevantes: tipoCodigoId, tabelaANS87ID. Entrega: taxaId — usado pela aba Taxas do convênio e pela lista servicoTaxa do serviço.

curl -s -X POST "$BASE/taxas/bulk" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "taxas": [ { "taxas": "Taxa de sala", "codigoTaxa": "TX-001", "tipoTaxaId": 1, "valor": 73.99 } ]
}'

Conferir: GET /taxas?ativo=true. Observação: POST /taxas responde 200 (não 201) ao criar. Corrigir: PUT /taxas/{id} com a taxa completa (a rota não declara que omitido mantém). Desativar (DELETE /taxas/{id}) uma taxa que está em serviço ativo responde 409 com a lista dos serviços — ver o quadro do passo 8.

8. Passo 6 — Produtos (catálogo) dependência corrigida

POST /produtos · /produtos/bulk (50) · produto:create.

Obrigatórios: nome, codigoProduto, apresentacao, contendo (quantidade na embalagem), depositoId, tipoProdutoId, fabricanteId, unidadeDeMedidaId, prazoDeReposicao (dias). Opcionais relevantes: principioAtivoId, tipoCodigoId, tabelaANS87ID, fornecedores (ids), estoquePodeNegativar, precoUltimaPesquisa/dataUltimaPesquisa, codigoEAN e codigoNCM. ✅ Novo — 13/10/2026 (#635) codigoProduto até 10 caracteres (letras, números, ponto, hífen ou barra — padrão TISS); codigoEAN como texto com 8, 12, 13 ou 14 dígitos (preserva o zero à esquerda; número ainda é aceito, mas perde os zeros); codigoNCM texto com exatamente 8 dígitos. Entrega: produtoId — composição dos serviços, aba Produtos do convênio, tabela interna, estoque.

curl -s -X POST "$BASE/produtos/bulk" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "produtos": [{
    "nome": "Soro fisiológico 500ml", "codigoProduto": "PRD-500", "apresentacao": "Frasco 500ml",
    "contendo": 1, "depositoId": 3, "tipoProdutoId": 1, "fabricanteId": 8,
    "unidadeDeMedidaId": 2, "prazoDeReposicao": 15
  }]
}'

422 = referência inválida (depósito, tipo, fabricante ou unidade inexistente). O preço de venda calculado não é exposto pela API; entradas de estoque e custo ficam para o passo 12. Conferir: GET /produtos?ativo=true&nome=soro. Corrigir: PUT /produtos/{id} com o produto completo (a rota não declara que omitido mantém). Desativar (DELETE /produtos/{id} ou PUT com ativo: false) um produto que está em serviço ativo responde 409 — ver o quadro do passo 8.

9. Passo 7 — Equipamentos

POST /equipamentos · /equipamentos/bulk (50) · só nome é obrigatório (409 se já existir equipamento ativo com o mesmo nome). Entrega: equipamentoId — composição do serviço (equipamentoIds) e grade de equipamento.

curl -s -X POST "$BASE/equipamentos/bulk" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "equipamentos": [ { "nome": "Ultrassom Sala 2" } ]
}'

10. Passo 8 — Serviços (subserviços ANTES dos pais)

POST /servicos · /servicos/bulk (50) · servico:create.

Obrigatórios: nome, descricao. Relevantes para preço e faturamento: valor (reais), somarItems, codigo, codigoTUSS, tempoServico (min), tipoServicoId, tipoCodigoId, tipoGuiaId, tabelaANS87ID, regimeDeAtendimentoId, tipoAtendimento (id), especialidadesId, ✅ Novo — 13/10/2026 documentos (documentos obrigatórios do serviço, ex.: PRESCRIÇÃO MÉDICA — [{ "id": 3 }]; exigidos no agendamento, no acolhimento, na autorização e pela IA) e perfilFiscalId (perfil fiscal da NFS-e; null desvincula). Composição (✅ contrato de 28/09/2026): produtoIds e equipamentoIds — cada item como ID puro ou objeto (120 ou { "id": 120, "quantidade": 2, "valorUnitario": 12.50 }; equipamento { "id": 5 }); servicoTaxa: [{ "taxaId": 15, "quantidade": 1 }] para as taxas do serviço (saíram taxaServicoId e valorTaxaServico — o valor da taxa vem do cadastro da taxa e da aba Taxas do convênio); servicosRelacionados — ids de subserviços já criados, como ID puro ou { "servicoId": 46, "quantidade": 1 }. Campo opcional omitido não derruba mais a rota (até 28/09, POST /servicos sem especialidadesId dava 500 … reading 'filter'). Entrega: servicoId.

  1. Primeiro os subserviços (serviços que entram dentro de outros — ex.: "Aplicação endovenosa"). Guarde os ids.
  2. Depois os serviços pais, informando os subserviços em servicosRelacionados. Não dá para apontar um id que ainda não existe — e corrigir depois exige PUT /servicos/{id} com a lista servicosRelacionados completa (lista enviada substitui; o resto do serviço é preservado desde 01–02/10/2026).
# 1) subserviço
curl -s -X POST "$BASE/servicos" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "nome": "Aplicação endovenosa", "descricao": "Aplicação endovenosa", "valor": 40,
  "servicoTaxa": [ { "taxaId": 15, "quantidade": 1 } ]
}'
# 2) serviço pai que contém o subserviço (id 46) e o produto (id 120)
#    (ID puro = quantidade 1; objeto = quantidade/valor próprios)
curl -s -X POST "$BASE/servicos" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "nome": "Noripurum aplicado EV", "descricao": "Noripurum aplicado EV",
  "somarItems": true,
  "produtoIds": [ { "id": 120, "quantidade": 1 } ],
  "servicosRelacionados": [ { "servicoId": 46, "quantidade": 1 } ]
}'
💲 O que somarItems decide (regra vigente desde 23–24/09)

somarItems é o "Definir preço do serviço pelos itens" da tela: decide só o valor próprio do serviço (zero quando marcado). Ele não decide mais se os itens entram — taxas, produtos e subserviços com Utiliza no convênio sempre entram na conta do serviço, cada um com a sua conversão. Detalhes: regra única do serviço composto e árvore de decisão de preços.

Conferir: GET /servicos/{id} (traz a composição completa e, na leitura, o campo somarItens; na escrita o nome do campo é somarItems). A listagem GET /servicos é resumida. Para corrigir: PUT /servicos/{id} parcial ✅ Novo — 13/10/2026 — envie só o que muda: campo omitido mantém, null limpa (valor: null vira 0), lista omitida mantém, lista enviada substitui. Ex.: { "nome": "X" } só renomeia; { "documentos": [{ "id": 3 }] } deixa só o documento 3. Produto da composição enviado sem valorUnitario mantém o valor que já tinha (produto novo sem valor entra com 0).

✅ Novo — 26/09/2026 Leitura segura para o PUT: GET /servicos/{id} agora devolve especialidadesId e servicosRelacionados (a composição) com os mesmos nomes que o PUT espera. Antes, regravar um serviço a partir do GET apagava a composição e as especialidades, porque essas listas não voltavam na leitura. Receita: GET → altere só o necessário → PUT com o objeto completo — incluindo servicoTaxa, produtoIds e equipamentoIds (o Swagger garante no GET a composição por subserviços e as especialidades; confira se as demais listas vieram e, se faltar alguma, monte-a antes do PUT, senão ela não é preservada). ✅ Atualização 13/10/2026: desde 01–02/10 (#634) o PUT de serviço é parcial — lista omitida é preservada; a leitura devolve documentos, produtos, equipamentos e taxas com os nomes do PUT.

✅ Produto ou taxa em uso num serviço não pode ser desativado (#626, desde 02/10/2026)

DELETE /produtos/{id}, PUT /produtos/{id} com ativo: false e DELETE /taxas/{id} — e a exclusão pela tela — respondem 409 se o item estiver na composição de algum serviço ativo; a resposta traz a lista em servicos (serviço inativo não bloqueia). Para trocar o item: (1) substitua-o nesses serviços (PUT /servicos/{id} com a lista produtoIds ou servicoTaxa completa, já com o substituto); (2) marque utiliza/zerarValor do substituto nas abas do convênio; (3) desative o antigo e confira em GET /convenios/{id}/farol/itens/{servicoRaizId}.

Histórico: até 02/10/2026 a desativação era livre e o item sumia da composição sem aviso.

11. Passo 9 — Colaboradores e login

POST /colaboradores · /colaboradores/bulk (50) · colaborador:create — só cadastra, não cria acesso.

Obrigatórios: nome, cpf, dataDeNascimento, sexoId, telefone, celular, email, endereco (UF por extenso). Se enviar especialidades[]: uf (id), numeroConselho, especialidadeId, conselhoProfissionalId (de GET /auxiliares/conselhos-profissionais). Opcionais: empresas (ids das unidades onde atua), conselho[], corNaAgenda, vinculoRepasse — ✅ Novo — 13/10/2026 em cada lista (servicos, produtos, taxas), o item com servico/produto/taxa vazio ou null é a linha de repasse geral do tipo (vale para todo item sem linha própria; no máximo uma por tipo — 400 se vierem duas); a linha específica substitui a geral; específica com 0 e 0% = sem repasse naquele item; tipo sem nenhuma linha = sem repasse (#643). O repasse entra no custo do Farol desde 12/10/2026. Entrega: colaboradorId — aba Colaboradores do convênio, grades, e responsavelId de todas as escritas operacionais.

Login de verdade: POST /colaboradores/{id}/usuario (colaborador:update) cria o acesso no sistema (e-mail + senha, mínimo 8 caracteres). 409 se o colaborador já tiver login. Se o e-mail já tem acesso ao Rabi (ex.: em outra clínica), o usuário é vinculado a esta clínica e continua com a senha que já usa — a senha enviada não é aplicada.

curl -s -X POST "$BASE/colaboradores/60/usuario" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "email": "ana.rocha@clinicarabi.exemplo", "senha": "SENHA_FORTE_AQUI"
}'

Permissões de tela do usuário (perfis) são configuradas pela tela; a API só expõe as permissões de dashboard (grupo Parâmetros). Conferir: GET /colaboradores/{id}. Corrigir: PUT /colaboradores/{id} parcial (campo omitido mantém; vinculoRepasse.servicos/produtos/taxas só são trocadas quando enviadas; não reativa colaborador desativado; sucesso responde o número cru 201).

12. Passo 9b — Tabela de preço interna (preço de produto)

Depois das taxas e dos produtos e antes dos convênios (a política de preço do convênio pode apontar para ela):

  1. POST /tabelas-preco — obrigatórios name e priceType1 (nome do "Preço 1"; priceType2/priceType3 opcionais).
  2. POST /tabelas-preco/produtos/bulk (até 200, chave precos) — cada item: purchasePriceSourceId (id da tabela) + productId e os valores priceType1..3 em reais (ex.: 19547.56 = R$ 19.547,56; omitido mantém, null limpa — #652). É upsert: reenviar atualiza o preço. Lotes enviados antes de 28/09/2026: confira os valores.
curl -s -X POST "$BASE/tabelas-preco/produtos/bulk" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "precos": [ { "purchasePriceSourceId": 4, "productId": 120, "priceType1": 12.00 } ]
}'

Conferir: GET /tabelas-preco (envelope padrão { dados, page, … }), GET /tabelas-preco/produtos?id=4 e GET /tabelas-preco/precificacao. Renomear a tabela: PUT /tabelas-preco/{id} com o objeto completo.

13. Passo 10 — Convênios em 2 fases

13.1 Fase 1 — dados do convênio

POST /convenios · /convenios/bulk (50) · convenio:create.

Obrigatórios: descricao, dataInicio, empresaId ("Empresa Principal"), unidadesIds (unidades onde o convênio é aceito na agenda). Relevantes: operadoraId, cnpj (único por clínica; no /bulk de convênios, cnpj também é obrigatório — schema ConvenioLoteItem, é a chave que recusa convênio já cadastrado ou repetido no lote), registroANS (na criação o código ANS vai aqui), prazos (prazoPagamento, prazoRetorno, prazoRecursoGlosa…), fatorK, xmlConsultaId/xmlSpSadtId, politicasPorTipoProduto[] (tipoProdutoId, fatorK, idJson com fontePrecoId e tipoPrecificacao) — nível 2 de preço do produto.

curl -s -X POST "$BASE/convenios" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "descricao": "Operadora Exemplo — Plano Empresarial",
  "nomeFantasia": "Operadora Exemplo", "cnpj": "11222333000181", "registroANS": "123456",
  "dataInicio": "2026-10-01T00:00:00.000Z",
  "empresaId": "54", "unidadesIds": [54], "operadoraId": "3",
  "prazoPagamento": 30, "prazoRetorno": 30
}'

409 = CNPJ já existe; 422 = empresa, operadora ou kit não encontrado. No lote, reenviar o lote inteiro é seguro (os já criados são recusados por CNPJ duplicado). PUT /convenios/{id} é parcial desde 01–02/10/2026 ✅ Novo — 13/10/2026: campo omitido mantém (inclusive empresaId, unidadesIds, operadoraId e as datas); null limpa; unidadesIds e politicasPorTipoProduto, quando enviados, são a lista completa (o que faltar é desativado/inativado). Ver convenções de PUT.

13.2 Fase 2 — as abas (PUT em lote, upsert)

🩺 Especialidades do convênio antes de serviços e profissionais (#628)

Na aba Especialidades (PUT /convenios/{id}/especialidades, campo utiliza = coluna Atende da tela), marque todas as especialidades que o convênio cobre antes de configurar os serviços e os profissionais do convênio (ordem 2 da tabela abaixo). 🟡 Parcial Desde 02/10/2026 ela vale no cadastro do convênio: o upsert de serviços e de colaboradores do convênio (PUT /convenios/{id}/servicos, /colaboradores) recusa marcar Utiliza em serviço de especialidade não atendida — por isso as especialidades vêm primeiro. No agendamento e no orçamento a trava está desligada desde 06/10/2026 (temporário). A consulta de disponibilidade da IA (TalkMe, #633) é a exceção: ela respeita a coluna Atende — por isso a IA pode dizer que não há horário num caso que a recepção consegue agendar. Convênio sem nenhuma especialidade marcada como Atende não restringe nada; serviço sem especialidade não passa pela trava.

✅ Em produção desde 09/10/2026 — #633 A consulta única de disponibilidade da IA de atendimento (TalkMe) usa este cadastro (e o painel Disponibilidade por Convênio da Agenda, via catálogo #659, também): utiliza nos serviços e Atende nas especialidades do convênio, serviços e convênios na grade do profissional (passo 10b), Atende convênio no cadastro do profissional e o convênio ativo nas unidades onde é atendido (unidadesIds). Faltando algo, a IA dirá que não há atendimento, mesmo que a recepção consiga agendar pela tela.

✅ Novo — 30/09/2026 A API não tem "modo consulta": desde 29/09 (#632) as telas de convênio só gravam depois de Liberar para edição e, desde 01/10, do Salvar; pela API, estas rotas gravam direto, como sempre (o autor aparece como API externa no Histórico do convênio). Todas com convenio:update, uma por vez por convênio (a segunda simultânea recebe 429). Campo omitido mantém; null nos valores limpa (volta a "vazio" → o valor desce para o nível de baixo); 0 é zero de verdade.

OrdemAbaRotaChave do itemMáx.Campos que mais importam
1PlanosPUT /convenios/{id}/planosplanoId200utiliza (coluna Atende)
2EspecialidadesPUT /convenios/{id}/especialidadesespecialidadeId200utiliza (Atende), dataInicio, dataFim
3TaxasPUT /convenios/{id}/taxastaxaId200utiliza, zerarValor, valorConvertido, nomeConvertido, descricaoConvertida, codigo, tipoCodigoId, tabela87ANSId
4ProdutosPUT /convenios/{id}/produtosprodutoId200utiliza, zerarValor, valorUnitarioConversao, fatorK, fontePrecoCompraOptionsId, tipoPrecificacao (PRECO_1..3), códigos TUSS/TISS, tabela87ANSId
5ServiçosPUT /convenios/{id}/servicosservicoId200utiliza, pacote, zerarValor, valorInternoConvenio, tipoAtendimentoId, autorizacaoPrevia, retornoServico, codigoTuss/codigoConvenio, tabela87ANSId
6ColaboradoresPUT /convenios/{id}/colaboradorescolaboradorId100credenciado, atende (só true se credenciado ou com conversão), colaboradorConvertidoId, especialidadesIds (aditivo)
curl -s -X PUT "$BASE/convenios/12/taxas" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "taxas": [ { "taxaId": 15, "utiliza": true, "valorConvertido": 73.99 } ]
}'
curl -s -X PUT "$BASE/convenios/12/produtos" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "produtos": [ { "produtoId": 120, "utiliza": true, "valorUnitarioConversao": null, "fatorK": "20" } ]
}'
curl -s -X PUT "$BASE/convenios/12/servicos" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "servicos": [
    { "servicoId": 46, "utiliza": true },
    { "servicoId": 47, "utiliza": true, "pacote": true, "valorInternoConvenio": 350.00 }
  ]
}'
⚠️ valorInternoConvenio sozinho NÃO fecha pacote (regra vigente)

O valor combinado do serviço (servicos[].valorInternoConvenio) muda só o preço do próprio serviço: sem pacote: true, taxas, produtos e subserviços com utiliza continuam somando por cima e o zerarValor deles nem é consultado. Pacote é sempre marcação por convênio (não existe no cadastro do serviço). Para um preço fechado que já inclui itens, envie as três coisas:

# 1) serviço: valor combinado + pacote
curl -s -X PUT "$BASE/convenios/12/servicos" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "servicos": [ { "servicoId": 47, "utiliza": true, "valorInternoConvenio": 350.00, "pacote": true },
                { "servicoId": 46, "utiliza": true, "zerarValor": true } ]
}'
# 2) itens inclusos: zerarValor nas abas produtos/taxas (item cobrado à parte: zerarValor false)
curl -s -X PUT "$BASE/convenios/12/produtos" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "produtos": [ { "produtoId": 120, "utiliza": true, "zerarValor": true } ]
}'
curl -s -X PUT "$BASE/convenios/12/taxas" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "taxas": [ { "taxaId": 15, "utiliza": true, "zerarValor": true } ]
}'

Em PUT /convenios/{id}/servicos: valorInternoConvenio: null limpa o valor combinado (volta a vazio); campo omitido mantém. Envie zerarValor explicitamente em cada item (não dependa de valor padrão — desde 25/09/2026, criar o vínculo marcando só pacote não liga mais o Zerar). Confira depois em GET /convenios/12/farol/itens/47: os itens inclusos devem vir com conta_no_total: false e motivo_exclusao: "ZERADO_EM_PACOTE"; os cobrados à parte, com conta_no_total: true.

✅ em produção desde 24/09 A coluna Valor do serviço no convênio tem 4 linhas na tela (cadastro, combinado, valor próprio que vai na guia e Σ total). somarItens aparece no Swagger (cadastro do serviço — GET /servicos/{id}); efetivo.totalConvenio (o Σ = total do serviço no convênio = receita do Farol) existe na API do sistema (usada pela tela) — na API externa não aparece no Swagger. Pela API externa, leia o total em receita_total_servico (linha nivel = 0) de GET /convenios/{id}/farol/itens/{servicoRaizId}, ou em /farol/servicos (receita_total).

⚠️ As regras de preço que a API aplica (vigentes desde 23–24/09/2026)
  • Zero é zero, vazio é vazio. Vazio (null/nunca preenchido) desce para o nível de baixo (política do convênio → catálogo); 0 é zero. Nunca use 0.01 como "sem valor".
  • Utiliza = o convênio cobre o item. Sem Utiliza: custo sem receita (fora da receita do Farol; no orçamento entra a R$ 0). Com Utiliza: cadeia de conversão do convênio — nunca o preço da casa.
  • Itens sempre entram na conta do serviço (taxas, produtos, subserviços com Utiliza), cada um com a sua conversão. Só ficam fora: sem Utiliza, ou zerado em pacote de preço fechado (pacote: true + valor fixo ou combinado, inclusive 0).
  • Zerar é por item e não desce em cascata. Subserviço zerado perde só o próprio valor; os itens dentro dele seguem o próprio Zerar. Só a falta de Utiliza no subserviço tira tudo o que está dentro dele.
  • Preço fixo ou valor combinado que deve "já incluir" itens → pacote: true no serviço + zerarValor: true nos itens inclusos. valorInternoConvenio sozinho não é pacote.

Tabela-verdade e exemplos: Guia do Implantador de preços · Árvore de decisão.

Resposta: 200 = tudo processado; 207 = confira resultados[] (indice, status CRIADO/ATUALIZADO/ERRO/NAO_PROCESSADO, erro). Reenviar tudo é seguro (idempotente). Conferir cada aba: GET /convenios/{id}/taxas, /produtos, /servicos, /especialidades, /planos, /colaboradores.

14. Passo 10b — Grades de horário

Depois de colaboradores, equipamentos, locais e convênios (a grade lista os convênios atendidos). Informe na grade os serviços (servicos[]) e os convênios (convenio[]) — a consulta de disponibilidade da IA TalkMe (#633, em produção desde 09/10/2026) depende deles:

curl -s -X POST "$BASE/grades-colaborador" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "colaboradorGrade": [{
    "colaboradorId": 60, "localId": 7,
    "inicioAtendimento": "2026-10-01T08:00:00.000Z", "fimAtendimento": "2026-10-01T12:00:00.000Z",
    "tempoAtendimento": 30, "repetir": "SEMANALMENTE", "diasDaSemana": "SEG,QUA,SEX",
    "convenio": [ { "id": 12 } ]
  }]
}'

Conferir: GET /grades-colaborador?colaboradorId=60&localId=7&vigenteEm=2026-10-01 (ou GET /grades-colaborador/filter?colaboradorId=60&localId=7) e GET /grades-equipamento?equipamentoId=…&localId=…&vigenteEm=…. As duas listagens são paginadas de verdade (page/pageSize, envelope padrão) e aceitam os filtros colaboradorId/equipamentoId, localId e vigenteEm (só grades vigentes na data) — use-os para não trazer a grade inteira da clínica. Corrigir um horário: PUT /grades-colaborador/{id} / PUT /grades-equipamento/{id} — parcial desde 01–02/10/2026 (listas convenios/servicos enviadas substituem; trocar modoConvenio inverte o sentido da lista de convênios).

15. Passo 11 — Conferência pelo Farol

A conferência final de cada convênio é o Farol — a mesma leitura da tela Configurações › Convênios › aba Farol e do painel Expandir serviço (mesma fonte no banco):

RotaO que mostraFiltros úteis
GET /convenios/{id}/farol/itens/{servicoRaizId} ✅ Novo — 26/09/2026Árvore de UM serviço (= Expandir serviço), sem paginação ({ dados: [...] }, poucas dezenas de linhas), com farol_servico, margem_servico_pct e totais por subárvore. Preferível quando você já sabe o serviço. 404 = serviço não utilizado neste convênio— (o serviço vai no caminho)
GET /convenios/{id}/farol/itensÁrvore de composição (aba Farol › Itens): uma linha por item de cada serviço — produtos, taxas e subserviços (paginada)servicoRaizId, itemTipo, itemNome, search, apenasAtivosNoConvenio, servicoAtivo
GET /convenios/{id}/farol/servicosUma linha por serviço com o total: custo_total, receita_total, receita_propria_servico, margem_resultado_pct, farolfarol (lista separada por vírgula), ativoNoConvenio, servicoAtivo, sortBy/sortOrder
GET /convenios/{id}/farol/produtosUma linha por produto: custo, receita, margem_resultado_pct, farolfarol, ativoNoConvenio, produtoAtivo
# um serviço (preferível): árvore inteira, sem paginação
curl -s "$BASE/convenios/12/farol/itens/47" -H "Authorization: Bearer $TOKEN"
# vários serviços: árvore paginada
curl -s "$BASE/convenios/12/farol/itens?apenasAtivosNoConvenio=true&servicoAtivo=true&pageSize=200" \
  -H "Authorization: Bearer $TOKEN"
# linha de exemplo (campos do Swagger de 26/09/2026; valores em reais, ilustrativos):
# { "servico_raiz_id": 47, "servico_raiz": "Noripurum aplicado EV", "convenio_id": 12,
#   "item_tipo": "PRODUTO", "nivel": 1, "servico_pai_id": 47, "item_id": 120,
#   "item_nome": "Soro fisiológico 500ml", "codigo": "PRD-500", "tabela_ans_87": "19",
#   "zerar_valor": true, "conta_no_total": false,
#   "motivo_exclusao": "ZERADO_EM_PACOTE", "quantidade": 1, "quantidade_efetiva": 1,
#   "custo_unitario": 8.5, "receita_unitaria": 12, "custo_item_total": 8.5, "receita_item_total": 0,
#   "custo_total_servico": …, "receita_total_servico": …, "margem_servico_pct": …,
#   "farol_servico": "VERDE", "utiliza_no_convenio": true, "pacote_pai": true }
# receita_item_total = 0 porque o item está zerado no pacote (cada linha reflete só ela).
# Os totais/margem/farol "_servico" são do serviço (ou da subárvore, na linha de SUBSERVICO):
# leia-os na linha nivel = 0 para o serviço inteiro.

Filtros de ativo: apenasAtivosNoConvenio=true traz só itens ativos no convênio; servicoAtivo=true traz só árvores cujo serviço raiz está ativo no cadastro (o mesmo filtro que já existe em /farol/servicos). Para o relatório de conferência, combine os dois: …/farol/itens?apenasAtivosNoConvenio=true&servicoAtivo=true.

15.1 Como ler cada linha de farol/itens

Detalhes da conta: como conferir (Guia do Implantador) e árvore de decisão de preços.

16. Passo 12 — Financeiro e estoque

17. Passo 13 — Pacientes

POST /pacientes/bulk (50 por requisição, um lote por vez) · paciente:create.

Obrigatórios: nome, sexoId, contato[] (cada contato com email e celular), endereco, e cpf — salvo recém-nascido sem CPF (atendimentoRn: true + cpfMae). Convênio no mesmo corpo: convenioPlano[] (convenioId*, planoId, numeroDaCarterinha, validade). ✅ Novo — 13/10/2026 Opcional: nomeSocial (exibido nas telas como "SOCIAL (civil)" e enviado no campo Nome Social da guia/XML TISS).

curl -s -X POST "$BASE/pacientes/bulk" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "pacientes": [{
    "nome": "Maria Exemplo", "cpf": "CPF_VALIDO_AQUI", "sexoId": 2, "dataDeNascimento": "1980-05-10",
    "contato": [ { "email": "maria@exemplo.com", "celular": "61999990000" } ],
    "endereco": { "cep": "70000000", "cidade": "Brasília", "unidadeFederativaId": 7 },
    "convenioPlano": [ { "convenioId": 12, "planoId": 4, "numeroDaCarterinha": "0001234" } ]
  }]
}'

409 (no simples) = CPF já cadastrado nesta clínica; 400 = CPF inválido ou contato obrigatório ausente. Vínculos depois: POST /pacientes/{id}/convenios (sempre cria um novo) e PUT /pacientes/{id}/convenios/{vinculoId} (o vinculoId vem de GET /pacientes/{id}/convenios; parcial — campo omitido mantém). Corrigir dados do paciente: PUT /pacientes/{id} é parcial desde 01–02/10/2026 — envie só o que muda; contato e convenioPlano, se enviados, substituem a lista (o que tem id e não veio é inativado). Anexos: POST /pacientes/{id}/anexos (multipart, campo file, até 10 arquivos JPG/PNG/PDF de até 10 MB).

18. Passo 14 — Parâmetros e permissões

Por último, as regras de negócio: /parametros/orcamento (inclui desabilitarBloqueioFarol), /parametros/financeiro, /parametros/estoque (reserva de medicamento), /parametros/avisos/{chave}, /parametros/acolhimento, /parametros/servicos-online/*, /parametros/termo, alçadas de desconto e de compra por colaborador, e o controle de acesso a dashboards. Sempre faça o GET antes do PUT e reenvie o objeto completo (só /parametros/desconto declara que omitido mantém; em /parametros/financeiro, omitir categoriaPagamentoId/centroDeCustoId grava null). A chave de API nunca é tratada como administrador: níveis que exigem aprovação administrativa não são conferidos por essas rotas. Perfis e permissões de tela dos usuários ficam na tela do sistema. Referência: Referência — Parâmetros.

19. Migração de histórico

O quêComoCuidados
Pacientes em lotePOST /pacientes/bulk (50 por vez)Lotes sequenciais (429 se houver outro em andamento). Em 207, reenvie só os itens com erro.
Vínculos de convênioNo próprio corpo (convenioPlano) ou POST /pacientes/{id}/conveniosCarteirinha já em uso → 400. Para corrigir, use PUT …/convenios/{vinculoId} — não crie de novo.
AnexosPOST /pacientes/{id}/anexos (multipart)Até 10 arquivos, JPG/PNG/PDF, 10 MB cada; tipoAnexoId opcional. Não há exclusão pela API.
Atendimentos1) POST /atendimentos (pacienteId, colaboradorId, empresaId, servicoId, convenioId, dataAtendimento) → devolve atendimentoId, agendamentoId, prontuarioId
2) PUT /atendimentos/{id} com responsavelId e horarioInicio = data/hora real — repita pacienteId e empresaId (a rota não declara que omitido mantém)
O atendimento grava sempre "agora" — o passo 2 é obrigatório para a data real. Cria por trás um agendamento sintético e um prontuário vazio. Não finaliza atendimento nem baixa estoque.
ProntuárioPOST /atendimentos/prontuario (prontuarioId, conteudo); mais de uma evolução: POST /atendimentos/novo-prontuario e depois grave o textoSobrescreve sem guardar versão anterior — grave o texto final. Não há exclusão de prontuário.
ConferênciaGET /atendimentos/historico?pacienteId=…pacienteId é obrigatório nesta rota (sem ele a leitura varreria o histórico inteiro da clínica). Filtros: medico, servico, dataAtendimento.
curl -s -X POST "$BASE/atendimentos" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "pacienteId": 231, "colaboradorId": 60, "empresaId": 54, "servicoId": 45, "convenioId": 12,
  "dataAtendimento": "2025-03-14T10:00:00.000Z"
}'
# → { "atendimentoId": 4400, "agendamentoId": 8801, "prontuarioId": 9001 }
curl -s -X PUT "$BASE/atendimentos/4400" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "responsavelId": 60, "pacienteId": 231, "empresaId": 54, "horarioInicio": "2025-03-14T10:00:00.000Z"
}'
curl -s -X POST "$BASE/atendimentos/prontuario" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "prontuarioId": 9001, "conteudo": "Texto clínico migrado do sistema anterior."
}'
curl -s "$BASE/atendimentos/historico?pacienteId=231" -H "Authorization: Bearer $TOKEN"

20. Operação contínua (resumo)

ÁreaRotas principaisObservações
AgendaGET /agendamentos/datas/disponiveis, POST /agendamentos/horarios/disponiveis, GET /agendamentos/locais/disponiveis, POST /agendamentos, PATCH /agendamentos/{id}/cancelarGET /agendamentos exige data (um dia por chamada — para sincronizar vários dias, uma chamada por dia) e pagina de verdade (page/pageSize até 200; filtros pacienteId, profissionalId, statusId). Criar exige responsavelId, paciente.id, idGrade, data, statusId, procedimentos[]; sem pagamento no ato; consome o limite mensal do plano. Cancelar exige motivoCancelamentoId (GET /agendamentos/motivo-cancelamento).
OrçamentoPOST /orcamentos, PUT /orcamentos/{id}, PUT /orcamentos/status/{idOrcamento}/{idStatus}, anexosListagem GET /orcamentos paginada, com filtros id, pacienteId, pacienteCpf, pacienteNome, statusId, createdAtDe/createdAtAte (com pacienteId o formato do item muda). PUT /orcamentos/{id}: parcial (procedimentos enviado substitui a lista sem perder sessões já realizadas). Status automáticos (Vencido, Pago, Agendado, Concluído, Renegociado) são recusados (422) na troca manual de status; a troca automática ainda não está ativa (#656). Pagamento no ato permitido (pode emitir NFS-e). 422 se o Farol exigir aprovação e não houver token/pendência. A descrição da rota diz que orçamento aprovado não pode ter procedimentos/valores editados (registrar pagamento continua funcionando); na tela, desde 25/09/2026, travam a edição os status Recusado e Aprovado pela Assistência (Aprovado pelo Paciente/Convênio seguem editáveis) — trate a recusa da API como a palavra final. Financeiro único (orçamentos criados desde 25/09/2026): todas as sessões compartilham uma movimentação financeira com o valor total do orçamento; depois de pago, o valor trava (sem alterar valor nem dar desconto).
Faturamento/faturamento/guias…, /divergencias, /recebimento, /glosasPré-faturamento (edição completa da guia, confirmar), faturar diferença, registrar/devolver recebimento, gravar glosa por item. Valores: algumas leituras de faturamento devolvem centavos ou string (ex.: GET /faturamento/guias/servico/{guiaFilha}, GET /faturamento/glosas/guia/{guiaMae}) — leia a descrição da rota antes de somar ou comparar.
NFS-ePOST /nfse/notas (calcula) → POST /nfse/notas/{id}/emitir; cancelar, substituir, contingência, tomadoresCalcular não envia nada à prefeitura; idempotente via header Idempotency-Key. Emitir consome o limite mensal do plano.
Estoque/estoque/entrada, /saida, /transferencia, /saldo-produtos, /previsaoEscritas exigem responsavelId; saída e transferência exigem lote (ou null) desde 28/09/2026. GET /estoque paginado com filtros produtoId/localizacaoId.
Financeiro/financeiro/movimentacoes (+ /bulk), POST /financeiro/pagamentos/confirmacaoSem exclusão pela API; criar/editar pode disparar NFS-e automática. A confirmação de pagamento (Pix/link) é idempotente (jaConfirmado: true).

Detalhes: Referência — Operação.

21. Roteiro para IA (prompt de implantação)

Cole o bloco abaixo como instrução de um agente de IA que vai implantar ou migrar uma clínica. Ele resume a ordem e as regras de ouro desta página.

Você vai implantar a clínica no Rabi Sistemas pela API externa.
BASE = https://api.hmg.rabisistemas.dev/api/v1/integrations (ensaio) — produção só com autorização.
Header em toda chamada: Authorization: Bearer rbt_SEU_TOKEN  (recomendado: token rbt_ "Implantação e
  manutenção" da clínica; alternativa: a chave rbk_ do time Rabi). Leia o token de variável de ambiente.
  Nunca registre a chave/token em log, documento, chamado, e-mail ou conversa salva.
  A API grava direto: o "modo consulta" das telas de convênio (#632) não vale para ela.

ORDEM OBRIGATÓRIA (não pule, não inverta):
 0. GET /auxiliares/* → monte o dicionário de ids. Tipo faltando? PARE e peça ao implantador
    para criar pela tela (a API não cria auxiliares). Peça também os ids que só existem na tela
    (especialidades, categorias de pagamento, contas/caixas, centros de custo, motivos de estoque).
 1. POST /empresas                → guarde empresaId
 2. POST /depositos(/bulk)        → 1 depósito mínimo POR UNIDADE (antes dos locais!)
 3. POST /locais                  → depositoPadraoSaidaId = depósito ATIVO (senão 422)
 4. POST /operadoras/bulk, /fornecedores/bulk, /fabricantes/bulk → guarde ids (e planos)
 5. POST /taxas/bulk              → TAXAS ANTES DE CONVÊNIOS e tabelas de preço
 6. POST /produtos/bulk           → PRODUTOS ANTES DE SERVIÇOS (exige depósito e fabricante)
 7. POST /equipamentos/bulk
 8. POST /servicos                → SUBSERVIÇOS PRIMEIRO; depois os pais com servicosRelacionados
                                    taxas em servicoTaxa: [{taxaId, quantidade}] (NÃO taxaServicoId/valorTaxaServico);
                                    produtoIds/equipamentoIds/servicosRelacionados: ID ou objeto
 9. POST /colaboradores/bulk      → e POST /colaboradores/{id}/usuario para quem precisa de login
 9b. POST /tabelas-preco + /tabelas-preco/produtos/bulk
10. POST /convenios(/bulk)        → dados; depois PUT /convenios/{id}/planos, /especialidades (Atende de TODAS
                                    as especialidades cobertas ANTES de serviços/colaboradores — sem isso
                                    o upsert recusa Utiliza no serviço; #628),
                                    /taxas, /produtos, /servicos, /colaboradores (um por vez)
10b. POST /grades-colaborador (com servicos[] e convenio[]), /grades-equipamento
11. GET /convenios/{id}/farol/servicos → lista de serviços; para cada serviço a conferir:
    GET /convenios/{id}/farol/itens/{servicoRaizId} → árvore com conta_no_total e motivo_exclusao
12. Estoque (entradas com valorCompra; saída/transferência com lote ou lote: null) e financeiro
13. POST /pacientes/bulk (+ vínculos e anexos)
14. Parâmetros (GET antes de PUT)

REGRAS DE OURO:
 - Vazio ≠ zero. Sem valor = omitir ou null. 0 = zero de verdade. NUNCA use 0.01 como "sem valor".
 - Utiliza = o convênio cobre. Sem Utiliza = custo sem receita.
 - Taxas, produtos e subserviços com Utiliza SEMPRE entram na conta do serviço.
   somarItems decide só o valor próprio do serviço.
 - valorInternoConvenio sozinho NÃO fecha pacote. Preço fechado = valorInternoConvenio + pacote: true
   no serviço + zerarValor: true em cada item incluso (produtos/taxas/subserviços).
 - Zerar é por item e não desce em cascata; só age em pacote de preço fechado.
 - PUT PARCIAL (desde 01-02/10/2026) em /servicos, /convenios, /pacientes, /colaboradores, /orcamentos,
   grades, /financeiro/movimentacoes e /faturamento/guias: campo omitido MANTÉM, null LIMPA,
   lista omitida mantém, lista enviada SUBSTITUI ([] apaga). Envie só o que muda; para trocar uma
   lista, mande a lista inteira (parta do GET). Abas PUT /convenios/{id}/... = upsert (omitido mantém).
   PUT sem declaração no Swagger (produtos, taxas, equipamentos, locais, depósitos, fornecedores,
   fabricantes) e /empresas/{id}: SOBRESCRITA — faça GET e reenvie o objeto completo.
 - Lotes: máx. 50 (tabela interna 200, tomadores NFS-e 100). Um lote por vez por recurso.
   207 → reenvie SÓ os itens com ERRO/NAO_PROCESSADO. 429 → espere e tente de novo.
 - 409 → o registro já existe: busque pelo GET e reutilize o id. 422 → falta pré-requisito (ordem!).
 - 401 → chave rejeitada (ausente, errada, expirada, revogada): PARE e avise. 403 → sem permissão:
   PARE e avise qual falta (campo escopoFaltante) — com rbt_, o implantador libera em "Editar permissões". 503 → falha de rede/tempo: repita com espera progressiva (2 s, 4 s, 8 s).
 - Validade: leia o header X-ApiKey-Expires-At na 1ª resposta (rbk_ e rbt_); se vencer antes do fim do
   trabalho, avise o implantador (rbt_: Renovar na tela de Integrações — o valor antigo deixa de valer;
   rbk_: chave nova pelo time Rabi).
 - Listagens usam {dados, page, pageSize, total, totalPages}; percorra até page = totalPages (não pare
   quando dados vier menor que pageSize). Rotas cuja resposta o Swagger descreve como "Array direto"
   (ex.: /atendimentos/historico, /estoque/historico/{id}) não têm envelope.
   GET /agendamentos EXIGE data (um dia por chamada). Grades: filtre por colaboradorId/equipamentoId,
   localId e vigenteEm.
 - Valores em reais, EXCETO leituras de faturamento sinalizadas na descrição da rota (centavos ou
   string, ex.: /faturamento/guias/servico/{guiaFilha}, /faturamento/glosas/guia/{guiaMae}).
   Leia a descrição da rota antes de somar/comparar.
 - Farol: GET /convenios/{id}/farol/itens/{servicoRaizId} (um serviço, sem paginação) ou
   GET /convenios/{id}/farol/itens?apenasAtivosNoConvenio=true&servicoAtivo=true (vários).
   Cada linha traz conta_no_total, motivo_exclusao, margem_servico_pct e farol_servico.
   efetivo.totalConvenio NÃO está na API externa: o total do serviço é receita_total_servico
   (linha nivel = 0) ou receita_total em /farol/servicos.
 - DELETE /produtos/{id} ou /taxas/{id} de item em composição de serviço ativo → 409 com a lista
   em "servicos" (#626). Substitua o item nesses serviços, depois desative.
 - Produto: codigoEAN e codigoNCM como TEXTO (preserva zeros). Tabela interna: priceType1..3 em REAIS.
 - Repasse do colaborador: item sem servico/produto/taxa = linha geral (no máximo 1 por tipo).
 - Gravou algo pela API antes de 28/09/2026 17:43? Faça a CONFERÊNCIA RETROATIVA (pacientes/bulk,
   lote de preços, PUT de movimentação/fornecedor, UF, estoque sem lote) antes de seguir.
 - 404 em rota que deveria existir = caminho/método errado (desde 26/09 rota inexistente dá 404,
   não mais 401 "jwt malformed"). 404 com rota certa = id não existe nesta clínica.
 - Ao final, entregue: tabela de ids criados, itens com erro e o resultado do Farol por convênio
   (liste linhas vermelhas e roxas com o motivo provável).

21.1 Checklist de fechamento

Continue por aqui: Visão geral · Referência — Cadastros · Erros e boas práticas · Guia de Implantação (pela tela) · Preços — Guia do Implantador