Local de atendimento

Um local de atendimento é o espaço físico onde o cuidado acontece: o hospital, a clínica, o ambulatório. Ele guarda três coisas — o nome, o código CNES e o endereço — e existe para que todo mundo saiba onde um evento de saúde aconteceu ou vai acontecer.

No Nilo Care esses registros ficam em Estabelecimentos de saúde, nas configurações do ambiente, visíveis para quem tem perfil de gestor. É lá que aparecem os campos Nome, Código CNES e o bloco Endereço. O mesmo cadastro aparece como Locais de atendimento no formulário do profissional, onde se escolhe onde cada um atende.

No FHIR o recurso é o Location.

Campos

A coluna No Nilo Care traz o rótulo com que o dado aparece para a equipe; são rótulos, não caminhos de navegação.

CampoObrigatórioO que significaNo Nilo Care
resourceTypesimConstante Location
identifiersimSuas chaves do local. É por elas que a API decide entre criar e atualizar
identifier com system https://cnes.datasus.gov.br/nãoTratado à parte: o value vira o código CNES do estabelecimentoCódigo CNES
namesimNome do estabelecimento, com no máximo 200 caracteresNome
addressnãoEndereço do estabelecimentoseção Endereço
address.linesim, se houver addressLogradouro, número e complemento, por posição
address.line[0]LogradouroRua
address.line[1]Número, com no máximo 10 caracteresNúmero
address.line[2]ComplementoComplemento
address.districtnãoBairroBairro
address.citynãoCidadeCidade
address.statenãoEstado, por extenso ou pela siglaEstado (UF)
address.countrynãoPaísPaís
address.postalCodesim, se houver addressCEPCEP
address.text · address.use · address.typenãoSó resposta: o endereço em uma linha, e as constantes work e both
id · metanãoSó resposta: identificador Nilo FHIR do local e metadados da gravação

Enviando address, o line e o postalCode vão junto. São os dois campos sem os quais a chamada é recusada; bairro, cidade, estado e país são opcionais. Se você não tem o CEP ou o logradouro, é melhor omitir o address inteiro numa criação — mas veja o aviso sobre atualização logo abaixo.

Um address sem line não é recusado com uma mensagem de campo: cai num erro genérico (code: exception), que é a forma mais difícil de diagnosticar. Mande sempre o line.

Campos que a Nilo não usa

O Location canônico traz muito mais do que esta integração lê: status, operationalStatus, alias, description, mode, type, telecom, physicalType, position, managingOrganization, partOf, hoursOfOperation, availabilityExceptions e endpoint. Nenhum deles é lido, e por isso nenhum aparece na referência do recurso.

A referência lista só os campos suportados, e é assim que ela deve ser lida: o que mandar na escrita. A API em si não recusa quem manda os outros — eles ficam guardados no recurso e voltam nas leituras seguintes, sem nunca terem significado nada para a plataforma. Um telecom enviado assim parece o telefone do estabelecimento, e não é: o contato do estabelecimento existe no Nilo Care — Celular, E-mail, Telefone comercial, Site —, mas não é exposto nem gravável por esta API.

Isso vale para os campos de topo. Dentro de address, não: o endereço é remontado inteiro a cada leitura, e o que a Nilo não usa é descartado. Um address.period, um line[3] ou um address.use que você envie somem sem aviso.

Cadastrar ou atualizar

POST
/fhir/resources/Location
1curl -X POST https://landing-zone-api.nilo.services/fhir/resources/Location \
2 -H "x-api-key: <apiKey>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "resourceType": "Location",
6 "identifier": [
7 {
8 "system": "https://www.acmesaude.com.br/integracao/local/",
9 "value": "HC-01",
10 "use": "usual"
11 }
12 ],
13 "name": "Hospital Central Acme",
14 "address": {
15 "line": [
16 "Rua Tamandaré",
17 "321",
18 "Casa 2"
19 ],
20 "postalCode": "01503-001",
21 "district": "Liberdade",
22 "city": "São Paulo",
23 "state": "SP",
24 "country": "Brasil"
25 }
26}'

