Skip to navigation

Cobertura de saúde

O Coverage representa o vínculo do paciente com um plano de saúde, convênio ou benefício — quem custeia o atendimento, com que carteirinha e por quanto tempo. Um paciente pode ter várias coberturas ao mesmo tempo, e a ordem entre elas diz qual usar primeiro.

A cobertura não vive sozinha: ela sempre aponta para um Patient já cadastrado, e o plano que ela nomeia é um InsurancePlan do catálogo do seu care provider. Nenhum dos dois é criado pela escrita da cobertura.

Campos

A coluna No NiloCare traz o rótulo com que o dado aparece para a equipe de cuidado. São rótulos, não caminhos de navegação: a tela pode ser reorganizada, o rótulo é o que permite conferir se o dado chegou onde você esperava.

CampoObrigatórioO que significaNo NiloCare
identifiersim, ao menos umChaves da cobertura. É por aqui que a API decide entre criar e atualizar—
beneficiarysimPaciente coberto. Aponta para um Patient que já existe(a cobertura fica na ficha dele)
relationshipsimRelação do beneficiário com o titular da carteirinha, em coding[0].codeRelação com titular
statussimSituação da coberturaSituação
payorsim, com ressalva 1Entidade pagadora. Exigido pelo FHIR, mas o conteúdo é descartado—
subscriberIdsim, com ressalva 2Carteirinha do titular. Num beneficiário titular, é a carteirinha dele mesmoNº da carteirinha / Nº da cart. do titular
dependentsim, com ressalva 2Carteirinha do beneficiário quando ele é dependenteNº da carteirinha
class[0].valuesim, com ressalva 2Id do plano no catálogo do seu care providerPlano de saúde
class[0].namenãoNome do plano. Informativo — não localiza o planoPlano de saúde
policyHolder.displaynãoEstipulante: quem contratou a apólice. Texto livreEstipulante
period.startnãoInício de vigênciaInício de vigência
period.endnãoFim de vigênciaFim de vigência
ordernãoPrioridade entre as coberturas do paciente, começando em 1ordem dos cartões Cobertura de saúde 1, 2, …
extension insurance-plannãoPlano por identificador, alternativa ao classPlano de saúde

1 payor é obrigatório na especificação FHIR R4 e um payload sem ele é recusado, mas o valor enviado não é gravado: a leitura devolve sempre Operadora.
2 Nenhum destes três é obrigatório isoladamente, mas a cobertura precisa de um plano ou uma carteirinha — os dois vazios recusam a escrita. Qual dos dois campos de carteirinha usar depende do relationship; veja Titular e dependente.

A URL completa da extensão está em Extensões — nesta página ela aparece só pelo nome final.

Relação com titular aparece como opcional no formulário do NiloCare, mas relationship é obrigatório na API: sem ele a escrita é recusada. Se a sua carga não distingue titular de dependente, envie self.

Campos do Coverage que a Nilo não usa

O Coverage canônico tem campos que esta integração não lê nem grava: type, subscriber, network, contract, costToBeneficiary e subrogation. Eles ficam fora da referência de propósito. Enviá-los não é erro — o valor é gravado no recurso FHIR e volta nas leituras seguintes —, mas nada no NiloCare passa a exibi-los.

Cadastrar ou atualizar

Não há endpoint separado para criar e atualizar. O mesmo POST faz os dois, e quem decide é o identifier: se já existir uma cobertura com aquele par system + value, ela é atualizada; se não existir, é criada.

POST
/fhir/resources/Coverage
curl -X POST https://landing-zone-api.nilo.services/fhir/resources/Coverage \
-H "x-api-key: <apiKey>" \
-H "Content-Type: application/json" \
-d '{
"beneficiary": {
"identifier": {
"system": "https://www.acmesaude.com.br/integracao/paciente/",
"value": "507823709",
"use": "usual"
},
"type": "Patient"
},
"payor": [
{
"display": "Operadora"
}
],
"resourceType": "Coverage",
"status": "active",
"class": [
{
"type": {
"text": "plan"
},
"value": "42",
"name": "Unimed com coparticipação SP/SP"
}
],
"identifier": [
{
"system": "https://www.acmesaude.com.br/integracao/cobertura/",
"value": "COB-88231",
"use": "official"
}
],
"order": 1,
"period": {
"end": "2023-05-23",
"start": "2022-05-23"
},
"policyHolder": {
"display": "Acme Indústria S.A."
},
"relationship": {
"coding": [
{
"code": "self"
}
]
},
"subscriberId": "1352743"
}'

