Mensagem programada

Uma mensagem programada é um envio agendado para um paciente: o lembrete de consulta, a mensagem de acompanhamento que a diretriz manda disparar em 30 dias.

No FHIR o recurso é a CommunicationRequest — e ele carrega duas coisas diferentes, distinguidas pelo system do identificador:

O que ésystem do identificador NiloEscrita
Mensagem programada para um paciente…/NamingSystem/jaiminho-api--scheduled-messagenão há
Matrícula do paciente numa campanha…/NamingSystem/jaiminho-api--patient-campaignsim — é a única

As duas formas não trazem os mesmos campos. A mensagem programada é rica: tem priority, category, occurrencePeriod, payload e note. A matrícula em campanha traz apenas status, basedOn, subject, recipient e authoredOn.

Confira o system do identificador antes de ler qualquer outro campo.

Campos

CampoPresente emO que significa
resourceTypeambosConstante CommunicationRequest
identifierambosIdentificador Nilo — e o que distingue as duas formas
statusambosVeja a tabela abaixo
subjectambosO paciente
recipient[0]ambosRepete o subject
authoredOnambosQuando foi programada ou matriculada
basedOn[0]ambosO plano de cuidado, na mensagem; a campanha, na matrícula
prioritysó mensagemasap quando atrasada, routine no resto
occurrencePeriod.startsó mensagemQuando ela deve ser enviada
occurrencePeriod.endsó mensagemQuando foi concluída ou cancelada
replaces[0]só mensagemA mensagem programada que esta substitui
payload[0].contentStringsó mensagemO texto da mensagem
category[0]só mensagemUm código interno — veja o aviso
note[0]só mensagemUm texto de diagnóstico em formato interno
id · metaambosIdentificador Nilo FHIR do recurso e metadados da gravação

status

statusNa mensagem programadaNa matrícula em campanha
activeProgramada — inclusive atrasadaPaciente na campanha
completedJá enviadaCampanha concluída com sucesso
revokedCanceladaPaciente saiu, ou pediu para sair
entered-in-errorMatrícula registrada por engano
unknownQualquer outro estado

priority não é uma prioridade: é um sinal de atraso. Ele vale asap quando o envio já passou da data e ainda não aconteceu, e routine em todo o resto. Ninguém escolhe esse valor.

E ele fica para trás. O valor gravado é o do último instante em que a mensagem foi sincronizada — e a passagem de “programada” para “atrasada” não altera nada no registro, então não dispara sincronização. Uma mensagem que venceu depois do último sync continua routine.

Para saber se uma mensagem está atrasada, compare occurrencePeriod.start com a data de hoje do seu lado. Não confie no priority.

subject e recipient[0] trazem o mesmo paciente. O FHIR distingue de quem é o assunto e para quem vai a mensagem; aqui os dois campos são a mesma pessoa, sempre.

occurrencePeriod não é um intervalo

occurrencePeriod.start é a data programada de envio, e occurrencePeriod.end é o instante em que a mensagem foi concluída ou cancelada. Numa mensagem ainda programada, o end não vem.

Não são as duas pontas de uma janela de envio: são “quando devia” e “quando acabou”.

Dois campos que vazam formato interno

category[0].coding[0].code traz um identificador interno da plataforma, não um código de vocabulário. Não é contrato: o valor pode mudar sem aviso, e não deve ser interpretado nem usado como filtro estável — o parâmetro de busca category, na prática, só serve para quem já conhece o valor que veio numa resposta.

note[0].text é a única informação de motivo que existe neste recurso. Ele vem no formato status-detail:<motivo>, e o motivo é um de oito valores fechados: espera de encerramento da conversa, nova tentativa em curso, limite de tentativas excedido, expirada, criada no passado, paciente inativo, conversa desabilitada, e atraso máximo excedido.

O formato não é contrato — pode mudar sem aviso —, mas conhecer a lista ajuda a entender por que uma mensagem não saiu. Use para exibição e diagnóstico, não para lógica.

Campos que a Nilo não usa

A CommunicationRequest canônica traz muito mais: basedOn de outros tipos, groupIdentifier, statusReason, medium, about, encounter, requester, sender, reasonCode, reasonReference e doNotPerform. Nenhum deles é lido.