A resposta é o recurso gravado, sem envelope:

Response
1{
2 "resourceType": "Location",
3 "identifier": [
4 {
5 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/almanac-api--health-facility",
6 "value": "295",
7 "use": "usual"
8 },
9 {
10 "system": "https://cnes.datasus.gov.br/",
11 "value": "2077485",
12 "use": "official"
13 },
14 {
15 "system": "https://www.acmesaude.com.br/integracao/local/",
16 "value": "HC-01",
17 "use": "usual"
18 }
19 ],
20 "name": "Hospital Central Acme",
21 "id": "8b53f107-2d94-4c6e-a71f-9042e5b8c316",
22 "meta": {
23 "lastUpdated": "2026-08-24T13:42:07.554000Z",
24 "versionId": "MTc4NjAyMTQ1MjkwNTAwMDM2Nw"
25 },
26 "address": {
27 "line": [
28 "Rua Tamandaré",
29 "321",
30 "Casa 2"
31 ],
32 "postalCode": "01503-001",
33 "district": "Liberdade",
34 "city": "São Paulo",
35 "state": "SP",
36 "country": "Brasil",
37 "text": "Rua Tamandaré, 321, Casa 2 - Liberdade, São Paulo/SP. 01503-001.",
38 "use": "work",
39 "type": "both"
40 }
41}

Guarde o id: é por ele que se faz a leitura direta. Ao lado das suas chaves, a resposta traz o identificador Nilo do local, no system …/NamingSystem/almanac-api--health-facility.

O endereço é posicional

address.line não é texto livre: as três posições têm significado fixo.

PosiçãoSignificadoNo Nilo Care
line[0]LogradouroRua
line[1]NúmeroNúmero
line[2]ComplementoComplemento

Não pule posições encurtando a lista. Enviar ["Rua Tamandaré", "Casa 2"] grava Casa 2 como número, não como complemento.

E não há como deixar uma posição em branco: string vazia é recusada pela validação do FHIR, com code: structure apontando a posição. Endereço sem número usa a convenção S/N no lugar — ["Rua Tamandaré", "S/N", "Casa 2"].

A leitura encurta a lista. Posições sem valor são omitidas na resposta: um local sem complemento volta com line de dois itens, e um local sem número volta com dois itens em que o segundo é o complemento.

Reenviar uma resposta como ela veio pode, por isso, mover o complemento para o número. Ao reenviar um endereço lido, reconstrua as três posições a partir do que cada uma significa, não da ordem em que vieram.

O endereço é completado pela plataforma

O endereço enviado é conferido e completado contra uma base de endereços antes de ser gravado. O que você mandou preenchido volta como você mandoustate: SP volta SP. O que muda é o resto:

  • os campos que você deixou de fora podem voltar preenchidos, completados a partir dos que você enviou — bairro, cidade, estado ou país, por exemplo;
  • a capitalização é normalizada: rua tamandaré volta Rua Tamandaré.

Por isso a resposta de uma escrita raramente é idêntica ao que você mandou. Isso é esperado, e não indica erro.

Numa atualização que traga postalCode, os demais campos do endereço são recalculados a partir dele. Reenviar o endereço inteiro é o caminho seguro; reenviar só o CEP pode trocar o resto.

Atualizar

O mesmo POST cria e atualiza. Reenviando um identifier que já corresponde a um local, ele é atualizado.

Omitir address numa atualização apaga o endereço já cadastrado. Ao contrário dos demais campos, o endereço não é preservado por omissão: um POST só com identifier e name sobre um local que tinha endereço deixa o estabelecimento sem endereço nenhum no Nilo Care.

Se você só quer corrigir o nome, reenvie o endereço junto — inteiro, com o line e o postalCode.

