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
1curl -X POST https://landing-zone-api.nilo.services/fhir/resources/Coverage \
2 -H "x-api-key: <apiKey>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "beneficiary": {
6 "identifier": {
7 "system": "https://www.acmesaude.com.br/integracao/paciente/",
8 "value": "507823709",
9 "use": "usual"
10 },
11 "type": "Patient"
12 },
13 "payor": [
14 {
15 "display": "Operadora"
16 }
17 ],
18 "resourceType": "Coverage",
19 "status": "active",
20 "class": [
21 {
22 "type": {
23 "text": "plan"
24 },
25 "value": "42",
26 "name": "Unimed com coparticipação SP/SP"
27 }
28 ],
29 "identifier": [
30 {
31 "system": "https://www.acmesaude.com.br/integracao/cobertura/",
32 "value": "COB-88231",
33 "use": "official"
34 }
35 ],
36 "order": 1,
37 "period": {
38 "end": "2023-05-23",
39 "start": "2022-05-23"
40 },
41 "policyHolder": {
42 "display": "Acme Indústria S.A."
43 },
44 "relationship": {
45 "coding": [
46 {
47 "code": "self"
48 }
49 ]
50 },
51 "subscriberId": "1352743"
52}'

A resposta devolve o recurso como ficou gravado:

Response
1{
2 "beneficiary": {
3 "identifier": {
4 "system": "https://www.acmesaude.com.br/integracao/paciente/",
5 "value": "507823709",
6 "use": "usual"
7 },
8 "type": "Patient",
9 "reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
10 },
11 "payor": [
12 {
13 "display": "Operadora"
14 }
15 ],
16 "resourceType": "Coverage",
17 "status": "active",
18 "class": [
19 {
20 "type": {
21 "text": "plan"
22 },
23 "value": "42",
24 "name": "Unimed com coparticipação SP/SP"
25 }
26 ],
27 "id": "7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73",
28 "identifier": [
29 {
30 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/hippocrates-api--coverage",
31 "value": "918233",
32 "use": "usual"
33 },
34 {
35 "system": "https://www.acmesaude.com.br/integracao/cobertura/",
36 "value": "COB-88231",
37 "use": "official"
38 }
39 ],
40 "meta": {
41 "lastUpdated": "2026-08-06T13:04:12.905000Z",
42 "versionId": "MTc4NjAyMTQ1MjkwNTAwMDM2Nw"
43 },
44 "order": 1,
45 "period": {
46 "end": "2023-05-23",
47 "start": "2022-05-23"
48 },
49 "policyHolder": {
50 "display": "Acme Indústria S.A."
51 },
52 "relationship": {
53 "coding": [
54 {
55 "code": "self"
56 }
57 ]
58 },
59 "subscriberId": "1352743"
60}

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
1curl -X POST https://landing-zone-api.nilo.services/fhir/resources/Coverage \
2 -H "x-api-key: <apiKey>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "beneficiary": {
6 "identifier": {
7 "system": "https://www.acmesaude.com.br/integracao/paciente/",
8 "value": "507823710",
9 "use": "usual"
10 },
11 "type": "Patient"
12 },
13 "payor": [
14 {
15 "display": "Operadora"
16 }
17 ],
18 "resourceType": "Coverage",
19 "status": "active",
20 "class": [
21 {
22 "type": {
23 "text": "plan"
24 },
25 "value": "42",
26 "name": "Unimed com coparticipação SP/SP"
27 }
28 ],
29 "dependent": "1352744",
30 "identifier": [
31 {
32 "system": "https://www.acmesaude.com.br/integracao/cobertura/",
33 "value": "COB-88232",
34 "use": "official"
35 }
36 ],
37 "order": 2,
38 "relationship": {
39 "coding": [
40 {
41 "code": "child"
42 }
43 ]
44 },
45 "subscriberId": "1352743"
46}'

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
1curl -G https://landing-zone-api.nilo.services/fhir/resources/InsurancePlan \
2 -H "x-api-key: <apiKey>" \
3 -d _count=50 \
4 -d _lastUpdated=eq2013-01-14 \
5 --data-urlencode _page_token=Cjj3YopYuf%2F%2F%2F%2F%2BABd%2BbgE0m...dSANQAFoLCUSM7w9VYUqaEANglLWUugQ%3D \
6 --data-urlencode identifier=https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/care-api--insurance-v2|2481 \
7 -d name=Ambulatorial \
8 -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
1curl -X POST https://landing-zone-api.nilo.services/fhir/resources/Coverage \
2 -H "x-api-key: <apiKey>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "beneficiary": {
6 "identifier": {
7 "system": "https://www.acmesaude.com.br/integracao/paciente/",
8 "value": "507823711",
9 "use": "usual"
10 },
11 "type": "Patient"
12 },
13 "payor": [
14 {
15 "display": "Operadora"
16 }
17 ],
18 "resourceType": "Coverage",
19 "status": "active",
20 "extension": [
21 {
22 "url": "https://landing-zone-api.nilo.services/fhir/resources/StructureDefinition/insurance-plan",
23 "valueIdentifier": {
24 "system": "https://www.acmesaude.com.br/integracao/plano/",
25 "use": "usual",
26 "value": "unimed-coparticipacao-sp"
27 }
28 }
29 ],
30 "identifier": [
31 {
32 "system": "https://www.acmesaude.com.br/integracao/cobertura/",
33 "value": "COB-88233",
34 "use": "official"
35 }
36 ],
37 "relationship": {
38 "coding": [
39 {
40 "code": "self"
41 }
42 ]
43 },
44 "subscriberId": "1352745"
45}'

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
1curl -G https://landing-zone-api.nilo.services/fhir/resources/Coverage \
2 -H "x-api-key: <apiKey>" \
3 -d _lastUpdated=eq2013-01-14 \
4 --data-urlencode _page_token=Cjj3YopYuf%2F%2F%2F%2F%2BABd%2BbgE0m...dSANQAFoLCUSM7w9VYUqaEANglLWUugQ%3D \
5 --data-urlencode beneficiary=Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4 \
6 -d class-type=plan \
7 -d class-value=42 \
8 -d dependent=1352744 \
9 --data-urlencode identifier=https://www.acmesaude.com.br/integracao/cobertura/|COB-88231 \
10 --data-urlencode patient=Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4 \
11 --data-urlencode patient:identifier=https://www.acmesaude.com.br/integracao/paciente/|507823709 \
12 --data-urlencode policy-holder=Organization/6e6a1b40-1e2a-4f1e-9f0e-2b1c3d4e5f60 \
13 -d status=active

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

