Agendamento
Um agendamento é o compromisso marcado entre um paciente e um profissional: quando, com quem, e se é online ou presencial. No Nilo Care ele aparece na ficha do paciente, nas seções Agendamentos liberados e Agendamentos marcados — e, nas implantações com a tela nova, também em Passados.
No FHIR o recurso é o Appointment, e esta
integração o usa nas duas pontas.
Dois campos do FHIR se cruzam aqui, e é fácil errar. O título do agendamento vai em
description, e as observações vão em comment — não o contrário. Trocá-los grava o texto
livre no campo de título da plataforma.
Nenhum dos dois é exibido nas telas de agendamento hoje: o que a equipe vê na lista é o nome do profissional e a especialidade. Mas eles ficam gravados, e é pelo nome certo que outros sistemas os leem.
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.
Sobreposição de horário
A plataforma sabe conferir a agenda do profissional antes de gravar: o horário não pode colidir
com outro compromisso, e precisa estar dentro dos horários em que ele atende. Pela integração
essa conferência não roda por padrão. Um Appointment que chega sem a extensão
appointment-allowConflict é gravado como se ela viesse com valueBoolean: true — o horário que
você enviou é tratado como decisão já tomada do seu lado.
Mandar a extensão com true explicita o que já é o padrão:
Para pedir a conferência num agendamento, envie a extensão com valueBoolean: false — com a
ressalva do terceiro aviso abaixo.
true dispensa mais do que a colisão de horário. Com ele, a plataforma para de conferir a
agenda daquele agendamento por inteiro — e passam também:
- horários fora da grade de atendimento do profissional, que a conferência recusa ainda que não colidam com nada;
- um segundo agendamento do mesmo paciente no mesmo horário, e um segundo agendamento do mesmo profissional no mesmo horário, que a conferência barra como duplicidade.
Como true é o padrão, é disso que você abre mão em cada agendamento enviado, e são as três
juntas — não há como dispensar uma e manter as outras.
Um agendamento gravado com true deixa de bloquear os outros. Ele não entra na conferência
de horário dos agendamentos seguintes: um agendamento comum, marcado depois no mesmo horário, é
aceito como se o primeiro não existisse — e é aceito sem extensão nenhuma.
O efeito não termina naquela chamada, e true é o padrão: cada agendamento que você envia
degrada a conferência de agenda para todo mundo que marca depois, inclusive pela tela.
false não é o contrário de true aqui. Ele não recusa a sobreposição: deixa a decisão para
a configuração de sobreposição do seu care provider. Se essa configuração está habilitada, a
sobreposição continua permitida e a resposta devolve true — não há como recusá-la agendamento
por agendamento. Para desabilitá-la, fale com o time de suporte.
Na atualização, omitir a extensão não preserva o false anterior. O padrão vale em toda
escrita, não só na criação: um update que chega sem a extensão sobrescreve com true o false
gravado antes. Para manter o false, reenvie-o em cada update daquele agendamento.
Uma extensão com a URL errada, ou cujo valor não venha em valueBoolean, é ignorada — e
ignorada vale true. Um false escrito errado não recusa nada, e não há erro avisando: confira
a URL antes de concluir que a conferência está valendo.
A extensão vem em toda leitura de Appointment, com true ou com false — não só nos
agendamentos que a usaram na escrita. Um false na resposta significa “a conferência de agenda
valeu para este agendamento”, não “você mandou false”.
A URL completa da extensão está em Extensões — aqui ela aparece só pelo nome final.
status: oito valores entram, seis saem
A escrita aceita oito valores do FHIR e os reduz a quatro situações da plataforma. A leitura devolve a situação, não o que você mandou:
A leitura, por sua vez, traduz nove situações da plataforma em seis códigos:
Um agendamento reagendado e um recorrente voltam como booked, iguais a um agendamento
comum. Não há campo que diga que ele foi remarcado, nem que ele faz parte de uma série.
O status não faz round-trip. Enviar proposed, pending, arrived ou checked-in e
reler devolve booked — os quatro colapsam na mesma situação. Não construa fluxo em cima de
distinguir “confirmado” de “paciente chegou”: a plataforma não guarda essa diferença.
waitlist e entered-in-error recusam a chamada. Eles não têm destino na plataforma, e a
recusa vem como not-supported, apontando Appointment.status.
E waitlist é justamente um valor que a leitura devolve, num agendamento marcado como
indisponível pela equipe. Ou seja: há agendamentos que você lê e não consegue reenviar.
proposed é aceito na escrita, mas não faz o que parece: ele não cria um agendamento
liberado, vira um agendamento normal. Liberar horário para o paciente marcar é ação da equipe na
tela.
waitlist é o único valor que a leitura devolve e a escrita não aceita.
Os participantes
participant precisa de exatamente um de cada tipo, e o type do actor é o que
distingue:
Zero ou dois participantes do mesmo tipo recusam a chamada, com uma mensagem que nomeia o tipo que faltou. Um agendamento com dois profissionais, ou sem paciente, não é possível.
E o type é obrigatório: sem ele a API não sabe qual é qual, e a chamada é recusada como
se o participante não existisse.
participant[].status é exigido pelo FHIR e descartado na escrita: a leitura devolve sempre
accepted, nos dois participantes. Não há como registrar que o paciente ainda não confirmou.
A modalidade
appointmentType diz se a consulta é online ou presencial, num coding cujo system é
{host}/fhir/resources/CodeSystem/appointment-type — a URL absoluta do seu ambiente, como
https://landing-zone-api.nilo.services/fhir/resources/CodeSystem/appointment-type em produção:
Omitir appointmentType cria um agendamento online, não um sem modalidade. Se a consulta é
presencial, mande o IN_PERSON — a omissão não é neutra.
Um coding em outro system é ignorado em silêncio e cai na mesma regra da omissão — e é
por isso que errar a URL do system é perigoso: a consulta presencial vira online e a chamada
responde 200. Já um código desconhecido dentro do system da Nilo recusa a chamada com
not-supported.
A sala de vídeo
Nos agendamentos online com sala criada, o link vem embutido no próprio recurso, em
contained[0].address:
É só resposta: enviar um contained não cria sala — embora o que você mandar fique gravado
e volte nas leituras seguintes, sem significar nada.
A sala não nasce com o agendamento, e nem todo agendamento tem uma. Três coisas precisam ser verdade:
- a funcionalidade tem de estar habilitada para o seu ambiente, o que é decidido na implantação. Se ela não estiver, nenhum agendamento terá sala — fale com o Suporte;
- o agendamento tem de ter datas no futuro: um agendamento retroativo não gera sala;
- a situação tem de ser de agendamento em aberto:
fulfilled,noshowecancellednão geram sala.
E a criação é assíncrona. A resposta do POST normalmente vem sem contained; a sala
aparece em segundos, podendo levar alguns minutos em períodos de alta demanda. Prefira ser
avisado por webhook a ficar consultando — e não conclua que o agendamento não terá sala só
porque a primeira leitura veio sem ela.
Campos que a Nilo não usa
O Appointment canônico traz muito mais do que esta integração lê: cancelationReason,
serviceCategory, serviceType, specialty, reasonCode, reasonReference, priority,
supportingInformation, minutesDuration, slot, basedOn, requestedPeriod e
patientInstruction. Nenhum deles é lido.
Repare em cancelationReason: não há como registrar por que um agendamento foi
cancelado. E em specialty: a especialidade do agendamento existe na tela e não tem campo
aqui.
A referência lista só os campos suportados, e é assim que ela deve ser lida: o que mandar na escrita. A API em si é mais tolerante — o que você mandar a mais fica guardado no recurso e volta nas leituras seguintes, sem nunca ter significado nada para a plataforma. Se o seu validador for estrito contra a referência, uma resposta assim vai parecer inválida; o remédio é não enviá-los.
Marcar um agendamento
A resposta é o recurso gravado, sem envelope:
Guarde o id: é por ele que se faz a leitura direta. Ao lado do seu identifier, a resposta
traz o identificador Nilo do agendamento, no system
…/NamingSystem/care-api--scheduling-v2.
Cancelar
Cancelar é reenviar o mesmo identifier com status: cancelled.
Não há atualização parcial. Todo POST monta o agendamento inteiro a partir do payload, e
o que você omitir é apagado na plataforma ou volta ao padrão: sem comment, as observações
somem da ficha; sem appointmentType, um agendamento presencial vira online.
Reenvie o recurso inteiro em toda atualização, inclusive no cancelamento.
E a leitura não acompanha esse apagamento. O recurso FHIR guarda o último valor não vazio:
depois de um POST sem comment, a consulta continua devolvendo a observação antiga, que a
equipe já não vê. Para zerar de verdade os dois lados, mande o campo vazio ("comment": "") em
vez de omiti-lo.
Não há remoção por integração: este endpoint nunca responde 204. Cancelar é a forma de
encerrar um agendamento.
Ao cancelar, a plataforma pode limpar as datas do registro. Um agendamento com
status: cancelled lido depois disso pode vir sem start e sem end — não trate a ausência
como erro de leitura.
Buscar
A resposta é sempre um Bundle do tipo searchset:
Busca sem resultados não é erro: volta 200 com um Bundle cujo entry é uma lista vazia.
Parâmetros de busca suportados
Os demais parâmetros canônicos do Appointment — service-category, service-type,
specialty, reason-code, reason-reference, slot, based-on e supporting-info — só
encontram o que você mesmo tiver enviado: a plataforma não preenche nenhum desses campos,
mas guarda o que vier no payload.
part-status é a exceção pelo motivo oposto: como todo participante é gravado como accepted,
part-status=accepted devolve tudo. Não é filtro útil.
Qual identificador as referências de participant.actor carregam depende da sua
implantação. Na configuração que usa identificadores externos, cada referência sai com o
identificador do seu sistema; sem ela, vem o identificador Nilo do paciente e do profissional. É
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.
A paginação é por _count e _page_token; havendo página seguinte, o Bundle traz a URL
pronta em link.
Ler por ID
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 agendamento está em
resource. Ler start ou participant na raiz da resposta não encontra nada.
Efeitos colaterais
Um agendamento apagado na plataforma some do store. A busca para de devolvê-lo e a leitura
por id passa a responder 404, sem aviso. Não conclua que ele foi cancelado — um cancelamento
devolve status: cancelled, não um 404.
O mesmo acontece, mais raramente, quando o agendamento fica sem situação registrada ou com uma situação que esta integração não conhece: o recurso é removido em silêncio na sincronização seguinte.
Marcar um agendamento não cria um atendimento. O atendimento nasce quando a consulta acontece — veja Atendimentos.
O profissional que você informa em participant fica registrado também como autor e como
responsável pelo agendamento na plataforma. Não há como separar quem atende de quem marcou.
Um agendamento marcado por integração aparece na agenda do profissional como qualquer outro, e a equipe pode alterá-lo por lá. Uma alteração feita na tela chega às suas leituras seguintes.
Erros
Recusa é 400, e o corpo é um OperationOutcome: issue[].expression aponta o campo culpado
e issue[].details.text explica o motivo. A exceção é o 409 da agenda duplicada.
As duas últimas linhas são a conferência de agenda, que a integração dispensa por padrão:
elas só aparecem quando o Appointment enviado traz a extensão
appointment-allowConflict com false e a configuração de
sobreposição do care provider não está habilitada. Nenhuma das duas aponta
expression: elas não são validações de um campo do payload, e sim da agenda como um todo.
A linha exception não é uma validação desta API — é uma falha repassada como está, 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 start, o end e o profissional.
Os payloads completos estão na aba Referência, em POST /fhir/resources/Appointment.
O que a integração não cobre
- o motivo do cancelamento;
- a especialidade e a recorrência do agendamento, que existem na tela;
- a distinção entre agendamento liberado e marcado na escrita — só na leitura;
- a confirmação do paciente:
participant[].statusé sempreaccepted; - criar a sala de vídeo — ela é criada pela plataforma.
Para a consulta prevista por uma diretriz, que antecede o agendamento, veja Consulta prevista; para o atendimento que acontece depois, Atendimentos.