A resposta devolve o recurso como ficou gravado:

Response
{
"beneficiary": {
"identifier": {
"system": "https://www.acmesaude.com.br/integracao/paciente/",
"value": "507823709",
"use": "usual"
},
"type": "Patient",
"reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
},
"payor": [
{
"display": "Operadora"
}
],
"resourceType": "Coverage",
"status": "active",
"class": [
{
"type": {
"text": "plan"
},
"value": "42",
"name": "Unimed com coparticipação SP/SP"
}
],
"id": "7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73",
"identifier": [
{
"system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/hippocrates-api--coverage",
"value": "918233",
"use": "usual"
},
{
"system": "https://www.acmesaude.com.br/integracao/cobertura/",
"value": "COB-88231",
"use": "official"
}
],
"meta": {
"lastUpdated": "2026-08-06T13:04:12.905000Z",
"versionId": "MTc4NjAyMTQ1MjkwNTAwMDM2Nw"
},
"order": 1,
"period": {
"end": "2023-05-23",
"start": "2022-05-23"
},
"policyHolder": {
"display": "Acme Indústria S.A."
},
"relationship": {
"coding": [
{
"code": "self"
}
]
},
"subscriberId": "1352743"
}

Guarde o id: é por ele que se faz a leitura direta da cobertura. A resposta traz também o identificador Nilo da cobertura (…/NamingSystem/hippocrates-api--coverage) ao lado do seu — os dois servem para buscar, e o seu continua sendo o único que você precisa guardar.

A atualização de cobertura substitui, não complementa. Diferente do Patient, um campo omitido não preserva o valor atual — a API grava a cobertura com o que o payload traz, e o que ficou de fora é enviado vazio. Para mudar só a vigência, reenvie o payload inteiro com a vigência nova, não apenas o period.

Ao menos um identifier é obrigatório. Não havendo casamento por identificador, a API ainda tenta encontrar a cobertura pela chave de negócio antes de criar uma nova — veja Como a API evita duplicatas.

Titular e dependente

subscriberId e dependent trocam de sentido conforme o relationship. É a fonte de erro mais comum neste recurso.

relationshipsubscriberIddependent
selfcarteirinha do beneficiárionão use — a leitura devolve nulo
qualquer outrocarteirinha do titularcarteirinha do beneficiário

Ou seja: a carteirinha do paciente que você está cadastrando vai em subscriberId quando ele é o titular e em dependent quando não é.

POST
/fhir/resources/Coverage
curl -X POST https://landing-zone-api.nilo.services/fhir/resources/Coverage \
-H "x-api-key: <apiKey>" \
-H "Content-Type: application/json" \
-d '{
"beneficiary": {
"identifier": {
"system": "https://www.acmesaude.com.br/integracao/paciente/",
"value": "507823710",
"use": "usual"
},
"type": "Patient"
},
"payor": [
{
"display": "Operadora"
}
],
"resourceType": "Coverage",
"status": "active",
"class": [
{
"type": {
"text": "plan"
},
"value": "42",
"name": "Unimed com coparticipação SP/SP"
}
],
"dependent": "1352744",
"identifier": [
{
"system": "https://www.acmesaude.com.br/integracao/cobertura/",
"value": "COB-88232",
"use": "official"
}
],
"order": 2,
"relationship": {
"coding": [
{
"code": "child"
}
]
},
"subscriberId": "1352743"
}'

Trocar os dois campos de lugar não gera erro: a escrita passa e a carteirinha fica gravada no campo errado. Confira o relationship antes de montar a carga.

Plano de saúde

O plano vai no primeiro item de class, com o id do plano em value. O name é informativo: quem localiza o plano é o value.

A API lê class[0] — o primeiro item da lista, sem olhar o type. Se o seu payload mandar outra classificação antes do plano, é o value dela que será tomado como id do plano. Envie o plano como primeiro item, ou envie só ele.

Para descobrir os ids disponíveis, liste os planos do seu ambiente e use o value do identificador Nilo (…/NamingSystem/care-api--insurance-v2) — veja Plano de saúde:

GET
/fhir/resources/InsurancePlan
curl -G https://landing-zone-api.nilo.services/fhir/resources/InsurancePlan \
-H "x-api-key: <apiKey>" \
-d _count=50 \
-d _lastUpdated=eq2013-01-14 \
--data-urlencode identifier=https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/care-api--insurance-v2|2481 \
-d name=Ambulatorial \
-d status=active

Quem não quer carregar o id Nilo do plano pode usar a extensão insurance-plan, que aceita qualquer identificador de um InsurancePlan já cadastrado — inclusive o do seu próprio sistema:

POST
/fhir/resources/Coverage
curl -X POST https://landing-zone-api.nilo.services/fhir/resources/Coverage \
-H "x-api-key: <apiKey>" \
-H "Content-Type: application/json" \
-d '{
"beneficiary": {
"identifier": {
"system": "https://www.acmesaude.com.br/integracao/paciente/",
"value": "507823711",
"use": "usual"
},
"type": "Patient"
},
"payor": [
{
"display": "Operadora"
}
],
"resourceType": "Coverage",
"status": "active",
"extension": [
{
"url": "https://landing-zone-api.nilo.services/fhir/resources/StructureDefinition/insurance-plan",
"valueIdentifier": {
"system": "https://www.acmesaude.com.br/integracao/plano/",
"use": "usual",
"value": "unimed-coparticipacao-sp"
}
}
],
"identifier": [
{
"system": "https://www.acmesaude.com.br/integracao/cobertura/",
"value": "COB-88233",
"use": "official"
}
],
"relationship": {
"coding": [
{
"code": "self"
}
]
},
"subscriberId": "1352745"
}'

Enviando as duas formas ao mesmo tempo, class vence e a extensão é ignorada. E a extensão não volta na leitura: a resposta traz o plano em class[0]. Reenviar um recurso exatamente como ele veio numa resposta funciona — mas pelo class, não pela extensão.

class[0].name é uma fotografia, não o nome atual do plano. Ele guarda o nome que o plano tinha quando esta cobertura foi gravada pela última vez. Se o plano for renomeado depois, a cobertura continua devolvendo o nome antigo até ser gravada de novo, por qualquer motivo.

Quem localiza o plano é o class[0].value, e esse continua correto. Para o nome atual, leia o plano de saúde — não o name da cobertura.

Vigência

period.start e period.end são datas. Envie-as como data pura (YYYY-MM-DD): o NiloCare guarda só a data, e uma data com hora é convertida para o fuso da Nilo antes de a data ser extraída — o que pode deslocar o dia em um valor próximo da meia-noite.

Ordem

order é a prioridade de uso entre as coberturas do paciente e começa em 1: order: 1 é a primeira cobertura, a que aparece como Cobertura de saúde 1 na ficha. order: 0 é recusado pela validação FHIR, que exige um inteiro positivo.

Uma cobertura gravada sem order é lida como order: 1. Com mais de uma cobertura por paciente, mande o order de todas explicitamente — do contrário todas voltam como primeira.

Buscar

GET
/fhir/resources/Coverage
curl -G https://landing-zone-api.nilo.services/fhir/resources/Coverage \
-H "x-api-key: <apiKey>" \
-d _lastUpdated=eq2013-01-14 \
--data-urlencode beneficiary=Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4 \
-d class-type=plan \
-d class-value=42 \
-d dependent=1352744 \
--data-urlencode identifier=https://www.acmesaude.com.br/integracao/cobertura/|COB-88231 \
--data-urlencode patient=Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4 \
--data-urlencode patient:identifier=https://www.acmesaude.com.br/integracao/paciente/|507823709 \
--data-urlencode policy-holder=Organization/6e6a1b40-1e2a-4f1e-9f0e-2b1c3d4e5f60 \
-d status=active

A resposta é sempre um Bundle do tipo searchset, mesmo quando há um único resultado:

Response
{
"resourceType": "Bundle",
"type": "searchset",
"entry": [
{
"fullUrl": "https://landing-zone-api.nilo.services/fhir/resources/Coverage/7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73",
"resource": {
"beneficiary": {
"identifier": {
"system": "https://www.acmesaude.com.br/integracao/paciente/",
"value": "507823709",
"use": "usual"
},
"type": "Patient",
"reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
},
"payor": [
{
"display": "Operadora"
}
],
"resourceType": "Coverage",
"status": "active",
"class": [
{
"type": {
"text": "plan"
},
"value": "42",
"name": "Unimed com coparticipação SP/SP"
}
],
"id": "7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73",
"identifier": [
{
"system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/hippocrates-api--coverage",
"value": "918233",
"use": "usual"
},
{
"system": "https://www.acmesaude.com.br/integracao/cobertura/",
"value": "COB-88231",
"use": "official"
}
],
"order": 1,
"period": {
"start": "2022-05-23"
},
"relationship": {
"coding": [
{
"code": "self"
}
]
},
"subscriberId": "1352743"
},
"search": {
"mode": "match"
}
}
],
"link": []
}