E essa remoção é invisível por aqui. O recurso FHIR guarda o último endereço gravado, então a resposta do POST e todas as leituras seguintes continuam mostrando o endereço que sumiu do cadastro. Não use o GET para confirmar que o endereço foi removido: ele não vai refletir isso.

O código CNES, esse sim, é preservado: uma atualização sem o identificador https://cnes.datasus.gov.br/ mantém o CNES que o estabelecimento já tinha. Para trocá-lo, mande o identificador novo.

O código CNES

O CNES entra como um identificador com o system https://cnes.datasus.gov.br/:

POST
/fhir/resources/Location
1curl -X POST https://landing-zone-api.nilo.services/fhir/resources/Location \
2 -H "x-api-key: <apiKey>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "resourceType": "Location",
6 "identifier": [
7 {
8 "system": "https://www.acmesaude.com.br/integracao/local/",
9 "value": "HC-01",
10 "use": "usual"
11 },
12 {
13 "system": "https://cnes.datasus.gov.br/",
14 "value": "2077485",
15 "use": "official"
16 }
17 ],
18 "name": "Hospital Central Acme",
19 "address": {
20 "line": [
21 "Rua Tamandaré",
22 "321"
23 ],
24 "postalCode": "01503-001",
25 "district": "Liberdade",
26 "city": "São Paulo",
27 "state": "SP",
28 "country": "Brasil"
29 }
30}'

O código CNES é único no ambiente. Enviar um CNES que já pertence a outro estabelecimento responde 409, e nada é gravado. É o único 409 deste recurso.

E há um segundo desfecho, pior que o 409. O CNES também serve para reconhecer o local: se a sua chave ainda não existe mas o CNES bate com um estabelecimento já cadastrado, a escrita não cria nada nem recusa — ela atualiza aquele estabelecimento, trocando o nome dele e agregando a sua chave. A chamada responde 200, e você só descobre olhando o id que voltou.

Trate o CNES como chave: não o reaproveite entre locais, e confira com um GET antes de cadastrar um estabelecimento cujo CNES você não tem certeza de que é novo.

Sem endereço

address é opcional na criação, e um local sem endereço é um cadastro válido:

POST
/fhir/resources/Location
1curl -X POST https://landing-zone-api.nilo.services/fhir/resources/Location \
2 -H "x-api-key: <apiKey>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "resourceType": "Location",
6 "identifier": [
7 {
8 "system": "https://www.acmesaude.com.br/integracao/local/",
9 "value": "AZS-01",
10 "use": "usual"
11 }
12 ],
13 "name": "Ambulatório Zona Sul"
14}'

Buscar

GET
/fhir/resources/Location
1curl -G https://landing-zone-api.nilo.services/fhir/resources/Location \
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 -d address=Liberdade \
7 --data-urlencode "address-city=São Paulo" \
8 -d address-country=Brasil \
9 -d address-postalcode=01503-001 \
10 --data-urlencode "address-state=São Paulo" \
11 --data-urlencode identifier=https://cnes.datasus.gov.br/|2077485 \
12 -d name=Central

A resposta é sempre um Bundle do tipo searchset:

Response
1{
2 "resourceType": "Bundle",
3 "type": "searchset",
4 "entry": [
5 {
6 "fullUrl": "https://landing-zone-api.nilo.services/fhir/resources/Location/8b53f107-2d94-4c6e-a71f-9042e5b8c316",
7 "resource": {
8 "resourceType": "Location",
9 "identifier": [
10 {
11 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/almanac-api--health-facility",
12 "value": "295",
13 "use": "usual"
14 },
15 {
16 "system": "https://cnes.datasus.gov.br/",
17 "value": "2077485",
18 "use": "official"
19 },
20 {
21 "system": "https://www.acmesaude.com.br/integracao/local/",
22 "value": "HC-01",
23 "use": "usual"
24 }
25 ],
26 "name": "Hospital Central Acme",
27 "id": "8b53f107-2d94-4c6e-a71f-9042e5b8c316",
28 "address": {
29 "line": [
30 "Rua Tamandaré",
31 "321",
32 "Casa 2"
33 ],
34 "postalCode": "01503-001",
35 "district": "Liberdade",
36 "city": "São Paulo",
37 "state": "SP",
38 "country": "Brasil",
39 "text": "Rua Tamandaré, 321, Casa 2 - Liberdade, São Paulo/SP. 01503-001.",
40 "use": "work",
41 "type": "both"
42 }
43 },
44 "search": {
45 "mode": "match"
46 }
47 },
48 {
49 "fullUrl": "https://landing-zone-api.nilo.services/fhir/resources/Location/c1d09a4e-6b72-4f38-9de5-0a83b7f2c145",
50 "resource": {
51 "resourceType": "Location",
52 "identifier": [
53 {
54 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/almanac-api--health-facility",
55 "value": "296",
56 "use": "usual"
57 }
58 ],
59 "name": "Ambulatório Zona Sul",
60 "id": "c1d09a4e-6b72-4f38-9de5-0a83b7f2c145"
61 },
62 "search": {
63 "mode": "match"
64 }
65 }
66 ],
67 "link": []
68}

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 do local, em system|value. Aceita a sua chave, o identificador Nilo ou o código CNESLocation.identifier
namestringParte do nome do estabelecimentoLocation.name
addressstringParte de qualquer campo do endereçoLocation.address
address-citystringCidadeLocation.address.city
address-statestringEstado, na forma em que foi gravado — quem cadastrou SP não é encontrado por São PauloLocation.address.state
address-postalcodestringCEPLocation.address.postalCode
address-countrystringPaísLocation.address.country
address-usetokenAceito, e inútil: todo endereço é workLocation.address.use
_lastUpdateddateData da última gravação no store, com os prefixos eq, ge, le

Os demais parâmetros canônicos do Location existem e não encontram nada aqui, porque a Nilo não preenche o campo correspondente: status, operational-status, type, near, organization, partof e endpoint. A exceção é o campo que você mesmo tenha enviado: ele fica no recurso e passa a ser encontrável — mais um motivo para omitir o que a plataforma não usa.

A paginação é por _count e _page_token; havendo página seguinte, o Bundle traz a URL pronta em link.

Um estabelecimento antigo, que ninguém tenha tocado desde que a sua integração começou, pode não aparecer na lista. Se você espera um local e ele não vem, peça ao Suporte: a busca não é um inventário garantido do cadastro.

Ler por ID

GET
/fhir/resources/Location/:id
1curl https://landing-zone-api.nilo.services/fhir/resources/Location/id \
2 -H "x-api-key: <apiKey>"

Diferente da busca, esta leitura não devolve um Bundle — mas também não devolve o recurso nu. A resposta é um envelope com fullUrl, search e resource, e o local está em resource. Ler name ou address na raiz da resposta não encontra nada.

Response
1{
2 "fullUrl": "https://landing-zone-api.nilo.services/fhir/resources/Location/8b53f107-2d94-4c6e-a71f-9042e5b8c316",
3 "resource": {
4 "resourceType": "Location",
5 "identifier": [
6 {
7 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/almanac-api--health-facility",
8 "value": "295",
9 "use": "usual"
10 },
11 {
12 "system": "https://cnes.datasus.gov.br/",
13 "value": "2077485",
14 "use": "official"
15 }
16 ],
17 "name": "Hospital Central Acme",
18 "id": "8b53f107-2d94-4c6e-a71f-9042e5b8c316",
19 "meta": {
20 "lastUpdated": "2026-08-24T13:42:07.554000Z",
21 "versionId": "MTc4NjAyMTQ1MjkwNTAwMDM2Nw"
22 },
23 "address": {
24 "line": [
25 "Rua Tamandaré",
26 "321",
27 "Casa 2"
28 ],
29 "postalCode": "01503-001",
30 "district": "Liberdade",
31 "city": "São Paulo",
32 "state": "SP",
33 "country": "Brasil",
34 "text": "Rua Tamandaré, 321, Casa 2 - Liberdade, São Paulo/SP. 01503-001.",
35 "use": "work",
36 "type": "both"
37 }
38 },
39 "search": {
40 "mode": "match"
41 }
42}