Repare em medium: não há campo que diga por onde a mensagem vai — se por conversa, se por outro canal. E em sender: não há como saber quem a programou.

Buscar

GET
/fhir/resources/CommunicationRequest
1curl -G https://landing-zone-api.nilo.services/fhir/resources/CommunicationRequest \
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 authored=ge2026-06-01 \
7 --data-urlencode identifier=https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/jaiminho-api--scheduled-message|440871 \
8 -d occurrence=ge2026-06-01 \
9 --data-urlencode patient=Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4 \
10 --data-urlencode patient:identifier=https://www.acmesaude.com.br/integracao/paciente/|507823709 \
11 -d priority=asap \
12 --data-urlencode recipient=Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4 \
13 --data-urlencode replaces=CommunicationRequest/c81f5b40-3a29-4e76-90d5-1b47ea62c803 \
14 -d status=active \
15 --data-urlencode subject=Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4

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/CommunicationRequest/c81f5b40-3a29-4e76-90d5-1b47ea62c803",
7 "resource": {
8 "resourceType": "CommunicationRequest",
9 "identifier": [
10 {
11 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/jaiminho-api--scheduled-message",
12 "value": "440871",
13 "use": "usual"
14 }
15 ],
16 "status": "active",
17 "id": "c81f5b40-3a29-4e76-90d5-1b47ea62c803",
18 "meta": {
19 "lastUpdated": "2026-06-26T03:00:12.447000Z",
20 "versionId": "MTc4NjAyMTQ1MjkwNTAwMDM2Nw"
21 },
22 "priority": "asap",
23 "subject": {
24 "identifier": {
25 "system": "https://www.acmesaude.com.br/integracao/paciente/",
26 "value": "507823709",
27 "use": "usual"
28 },
29 "type": "Patient",
30 "reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
31 },
32 "recipient": [
33 {
34 "identifier": {
35 "system": "https://www.acmesaude.com.br/integracao/paciente/",
36 "value": "507823709",
37 "use": "usual"
38 },
39 "type": "Patient",
40 "reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
41 }
42 ],
43 "category": [
44 {
45 "coding": [
46 {
47 "code": "jaiminho_api.ScheduledMessage",
48 "system": "https://landing-zone-api.nilo.services/fhir/resources/CodeSystem/communication-category"
49 }
50 ]
51 }
52 ],
53 "basedOn": [
54 {
55 "identifier": {
56 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/hippocrates-api--patient-care-line",
57 "value": "77120",
58 "use": "usual"
59 },
60 "type": "CarePlan"
61 }
62 ],
63 "payload": [
64 {
65 "contentString": "Lembrete: sua consulta de acompanhamento está marcada para a próxima semana."
66 }
67 ],
68 "authoredOn": "2026-06-20T08:00:00+00:00",
69 "occurrencePeriod": {
70 "start": "2026-06-25T09:00:00+00:00"
71 }
72 },
73 "search": {
74 "mode": "match"
75 }
76 }
77 ],
78 "link": []
79}

Busca sem resultados não é erro: volta 200 com um Bundle cujo entry é uma lista vazia.

Response
1{
2 "resourceType": "Bundle",
3 "type": "searchset",
4 "entry": [],
5 "link": []
6}

Parâmetros de busca suportados

NomeTipoDescriçãoExpressão
identifiertokenIdentificador da mensagem, em system|valueCommunicationRequest.identifier
patient · subjectreferencePaciente, pelo id Nilo FHIR deleCommunicationRequest.subject
patient:identifier · subject:identifierreferencePaciente, pelo identificador deleCommunicationRequest.subject.identifier
recipientreferenceO mesmo paciente, pelo outro campoCommunicationRequest.recipient
statustokenSituação da mensagemCommunicationRequest.status
prioritytokenasap traz as que estavam atrasadas na última gravação — veja o avisoCommunicationRequest.priority
occurrencedateData programada de envio, com os prefixos eq, ge, leCommunicationRequest.occurrence
authoreddateData em que foi programada, com os mesmos prefixosCommunicationRequest.authoredOn
categorytokenCategoria, em system|code — o código é internoCommunicationRequest.category
replacesreferenceA mensagem que esta substitui, pelo id Nilo FHIR delaCommunicationRequest.replaces
_lastUpdateddateData da última gravação no store, com os mesmos prefixos