Busca sem resultados não é erro: volta 200 com um Bundle cujo entry é uma lista vazia. Trate a ausência de resultados pela lista vazia, não esperando um 404.

Response
{
"resourceType": "Bundle",
"type": "searchset",
"entry": [],
"link": []
}

Parâmetros de busca suportados

NomeTipoDescriçãoExpressão
identifiertokenIdentificador da cobertura, em system|valueCoverage.identifier
patient:identifierreferencePaciente coberto, pelo identificador dele, em system|valueCoverage.beneficiary.identifier
patientreferencePaciente coberto, pelo id Nilo FHIRCoverage.beneficiary
beneficiaryreferenceO mesmo campo que patient, e aceita o mesmo :identifierCoverage.beneficiary
statustokenSituação da coberturaCoverage.status
dependentstringCarteirinha de um beneficiário dependenteCoverage.dependent
class-valuestringId do planoCoverage.class.value
class-typetokenTipo da classificação — a Nilo grava sempre planCoverage.class.type
policy-holderreferenceEstipulanteCoverage.policyHolder

A busca do dia a dia é a lista de coberturas de um paciente. Com o modificador :identifier ela usa a chave que você já tem no seu sistema, sem precisar guardar o id Nilo FHIR do paciente:

curl --request GET \
--url 'https://landing-zone-api.nilo.services/fhir/resources/Coverage?patient:identifier=https://www.acmesaude.com.br/integracao/paciente/|507823709' \
--header 'x-api-key: SUA_API_KEY'

Qual identificador do paciente o beneficiary carrega depende da sua implantação. Na configuração que usa identificadores externos nas referências, é o identificador do seu sistema — e o filtro acima funciona como está. Sem ela, o beneficiary traz o identificador Nilo do paciente (…/NamingSystem/hippocrates-api--patient), e é esse system que você tem de usar no filtro. Confira numa resposta de leitura qual dos dois está lá antes de montar a busca em volume.

Dois parâmetros canônicos do Coverage existem mas praticamente não encontram nada aqui. policy-holder casa por referência, e a Nilo grava o estipulante apenas como texto em policyHolder.display. E o FHIR R4 não define parâmetro para subscriberId: a carteirinha de um titular não é filtrável — só a de um dependente, por dependent.

Ler por ID

GET
/fhir/resources/Coverage/:id
curl https://landing-zone-api.nilo.services/fhir/resources/Coverage/7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73 \
-H "x-api-key: <apiKey>"

Diferente da busca, a leitura por ID devolve o recurso direto, sem envelope Bundle, e responde 404 quando o id não existe.

Response
{
"beneficiary": {
"identifier": {
"system": "https://www.acmesaude.com.br/integracao/paciente/",
"value": "507823709",
"use": "usual"
},
"type": "Patient",
"reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
},
"payor": [
{
"display": "Operadora"
}
],
"resourceType": "Coverage",
"status": "active",
"class": [
{
"type": {
"text": "plan"
},
"value": "42",
"name": "Unimed com coparticipação SP/SP"
}
],
"id": "7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73",
"identifier": [
{
"system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/hippocrates-api--coverage",
"value": "918233",
"use": "usual"
},
{
"system": "https://www.acmesaude.com.br/integracao/cobertura/",
"value": "COB-88231",
"use": "official"
}
],
"meta": {
"lastUpdated": "2026-08-06T13:04:12.905000Z",
"versionId": "MTc4NjAyMTQ1MjkwNTAwMDM2Nw"
},
"order": 1,
"relationship": {
"coding": [
{
"code": "self"
}
]
},
"subscriberId": "1352743"
}

Valores aceitos

relationship.coding[0].code usa o vocabulário subscriber-relationship do FHIR R4. Os sete códigos têm rótulo no NiloCare:

CódigoNo NiloCare
selfPróprio
childFilho
parentPais
spouseCônjuge (Civil)
commonCônjuge (União estável)
injuredExtensão a terceiros
otherOutro