Response
1{
2 "resourceType": "Bundle",
3 "type": "searchset",
4 "entry": [
5 {
6 "fullUrl": "https://landing-zone-api.nilo.services/fhir/resources/Coverage/7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73",
7 "resource": {
8 "beneficiary": {
9 "identifier": {
10 "system": "https://www.acmesaude.com.br/integracao/paciente/",
11 "value": "507823709",
12 "use": "usual"
13 },
14 "type": "Patient",
15 "reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
16 },
17 "payor": [
18 {
19 "display": "Operadora"
20 }
21 ],
22 "resourceType": "Coverage",
23 "status": "active",
24 "class": [
25 {
26 "type": {
27 "text": "plan"
28 },
29 "value": "42",
30 "name": "Unimed com coparticipação SP/SP"
31 }
32 ],
33 "id": "7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73",
34 "identifier": [
35 {
36 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/hippocrates-api--coverage",
37 "value": "918233",
38 "use": "usual"
39 },
40 {
41 "system": "https://www.acmesaude.com.br/integracao/cobertura/",
42 "value": "COB-88231",
43 "use": "official"
44 }
45 ],
46 "order": 1,
47 "period": {
48 "start": "2022-05-23"
49 },
50 "relationship": {
51 "coding": [
52 {
53 "code": "self"
54 }
55 ]
56 },
57 "subscriberId": "1352743"
58 },
59 "search": {
60 "mode": "match"
61 }
62 }
63 ],
64 "link": []
65}

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
1{
2 "resourceType": "Bundle",
3 "type": "searchset",
4 "entry": [],
5 "link": []
6}

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
1curl https://landing-zone-api.nilo.services/fhir/resources/Coverage/7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73 \
2 -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
1{
2 "beneficiary": {
3 "identifier": {
4 "system": "https://www.acmesaude.com.br/integracao/paciente/",
5 "value": "507823709",
6 "use": "usual"
7 },
8 "type": "Patient",
9 "reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
10 },
11 "payor": [
12 {
13 "display": "Operadora"
14 }
15 ],
16 "resourceType": "Coverage",
17 "status": "active",
18 "class": [
19 {
20 "type": {
21 "text": "plan"
22 },
23 "value": "42",
24 "name": "Unimed com coparticipação SP/SP"
25 }
26 ],
27 "id": "7f2b91c4-3d0a-4e8b-9c11-5a6d8e2f0b73",
28 "identifier": [
29 {
30 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/hippocrates-api--coverage",
31 "value": "918233",
32 "use": "usual"
33 },
34 {
35 "system": "https://www.acmesaude.com.br/integracao/cobertura/",
36 "value": "COB-88231",
37 "use": "official"
38 }
39 ],
40 "meta": {
41 "lastUpdated": "2026-08-06T13:04:12.905000Z",
42 "versionId": "MTc4NjAyMTQ1MjkwNTAwMDM2Nw"
43 },
44 "order": 1,
45 "relationship": {
46 "coding": [
47 {
48 "code": "self"
49 }
50 ]
51 },
52 "subscriberId": "1352743"
53}

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

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
1{
2 "issue": [
3 {
4 "code": "not-found",
5 "details": {
6 "text": "Patient does not exist"
7 },
8 "expression": [
9 "Coverage.beneficiary"
10 ],
11 "severity": "error"
12 }
13 ],
14 "resourceType": "OperationOutcome"
15}

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
1{
2 "issue": [
3 {
4 "code": "not-found",
5 "details": {
6 "text": "Patient does not exist"
7 },
8 "expression": [
9 "Coverage.beneficiary"
10 ],
11 "severity": "error"
12 }
13 ],
14 "resourceType": "OperationOutcome"
15}

Toda escrita precisa de ao menos um identifier:

Response
1{
2 "issue": [
3 {
4 "code": "not-found",
5 "details": {
6 "text": "Patient does not exist"
7 },
8 "expression": [
9 "Coverage.beneficiary"
10 ],
11 "severity": "error"
12 }
13 ],
14 "resourceType": "OperationOutcome"
15}

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
1{
2 "issue": [
3 {
4 "code": "not-found",
5 "details": {
6 "text": "Patient does not exist"
7 },
8 "expression": [
9 "Coverage.beneficiary"
10 ],
11 "severity": "error"
12 }
13 ],
14 "resourceType": "OperationOutcome"
15}

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.