Boas práticas de consumo
Como integrar com menos chamadas e sem esbarrar no limite da API: o que salvar na sua base, quando ir à API e como montar cada jornada — cadastro, encaminhamentos e agendamento.
Limite de uso da API
É o padrão do ambiente. Acima desse teto as chamadas são recusadas com 403 até a janela de um minuto virar — na média, sobra uma chamada a cada 2 segundos para toda a sua integração.
O limite vale por IP, não por usuário. Por isso ele é resolvido na arquitetura, não em cada tela:
- Uma saída só. Todas as chamadas à API Rapidoc passam por um ponto do seu backend, com uma fila que respeita o teto de 30 por minuto. O navegador ou o app nunca chamam a API direto.
- Leia da sua base primeiro. Planos, especialidades, beneficiários e encaminhamentos
PENDINGficam salvos do seu lado; a API só é chamada quando o dado não existe ou precisa mudar. - Agrupe. Cadastre até 10 vidas por
POST /beneficiariese busque a disponibilidade do mês inteiro numa chamada, em vez de uma por dia. - Espere antes de repetir. Chamada recusada com
403por limite volta para a fila com espera crescente (backoff) — reenviar na hora só consome o limite de novo.
O que salvar na sua base
Dados de referência mudam pouco: salve uma vez e leia localmente. O mínimo é o uuid do beneficiário.
uuid— obrigatório, chave de todas as chamadas da vidacpfe nome, para localizar a vida sem consultar a APIdocumentTypeedocumentNumber, quando estrangeiro- situação (ativa ou inativa)
/ beneficiariesSalve o uuid devolvido no cadastro. Ele não muda, mesmo se a vida for inativada e reativada.
uuiddo planopaymentTypeeserviceType- nome e descrição
/ plansAtualize quando o contrato mudar (ou uma vez ao dia). O array plans do beneficiário sai daqui.
uuidda especialidade- nome
- plano ao qual pertence
/ plansJá vêm em specialties[] de cada plano — sem chamada extra a /specialties. Mudam junto com o plano.
uuiddo encaminhamentobeneficiary.uuid,status,createdAturlPath(PDF)
/ beneficiary-medical-referrals?status=PENDINGNão salve: disponibilidade de agenda e status de agendamentos. Esses dados mudam a todo momento e sempre vêm da API.
Sincronizar cadastro
Planos e beneficiários entram na sua base uma vez; o dia a dia lê dali.
- API RapidocGET
/tema/ api/ plans Quando o contrato mudar, ou uma vez ao dia. Uma chamada traz planos e especialidades.
- Sua baseSalva planos e especialidades
uuid,paymentTypeeserviceTypede cada plano;uuide nome de cada especialidade. - Sua baseMonta o payload das vidas com os planos salvos
O array
planscompleto dos titulares sai da sua base, sem consultar a API a cada cadastro. Dependentes vão só com oholder. - API RapidocPOST
/tema/ api/ beneficiaries Até 10 vidas por requisição.
- Sua baseSalva
uuid,cpf,documentTypeedocumentNumberde cada beneficiárioToda chamada da vida usa o
uuidsalvo.cpfe documento servem para reencontrar a vida depois —GET /beneficiaries/{cpf}ouGET /beneficiaries/document/{documentNumber}para estrangeiros.
Encaminhamentos PENDING: sua base primeiro
Uma sincronização periódica mantém os pendentes na sua base; a busca da vida na API é só o fallback.
Sincronização periódica
- API RapidocGET
/tema/ api/ beneficiary-medical-referrals?status=PENDING Rotina periódica do seu backend. Uma chamada traz os pendentes de todas as vidas do cliente.
- Sua baseGrava os
PENDINGe atualiza os que saíram da listaEncaminhamento salvo que não voltou mais como
PENDINGmudou de situação — não use para agendar.
Na hora de agendar
- Sua baseBusca encaminhamento
PENDINGda vida na sua basePelo
uuiddo beneficiário salvo. - DecisãoEncontrou?SimUsa o
uuidsalvo — nenhuma chamada à API.NãoFallback: busca os encaminhamentos daquela vida na API (próximo passo). - API RapidocGET
/tema/ api/ beneficiaries/ {uuid}/ medical-referrals Só no fallback. Traz todos os encaminhamentos da vida — filtre os
PENDINGdo seu lado. - Sua baseSalva os encaminhamentos
PENDINGdevolvidosSem pendente: o agendamento segue sem encaminhamento.
Agendar consulta
Especialidade e encaminhamento saem da sua base; a API só é chamada para horários e para criar a consulta.
Especialista
- Sua baseLista as especialidades dos planos da vida
Da tabela de especialidades salva. Garante que a especialidade está nos planos — fora disso a API recusa com
400. - Sua baseResolve o encaminhamento
PENDINGPela jornada anterior. Psicologia e Nutrição não usam encaminhamento.
- API RapidocGET
/tema/ api/ specialty-availability?specialtyUuid={uuid}&beneficiaryUuid={uuid}&dateInitial=14/ 05/ 2026&dateFinal=14/ 06/ 2026 Uma chamada traz os horários de até 1 mês a partir do dia da consulta:
dateInitialé o dia da chamada (não pode ser data passada) edateFinal, até 1 mês depois. - Front-endExibe os horários do mês e o beneficiário escolhe
Síncrono com a API: os horários vêm sempre da chamada acima, com o mês inteiro, nunca da sua base. Troca de dia na tela filtra essa lista.
- API RapidocPOST
/tema/ api/ appointments Com
availabilityUuid,specialtyUuidebeneficiaryMedicalReferralUuid(ouapproveAdditionalPayment: truequando não houver encaminhamento). A partir daqui o fluxo é síncrono com a API Rapidoc. - DecisãoResposta 201?SimConsulta criada. Status, link e cancelamento são lidos direto da API — nada a salvar; a sincronização periódica tira o encaminhamento dos pendentes.NãoHorário já usado por outro agendamento: reconsulte a disponibilidade e ofereça outro horário.
Generalista
- Sua basePega o
uuiddo beneficiário na sua base - API RapidocGET
/tema/ api/ beneficiaries/ {uuid}/ request-appointment Direto: é fila de pronto atendimento, sem disponibilidade nem encaminhamento.
Câmera e microfone
A consulta em vídeo acontece na página aberta pela URL de atendimento. Se o seu app ou site não liberar câmera e microfone para essa página, o beneficiário entra na sala, mas o profissional não o vê nem o ouve.
São sempre duas etapas, seja qual for a plataforma:
- Pedir a permissão no seu lado — ao navegador ou ao sistema do celular — antes de abrir a URL.
- Repassar a permissão para a página da consulta, que roda dentro de um
iframe(web) ou de umWebView(app).
Navegadores e WebViews bloqueiam câmera e microfone em páginas servidas por HTTP. A página que abre o iframe precisa estar em HTTPS, inclusive em homologação.
Web (iframe)
Peça a permissão com getUserMedia assim que tiver a URL de atendimento e só então mostre o iframe. Com o acesso já concedido à sua página, o navegador repassa câmera e microfone para a consulta pelo atributo allow. Feche o stream logo depois: ele só serve para disparar o pedido de permissão.
Na Web, a sala de consulta também oferece compartilhamento de tela, usado pelo beneficiário para mostrar um exame, um documento ou qualquer outra imagem ao profissional durante o atendimento. Esse recurso é exclusivo do iframe (não existe no WebView do app) e depende da permissão display-capture: sem ela no allow do iframe (e sem restrição no Permissions-Policy da sua página), o navegador bloqueia o compartilhamento de tela dentro da consulta.
async function abrirConsulta(urlAtendimento: string) {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
stream.getTracks().forEach((track) => track.stop());
setUrlConsulta(urlAtendimento);
} catch (erro) {
if (erro instanceof DOMException && erro.name === 'NotAllowedError') {
avisar('Libere a câmera e o microfone nas configurações do navegador para iniciar a consulta.');
} else if (erro instanceof DOMException && erro.name === 'NotFoundError') {
avisar('Nenhuma câmera ou microfone foi encontrado neste dispositivo.');
} else {
avisar('Não foi possível acessar a câmera e o microfone.');
}
}
}<iframe
src={urlConsulta}
allow="camera; microphone; display-capture; fullscreen"
style={{ width: '100%', height: '80vh', border: 0 }}
/>Headers da sua página
A URL de atendimento não tem domínio fixo: ele pode variar de um atendimento para outro. Use sempre a URL recebida da API, sem montar nem validar o domínio no seu código.
Permissions-Policy— se o seu site não declaracamera,microphoneedisplay-capturenesse header, não precisa fazer nada: oallowdoiframejá delega o acesso para a origem da URL carregada. Se declara, restringir a(self)bloqueia a consulta.Content-Security-Policy— se o seu site restringeframe-src, a URL de atendimento precisa ser permitida. Como o domínio pode mudar, alinhe com a Rapidoc antes de fechar uma lista.
Permissions-Policy: camera=(self), microphone=(self), display-capture=(self)
Content-Security-Policy: frame-src 'self'React Native (WebView)
O exemplo usa react-native-webview para abrir a consulta e react-native-permissions para pedir o acesso ao usuário. Declare as permissões no Android e no iOS, peça ao usuário e só então abra o WebView.
Android — AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.microphone" android:required="false" />
<application>
<!-- ... -->
</application>
</manifest>android:required="false" mantém o app disponível na loja para aparelhos sem câmera ou autofoco.
iOS — Info.plist
<key>NSCameraUsageDescription</key>
<string>Usamos a câmera para que o profissional possa ver você durante a consulta.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Usamos o microfone para que o profissional possa ouvir você durante a consulta.</string>Sem essas chaves, o iOS encerra o app no momento em que a câmera é pedida. O texto aparece para o usuário na janela de permissão — escreva o motivo com clareza.
No iOS, o react-native-permissions também precisa das permissões declaradas no Podfile:
setup_permissions([
'Camera',
'Microphone',
])Pedir a permissão
import { Platform } from 'react-native';
import { PERMISSIONS, RESULTS, openSettings, requestMultiple } from 'react-native-permissions';
const PERMISSOES =
Platform.OS === 'ios'
? [PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.MICROPHONE]
: [PERMISSIONS.ANDROID.CAMERA, PERMISSIONS.ANDROID.RECORD_AUDIO];
export async function liberarCameraMicrofone(): Promise<boolean> {
const resultado = Object.values(await requestMultiple(PERMISSOES));
if (resultado.every((status) => status === RESULTS.GRANTED)) {
return true;
}
if (resultado.some((status) => status === RESULTS.BLOCKED)) {
await openSettings();
}
return false;
}BLOCKED significa que o usuário negou antes e o sistema não mostra mais a janela: a única saída é levá-lo às configurações do app.
WebView
import { WebView } from 'react-native-webview';
export function Consulta({ urlAtendimento }: { urlAtendimento: string }) {
return (
<WebView
source={{ uri: urlAtendimento }}
javaScriptEnabled
allowsInlineMediaPlayback
mediaPlaybackRequiresUserAction={false}
mediaCapturePermissionGrantType="grantIfSameHostElsePrompt"
/>
);
}| Prop | Por quê |
|---|---|
javaScriptEnabled | A sala de consulta é uma aplicação web e depende de JavaScript. |
allowsInlineMediaPlayback | No iOS, sem ele o vídeo abre em tela cheia do player nativo em vez de ficar dentro da sala. |
mediaPlaybackRequiresUserAction | false deixa o áudio e o vídeo do profissional começarem sem um toque extra do usuário. |
mediaCapturePermissionGrantType | No iOS, concede câmera e microfone à página da consulta sem repetir o pedido a cada acesso, desde que o app já tenha a permissão. |
No Android, o react-native-webview repassa o pedido da página para as permissões do app — por isso CAMERA e RECORD_AUDIO precisam estar concedidas antes de abrir o WebView.
Revisão dos endpoints
Quando cada chamada vale a pena, e o que dela fica salvo do seu lado.
| Endpoint | Salvar na sua base | Quando chamar |
|---|---|---|
GET /plans | Sim — planos e especialidades | Quando o contrato mudar, ou uma vez ao dia |
GET /specialties | Não é necessário | Evite: as especialidades já vêm em specialties[] do GET /plans |
POST /beneficiaries | Sim — uuid de cada vida | No cadastro, em lotes de até 10 |
PUT /beneficiaries/{uuid} | Atualize o registro local | Só quando algo mudou, com o array plans completo (titular) ou o holder (dependente) |
GET /beneficiaries/{cpf} | Sim — recupera o uuid | Só quando a vida não está na sua base |
GET /beneficiaries/document/{documentNumber} | Sim — recupera o uuid | Mesmo caso, para estrangeiros identificados por documento |
GET /beneficiaries | Sim | Carga inicial ou conciliação eventual — nunca a cada tela |
GET /beneficiary-medical-referrals | Sim — os PENDING | Sincronização periódica, com status=PENDING |
GET /beneficiaries/{uuid}/medical-referrals | Sim — os PENDING da vida | Fallback, só quando não há pendente salvo para a vida |
GET /specialty-availability | Não | Uma vez por especialidade, com dateInitial/dateFinal cobrindo até 1 mês a partir do dia da consulta; reconsulte se o horário foi tomado |
POST /appointments | Não — daqui em diante, síncrono com a API | Uma vez por agendamento |
GET /beneficiaries/{uuid}/request-appointment | Não | Uma vez, quando o beneficiário pede o pronto atendimento |
GET /appointments | Não | Com filtros (beneficiaryUuid, status, período), nunca a lista inteira em loop |
DELETE /appointments/{uuid} | Não | Uma vez, com 48 horas de antecedência |
Custo em chamadas
O mesmo agendamento com especialista, com e sem os dados de referência salvos.
| Etapa | Sem base local | Com base local |
|---|---|---|
| Planos e especialidades da vida | GET /plans | Sua base |
| Encaminhamento pendente | GET /beneficiary-medical-referrals | Sua base (sincronizada; API da vida só no fallback) |
| Horários | GET /specialty-availability a cada dia navegado | GET /specialty-availability do mês, uma vez |
| Agendar | POST /appointments | POST /appointments |
| Total | 3 + uma por dia navegado | 2 chamadas |
Faça e não faça
- Salvar
uuid,cpfe documento do beneficiário assim que o cadastro responder - Guardar planos e o
uuiddas especialidades de cada plano - Consultar encaminhamentos
PENDINGna sua base antes da API - Buscar a disponibilidade do mês numa chamada só
- Sincronizar periodicamente os encaminhamentos PENDING do cliente
- Centralizar as chamadas numa fila no backend, abaixo de 30 por minuto
- Pedir câmera e microfone antes de abrir a URL de atendimento
- Servir em HTTPS a página que abre o iframe da consulta
- Abrir exatamente a URL de atendimento recebida da API, sem fixar o domínio no código
- Explicar no
Info.plist, em linguagem simples, por que a câmera e o microfone são usados - Quando a permissão estiver bloqueada, orientar o usuário e levá-lo às configurações
- Chamar
GET /plansouGET /specialtiesa cada tela - Buscar o beneficiário na API para descobrir um uuid que já estava salvo
- Consultar a disponibilidade dia a dia enquanto o usuário navega no calendário
- Guardar horários livres para agendar depois — eles podem ser tomados
- Reenviar na hora uma chamada recusada, sem espera
- Ignorar erro de certificado SSL no WebView (ex.:
onReceivedSslErrorseguido deproceed()) - Liberar tráfego HTTP sem criptografia (
usesCleartextTrafficno Android,NSAllowsArbitraryLoadsno iOS) - Restringir
camera/microphonea(self)na página que abre o iframe - Deixar aberto o stream do
getUserMediausado só para pedir a permissão - Pedir permissões que a consulta não usa, como localização ou armazenamento