Só self tem efeito sobre o comportamento da API — é ele que decide qual campo carrega a carteirinha do beneficiário. Os outros seis são equivalentes entre si nesse aspecto.

status usa os quatro códigos do FHIR R4:

CódigoNo NiloCare
activeAtivo
draftEm contratação
cancelledCancelado
entered-in-errorInválido

Numa cobertura criada pela interface e nunca escrita por esta API o status pode estar vazio do lado Nilo. Nesse caso a leitura o deriva da vigência: cancelled quando o fim de vigência já passou, active nos outros casos. A escrita nunca deriva — o status que você manda é o que fica gravado.

O catálogo de planos não é um vocabulário fixo: cada care provider tem os seus, cadastrados na implantação. Liste-os com GET /fhir/resources/InsurancePlan, como mostrado em Plano de saúde, acima.

Como a API evita duplicatas

O caminho normal é o identifier, e ele é obrigatório. Mas quando nenhum dos identificadores enviados encontra uma cobertura, a API tenta uma segunda vez pela chave de negócio — a combinação de:

  • carteirinha do beneficiário (subscriberId num titular, dependent num dependente);
  • paciente (beneficiary);
  • plano (class[0].value ou a extensão insurance-plan).

Havendo cobertura com os três iguais, ela é atualizada e passa a carregar também o seu identificador. Do contrário, uma nova é criada.

Isso é o que torna seguro integrar coberturas que já existiam no NiloCare antes da integração: a primeira escrita reconhece a cobertura pela carteirinha em vez de duplicá-la. A chave só funciona com os três valores presentes — faltando qualquer um, a API cria uma cobertura nova.

Efeitos colaterais

A escrita de Coverage grava apenas a cobertura. Ela não cria nem altera o paciente, não cadastra planos e não dispara mensagens.

Este endpoint não remove coberturas: a escrita nunca responde 204. A remoção acontece do lado Nilo e é propagada para o store FHIR pela sincronização, não por esta API.

Erros

Recusa é sempre 400 com um OperationOutcome: issue[].expression aponta o campo culpado e issue[].details.text explica o motivo.

O paciente do beneficiary precisa existir. Cadastre-o pelo Patient antes — a cobertura não o cria:

Response
{
"issue": [
{
"code": "not-found",
"details": {
"text": "Patient does not exist"
},
"expression": [
"Coverage.beneficiary"
],
"severity": "error"
}
],
"resourceType": "OperationOutcome"
}

relationship é obrigatório, e o código tem de estar em coding[0].code. Mandar o campo sem coding, ou o coding sem code, dá o mesmo erro que omiti-lo:

Response
{
"issue": [
{
"code": "not-found",
"details": {
"text": "Patient does not exist"
},
"expression": [
"Coverage.beneficiary"
],
"severity": "error"
}
],
"resourceType": "OperationOutcome"
}

Toda escrita precisa de ao menos um identifier:

Response
{
"issue": [
{
"code": "not-found",
"details": {
"text": "Patient does not exist"
},
"expression": [
"Coverage.beneficiary"
],
"severity": "error"
}
],
"resourceType": "OperationOutcome"
}

E a cobertura precisa de um plano ou de uma carteirinha. Os dois vazios são recusados — a mesma regra que o formulário do NiloCare aplica ao exigir “o plano de saúde ou número da carteirinha”:

Response
{
"issue": [
{
"code": "not-found",
"details": {
"text": "Patient does not exist"
},
"expression": [
"Coverage.beneficiary"
],
"severity": "error"
}
],
"resourceType": "OperationOutcome"
}

Esse último erro chega da camada de validação sem ser reformulado, e por isso o details.text é técnico e o code é o genérico exception. Trate-o pela menção a card_number e insurance_id na mensagem, não pelo texto exato — ele pode mudar.

payor e status são obrigatórios pela especificação FHIR R4, então um payload sem eles é recusado antes de chegar às regras acima, com um OperationOutcome de código structure apontando o campo que falta.

O que a integração não cobre

O plano de saúde do paciente não fica no Patient — é sempre um Coverage à parte, ligado a ele por beneficiary. E o catálogo de planos em si não é criado por esta API: os planos são cadastrados na implantação, e o que a integração faz com eles é ler e, quando preciso, corrigir o nome — veja Plano de saúde.