Onde o local é referenciado

O Location é o cadastro; quem o usa são os registros de cuidado.

RecursoCampoComo o local entra
Hospitalizaçãolocation[0].locationPor display, em texto livre — ou por identifier de um Location cadastrado, e aí o nome dele é copiado como texto
Pronto atendimentolocation[0].locationIdem
Profissionaladdress[]Cada endereço com use: work cria um local novo e o vincula ao profissional — é o que alimenta os Locais de atendimento dele

Não há vínculo vivo entre o evento e o local. O que o evento guarda é o nome do local no momento da gravação, copiado como texto. Renomear ou mudar o endereço de um Location depois não altera o que ficou registrado no evento.

Os dois caminhos de cadastro não se encontram. O endereço work de um profissional sempre cria um local novo — não há como apontar para um Location já cadastrado a partir do profissional. Repetir o mesmo endereço em vários profissionais gera vários locais diferentes, todos com o mesmo endereço. Se o seu ambiente cadastra locais por aqui, evite mandar address com use: work no profissional.

Valores devolvidos que você não enviou

address.use é sempre work e address.type é sempre both. São constantes da leitura, não uma classificação do endereço — não há como registrar um endereço residencial ou postal aqui.

address.text é o endereço montado numa linha só pela plataforma, para exibição. Enviá-lo não tem efeito, e o formato dele não é contrato: monte a sua própria exibição a partir dos campos.

Efeitos colaterais

Atualizar sem address apaga o endereço. É o efeito de maior consequência deste recurso, e vale repetir aqui: o endereço não é preservado por omissão.

Alterar um local não altera nenhum evento já registrado: os eventos guardam o nome do local como texto, e continuam com o nome antigo.

Este endpoint nunca responde 204: não há remoção de local por integração.

Erros

Recusa é 400, e o corpo é um OperationOutcome: issue[].details.text explica o motivo e, quando a recusa é de um campo conferido por esta API, issue[].expression aponta qual. A única exceção é o 409 do CNES duplicado.

StatuscodeexpressionMensagemQuando
400requiredLocation.identifierField is requiredO payload não tem identifier
400business-ruleLocation.identifierUnable to use any of the provided identifiers to match an existing resource, which can lead to duplicates.identifier, mas nenhum utilizável — falta system ou falta value
400structureResource has identifier from a Nilo environment that is not the current environment.O payload traz um identificador Nilo gerado em outro ambiente
400structureo campo recusadomensagem da validação FHIRO payload não é um Location válido
400exceptionHTTPBadRequest: …address sem postalCode, name ausente ou longo demais, número acima de 10 caracteres
400exceptionTypeError: …address enviado sem line
409conflictResource conflicts with other existing resource(s), mais um diagnosticsO código CNES já pertence a outro estabelecimento
Response
1{
2 "issue": [
3 {
4 "code": "exception",
5 "details": {
6 "text": "HTTPBadRequest: [Bad Request] 400 for https://<serviço>/api/v1/addresses/: {'postal_code': ['This field may not be null.']}"
7 },
8 "severity": "error"
9 }
10 ],
11 "resourceType": "OperationOutcome"
12}

As linhas exception não são validações desta API — são falhas repassadas como estão. Elas saem sem expression e com uma mensagem de erro de linguagem, que inclui o endereço interno chamado. O mesmo vale para o diagnostics do 409.

Nada disso é contrato: não tente interpretar o texto, não o exiba para o usuário final e não o registre em log de longa duração. Trate code: exception como “payload recusado, motivo não classificado” e confira o line, o postalCode e o name.

Os payloads completos estão na aba Referência, em POST /fhir/resources/Location.