Os demais parâmetros canônicos existem e não encontram nada, porque a plataforma não preenche o campo correspondente: medium, encounter, requester, sender, group-identifier e based-on — este último porque a referência ao plano de cuidado vem sem reference; use based-on:identifier.

Qual identificador as referências carregam depende da sua implantação. Na configuração que usa identificadores externos, o subject sai com o identificador do seu sistema; sem ela, vem o identificador Nilo do paciente. Confira numa resposta de leitura antes de montar a busca em volume.

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

Ler por ID

GET
/fhir/resources/CommunicationRequest/:id
1curl https://landing-zone-api.nilo.services/fhir/resources/CommunicationRequest/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 a mensagem está em resource.

Response
1{
2 "fullUrl": "https://landing-zone-api.nilo.services/fhir/resources/CommunicationRequest/c81f5b40-3a29-4e76-90d5-1b47ea62c803",
3 "resource": {
4 "resourceType": "CommunicationRequest",
5 "identifier": [
6 {
7 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/jaiminho-api--scheduled-message",
8 "value": "440871",
9 "use": "usual"
10 }
11 ],
12 "status": "completed",
13 "id": "c81f5b40-3a29-4e76-90d5-1b47ea62c803",
14 "meta": {
15 "lastUpdated": "2026-06-26T03:00:12.447000Z",
16 "versionId": "MTc4NjAyMTQ1MjkwNTAwMDM2Nw"
17 },
18 "priority": "routine",
19 "subject": {
20 "identifier": {
21 "system": "https://www.acmesaude.com.br/integracao/paciente/",
22 "value": "507823709",
23 "use": "usual"
24 },
25 "type": "Patient",
26 "reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
27 },
28 "category": [
29 {
30 "coding": [
31 {
32 "code": "jaiminho_api.ScheduledMessage",
33 "system": "https://landing-zone-api.nilo.services/fhir/resources/CodeSystem/communication-category"
34 }
35 ]
36 }
37 ],
38 "payload": [
39 {
40 "contentString": "Lembrete: sua consulta de acompanhamento está marcada para a próxima semana."
41 }
42 ],
43 "authoredOn": "2026-06-20T08:00:00+00:00",
44 "occurrencePeriod": {
45 "end": "2026-06-25T09:00:47+00:00",
46 "start": "2026-06-25T09:00:00+00:00"
47 }
48 },
49 "search": {
50 "mode": "match"
51 }
52}

Onde a mensagem programada aparece

As mensagens geradas por uma diretriz também aparecem em activity[] do plano de cuidado do paciente, junto com as tarefas e os questionários. A vantagem desta página é poder buscá-las diretamente, sem passar pelo plano.

O caminho de volta é o basedOn, que aponta para o plano de cuidado — sem reference, então use based-on:identifier para buscar por ele. Numa matrícula em campanha, o mesmo basedOn aponta para a campanha, não para um plano: confira o system do identificador antes de interpretá-lo.

Uma mensagem reprogramada aponta em replaces[0] para a anterior. É a única forma de reconstruir a cadeia de reprogramações.

Matricular um paciente numa campanha

O POST deste recurso faz uma coisa só: coloca um paciente numa campanha de mensagens já configurada no seu ambiente. Ele não programa uma mensagem avulsa.

POST
/fhir/resources/CommunicationRequest
1curl -X POST https://landing-zone-api.nilo.services/fhir/resources/CommunicationRequest \
2 -H "x-api-key: <apiKey>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "resourceType": "CommunicationRequest",
6 "subject": {
7 "identifier": {
8 "system": "https://www.acmesaude.com.br/integracao/paciente/",
9 "value": "507823709",
10 "use": "usual"
11 },
12 "type": "Patient"
13 },
14 "basedOn": [
15 {
16 "identifier": {
17 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/jaiminho-api--campaign",
18 "value": "318",
19 "use": "usual"
20 }
21 }
22 ]
23}'

A resposta é o recurso gravado, sem envelope:

Response
1{
2 "resourceType": "CommunicationRequest",
3 "identifier": [
4 {
5 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/jaiminho-api--patient-campaign",
6 "value": "88301",
7 "use": "usual"
8 }
9 ],
10 "status": "active",
11 "id": "5a2c7e08-4b91-4d36-a870-f1e4062c9b53",
12 "meta": {
13 "lastUpdated": "2026-07-08T10:02:19.640000Z",
14 "versionId": "MTc4NjAyMTQ1MjkwNTAwMDQwNQ"
15 },
16 "subject": {
17 "identifier": {
18 "system": "https://www.acmesaude.com.br/integracao/paciente/",
19 "value": "507823709",
20 "use": "usual"
21 },
22 "type": "Patient",
23 "reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
24 },
25 "recipient": [
26 {
27 "identifier": {
28 "system": "https://www.acmesaude.com.br/integracao/paciente/",
29 "value": "507823709",
30 "use": "usual"
31 },
32 "type": "Patient",
33 "reference": "Patient/ba200cfa-dae0-46cf-81a0-008e3f7414b4"
34 }
35 ],
36 "basedOn": [
37 {
38 "identifier": {
39 "system": "https://landing-zone-api.nilo.services/fhir/resources/NamingSystem/jaiminho-api--campaign",
40 "value": "318",
41 "use": "usual"
42 }
43 }
44 ],
45 "authoredOn": "2026-07-08T10:02:19+00:00"
46}

São quatro exigências, e todas recusam a chamada quando não são atendidas:

CampoRegra
statusTem de ser exatamente active
subject.identifierObrigatório — o paciente, pela sua chave ou pelo identificador Nilo
basedOnObrigatório, com exatamente uma campanha
basedOn[0].identifierNo system da campanha, e a campanha tem de existir

A campanha não é criada por aqui. Ela é configurada no seu ambiente, e o que esta API faz é matricular pacientes nela. Se a campanha não existir, a chamada é recusada com Campaign not found — fale com o Suporte para saber quais campanhas existem e quais são os identificadores delas.

Um paciente não pode ser matriculado duas vezes na mesma campanha. Se ele já estiver nela com a matrícula em andamento, a chamada é recusada com Patient already on campaign.

E não há como retirá-lo por esta API: sair da campanha é ação da plataforma.

priority, occurrencePeriod, payload, category e note não são lidos neste POST, e não voltam na resposta. A matrícula é um recurso mais pobre que a mensagem programada.

Erros da matrícula

Recusa é 400, e o corpo é um OperationOutcome com code: structure e o campo em issue[].expression.

expressionMensagemQuando
CommunicationRequest.statusCampaigns can't be created with the status not activeO status não é active
CommunicationRequest.subject.identifierfield requiredNão há subject, ou ele veio sem identifier
CommunicationRequest.subject.identifierPatient not foundO identificador não resolve para um paciente do seu ambiente
CommunicationRequest.basedOnfield requiredO payload não tem basedOn
CommunicationRequest.basedOnCampaign not foundNenhuma campanha no system esperado, ou a campanha não existe
CommunicationRequest.basedOnMultiple campaigns are not allowedMais de uma campanha em basedOn
CommunicationRequest.subjectPatient already on campaignO paciente já está nessa campanha
Response
1{
2 "issue": [
3 {
4 "code": "structure",
5 "details": {
6 "text": "Patient already on campaign"
7 },
8 "expression": [
9 "CommunicationRequest.subject"
10 ],
11 "severity": "error"
12 }
13 ],
14 "resourceType": "OperationOutcome"
15}

429

Este recurso pode ter limite de escrita, conforme a configuração do ambiente. Havendo limite e estourando-o, a chamada responde 429 com code: throttled, e a mensagem informa quantos segundos esperar.

O que a integração não cobre

  • programar uma mensagem avulsa, cancelá-la ou reprogramá-la — só a matrícula em campanha é gravável;
  • retirar um paciente de uma campanha;
  • listar as campanhas existentes: elas não são publicadas como recurso;
  • por onde a mensagem vai ser enviada;
  • quem a programou;
  • o elo com a mensagem efetivamente enviada — o Communication correspondente não é referenciado.