Agravos - Endpoints
Referência completa dos endpoints do módulo de agravos da API Sinapse
Endpoints de Agravos
O módulo de agravos gerencia casos de doenças de notificação obrigatória como dengue, zika e chikungunya.
Listar Tipos de Agravos
Retorna todos os tipos de agravos disponíveis no sistema.
GET /api/v1/agravos/tipos
Authorization: Bearer {access_token}Permissão necessária: agravos.listar
Success Response (200):
{
"total": 3,
"tipos": [
{
"codigo": "dengue",
"nome": "Dengue",
"cid10_codes": ["A90", "A91"],
"descricao": "Dengue clássica e hemorrágica",
"notificacao_obrigatoria": true
},
{
"codigo": "zika",
"nome": "Zika",
"cid10_codes": ["U06.9"],
"descricao": "Infecção pelo vírus Zika",
"notificacao_obrigatoria": true
},
{
"codigo": "chikungunya",
"nome": "Chikungunya",
"cid10_codes": ["A92.0"],
"descricao": "Febre de Chikungunya",
"notificacao_obrigatoria": true
}
]
}JavaScript:
async function listarTiposAgravos() {
const response = await fetch('/api/v1/agravos/tipos', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
if (response.ok) {
const tipos = await response.json();
tipos.items.forEach(tipo => {
console.log(`${tipo.nome} (${tipo.codigo})`);
console.log(`CID-10: ${tipo.cid10_codes.join(', ')}`);
});
}
}Listar Casos
Lista casos de agravos com suporte a filtros e paginação.
GET /api/v1/agravos/casos
Authorization: Bearer {access_token}Query Parameters:
| Parâmetro | Tipo | Descrição |
|---|---|---|
page | integer | Número da página (padrão: 1) |
size | integer | Itens por página (padrão: 50, máx: 100) |
agravo_codigo | string | Filtrar por código do agravo (dengue, zika, chikungunya) |
data_inicio | date | Data inicial de notificação (YYYY-MM-DD) |
data_fim | date | Data final de notificação (YYYY-MM-DD) |
estado | string | UF de residência (ex: SP) |
municipio | string | Município de residência |
classificacao | string | Classificação final (confirmado, descartado, etc) |
sort | string | Campo para ordenação [Em breve] |
order | string | Direção (asc, desc) [Em breve] |
Permissão necessária: agravos.listar
Success Response (200):
{
"total": 1523,
"page": 1,
"size": 50,
"pages": 31,
"tipos": [
{
"id": 1234,
"tipo_agravo": "dengue",
"numero_notificacao": "2025-001234",
"data_notificacao": "2025-07-29T10:30:00-03:00",
"data_inicio_sintomas": "2025-07-25",
"paciente": {
"nome": "PACIENTE ANONIMIZADO",
"idade": 35,
"sexo": "F",
"gestante": false
},
"endereco": {
"municipio_residencia": "São Paulo",
"estado_residencia": "SP",
"bairro": "Centro",
"zona": "urbana"
},
"classificacao_final": "dengue_com_sinais_alarme",
"criterio_confirmacao": "laboratorial",
"evolucao": "cura",
"hospitalizacao": true,
"dados_especificos": {
"sorotipo": "DENV-2",
"prova_laco": "positiva",
"manifestacoes_hemorragicas": true
},
"unidade_notificante": {
"nome": "Hospital Municipal",
"cnes": "1234567"
},
"criado_em": "2025-07-29T10:35:00-03:00",
"atualizado_em": "2025-07-29T14:20:00-03:00"
}
],
// "agregacoes": {...} **[Em breve]**
}Python com Filtros:
import requests
from datetime import datetime, timedelta
def buscar_casos_dengue_sp():
# Casos de dengue em SP nos últimos 30 dias
data_fim = datetime.now().date()
data_inicio = data_fim - timedelta(days=30)
params = {
'tipo_agravo': 'dengue',
'estado': 'SP',
'data_notificacao_inicio': data_inicio.isoformat(),
'data_notificacao_fim': data_fim.isoformat(),
'page': 1,
'size': 100,
'sort': 'data_notificacao',
'order': 'desc'
}
headers = {'Authorization': f'Bearer {token}'}
response = requests.get(
'https://api.sinapse.org.br/v1/agravos/casos',
params=params,
headers=headers
)
if response.status_code == 200:
data = response.json()
print(f"Total de casos: {data['total']}")
# Processar casos
for caso in data['items']:
print(f"Caso {caso['numero_notificacao']}: "
f"{caso['municipio_residencia']} - "
f"{caso['classificacao_final']}")
return dataJavaScript com Paginação:
class AgravosPaginator {
constructor(apiClient) {
this.api = apiClient;
}
async *listarTodosCasos(filtros = {}) {
let page = 1;
let hasMore = true;
while (hasMore) {
const params = new URLSearchParams({
...filtros,
page: page,
size: 100
});
const response = await this.api.get(
`/agravos/casos?${params}`
);
if (!response.ok) {
throw new Error('Erro ao buscar casos');
}
const data = await response.json();
// Yield cada caso individualmente
for (const caso of data.items) {
yield caso;
}
hasMore = page < data.pages;
page++;
}
}
}
// Uso
const paginator = new AgravosPaginator(apiClient);
for await (const caso of paginator.listarTodosCasos({
tipo_agravo: 'dengue',
estado: 'RJ'
})) {
console.log(`Processando caso ${caso.numero_notificacao}`);
// Processar cada caso
}Criar Caso
Registra um novo caso de agravo.
POST /api/v1/agravos/casos
Authorization: Bearer {access_token}
Content-Type: application/jsonBody Parameters:
{
"tipo_agravo": "dengue",
"numero_notificacao": "2025-001235",
"data_notificacao": "2025-07-29",
"data_inicio_sintomas": "2025-07-25",
"paciente_nome": "João da Silva",
"paciente_data_nascimento": "1990-05-15",
"paciente_sexo": "M",
"paciente_gestante": false,
"paciente_raca": "parda",
"paciente_escolaridade": "medio_completo",
"municipio_residencia": "São Paulo",
"estado_residencia": "SP",
"bairro_residencia": "Vila Mariana",
"zona_residencia": "urbana",
"municipio_infeccao": "São Paulo",
"estado_infeccao": "SP",
"pais_infeccao": "Brasil",
"classificacao_inicial": "dengue_classico",
"criterio_confirmacao": "clinico_epidemiologico",
"dados_especificos": {
"febre": true,
"mialgia": true,
"cefaleia": true,
"exantema": false,
"nausea": true,
"dor_retro_orbital": true,
"artralgia": true,
"petequias": false,
"prova_laco": "nao_realizada",
"sinais_alarme": false
}
}Permissão necessária: agravos.criar
Success Response (201):
{
"id": 1235,
"tipo_agravo": "dengue",
"numero_notificacao": "2025-001235",
"status": "em_investigacao",
"criado_em": "2025-07-29T11:00:00-03:00",
"mensagem": "Caso criado com sucesso"
}Error Response (422):
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Dados inválidos",
"details": [
{
"field": "paciente_data_nascimento",
"message": "Data de nascimento não pode ser futura"
},
{
"field": "dados_especificos.prova_laco",
"message": "Valor inválido. Opções: positiva, negativa, nao_realizada"
}
]
}
}React Form Component:
function FormularioAgravo() {
const [formData, setFormData] = useState({
tipo_agravo: 'dengue',
numero_notificacao: '',
data_notificacao: new Date().toISOString().split('T')[0],
// ... outros campos
});
const [dadosEspecificos, setDadosEspecificos] = useState({
febre: false,
mialgia: false,
cefaleia: false,
// ... outros sintomas
});
const handleSubmit = async (e) => {
e.preventDefault();
const payload = {
...formData,
dados_especificos: dadosEspecificos
};
try {
const response = await fetch('/api/v1/agravos/casos', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify(payload)
});
if (response.ok) {
const result = await response.json();
alert(`Caso ${result.numero_notificacao} criado com sucesso!`);
// Redirecionar ou limpar formulário
} else if (response.status === 422) {
const error = await response.json();
// Mostrar erros de validação
error.error.details.forEach(detail => {
console.error(`${detail.field}: ${detail.message}`);
});
}
} catch (error) {
console.error('Erro ao criar caso:', error);
}
};
return (
<form onSubmit={handleSubmit}>
{/* Campos do formulário */}
</form>
);
}Buscar Caso
Retorna detalhes completos de um caso específico.
GET /api/v1/agravos/casos/{id}
Authorization: Bearer {access_token}Path Parameters:
| Parâmetro | Tipo | Descrição |
|---|---|---|
id | integer | ID do caso |
Query Parameters:
| Parâmetro | Tipo | Descrição |
|---|---|---|
include | string | Incluir relações: historico,anexos,investigacao [Em breve] |
Permissão necessária: agravos.listar
Success Response (200):
{
"id": 1234,
"tipo_agravo": "dengue",
"numero_notificacao": "2025-001234",
"data_notificacao": "2025-07-29T10:30:00-03:00",
"data_inicio_sintomas": "2025-07-25",
"paciente": {
"nome": "PACIENTE ANONIMIZADO",
"idade": 35,
"sexo": "F",
"gestante": false,
"raca": "branca",
"escolaridade": "superior_completo",
"ocupacao": "professora"
},
"endereco": {
"logradouro": "Rua das Flores",
"numero": "123",
"bairro": "Centro",
"municipio_residencia": "São Paulo",
"estado_residencia": "SP",
"cep": "01234-567",
"zona": "urbana",
"latitude": -23.550520,
"longitude": -46.633308
},
"dados_clinicos": {
"sinais_sintomas": {
"febre": true,
"mialgia": true,
"cefaleia": true,
"exantema": false,
"nausea": true,
"dor_retro_orbital": true,
"artralgia": true,
"dor_abdominal": false,
"vomito_persistente": false,
"hepatomegalia": false,
"sangramento_mucosa": false,
"letargia": false,
"acumulo_liquidos": false
},
"sinais_alarme": false,
"comorbidades": ["hipertensao", "diabetes"],
"hospitalizacao": true,
"data_internacao": "2025-07-26",
"uti": false
},
"dados_laboratoriais": {
"ns1": "reagente",
"igm": "nao_reagente",
"pcr": "detectado",
"isolamento_viral": "DENV-2",
"plaquetas": 95000,
"hematocrito": 48
},
"classificacao_final": "dengue_com_sinais_alarme",
"criterio_confirmacao": "laboratorial",
"evolucao": "cura",
"data_encerramento": "2025-08-05",
"investigacao": {
"locais_frequentados": [
{
"local": "Trabalho",
"endereco": "Av. Paulista, 1000",
"periodo": "diariamente"
}
],
"viagem_recente": false,
"contato_casos_similares": true
},
"historico": [
{
"data": "2025-07-29T10:35:00-03:00",
"acao": "caso_criado",
"usuario": "Maria Silva",
"detalhes": "Notificação inicial"
},
{
"data": "2025-07-30T14:20:00-03:00",
"acao": "resultado_laboratorial",
"usuario": "Sistema",
"detalhes": "PCR positivo para DENV-2"
}
],
"unidade_notificante": {
"nome": "Hospital Municipal",
"cnes": "1234567",
"telefone": "(11) 3456-7890",
"responsavel": "Dr. João Santos"
}
}Atualizar Caso
Atualiza informações de um caso existente.
PUT /api/v1/agravos/casos/{id}
Authorization: Bearer {access_token}
Content-Type: application/jsonBody Parameters:
Envie apenas os campos que deseja atualizar:
{
"classificacao_final": "dengue_grave",
"evolucao": "obito",
"data_obito": "2025-08-01",
"dados_laboratoriais": {
"igm": "reagente",
"plaquetas": 45000
}
}Permissão necessária: agravos.editar
Success Response (200):
{
"id": 1234,
"message": "Caso atualizado com sucesso",
// "campos_alterados": [...] **[Em breve]**
"atualizado_em": "2025-07-30T16:45:00-03:00"
}JavaScript - Atualização Parcial:
async function atualizarResultadoLaboratorial(casoId, resultados) {
const response = await fetch(`/api/v1/agravos/casos/${casoId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
dados_laboratoriais: resultados,
classificacao_final: determinarClassificacao(resultados)
})
});
if (response.ok) {
const result = await response.json();
console.log('Campos alterados:', result.campos_alterados);
}
}
function determinarClassificacao(lab) {
if (lab.plaquetas < 50000 || lab.hematocrito > 50) {
return 'dengue_grave';
} else if (lab.plaquetas < 100000) {
return 'dengue_com_sinais_alarme';
}
return 'dengue_sem_sinais_alarme';
}Deletar Caso
Remove um caso do sistema (soft delete).
DELETE /api/v1/agravos/casos/{id}
Authorization: Bearer {access_token}Query Parameters:
| Parâmetro | Tipo | Descrição |
|---|---|---|
motivo | string | Motivo da exclusão [Em breve] |
force | boolean | Exclusão permanente (requer permissão especial) [Em breve] |
Permissão necessária: agravos.deletar
Success Response (204):
Sem conteúdo de resposta
Error Response (400):
{
"error": {
"code": "MISSING_REASON",
"message": "Motivo da exclusão é obrigatório"
}
}Importação em Massa [Em breve]
Importa múltiplos casos através de arquivo.
POST /api/v1/agravos/casos/bulk
Authorization: Bearer {access_token}
Content-Type: multipart/form-dataForm Data:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
file | file | Sim | Arquivo CSV ou Excel |
formato | string | Sim | Formato: csv, xlsx |
modo | string | Não | Modo: validar, importar (padrão: validar) |
ignorar_erros | boolean | Não | Continuar em caso de erros |
Permissão necessária: agravos.importar
Success Response (202):
{
"job_id": "import_20250729_110000",
"status": "processing",
"status_url": "/api/v1/jobs/import_20250729_110000",
"message": "Importação iniciada"
}Verificar Status:
{
"job_id": "import_20250729_110000",
"status": "completed",
"progress": 100,
"resultado": {
"total_linhas": 500,
"importados": 485,
"erros": 15,
"avisos": 23,
"tempo_processamento": "45.3s",
"erros_detalhados": [
{
"linha": 23,
"numero_notificacao": "2025-001256",
"erro": "Data de nascimento inválida"
}
]
}
}HTML Form com Progress:
<form id="importForm">
<input type="file" id="fileInput" accept=".csv,.xlsx" />
<button type="submit">Importar</button>
<div id="progress" style="display: none;">
<progress id="progressBar" max="100" value="0"></progress>
<span id="progressText">0%</span>
</div>
</form>
<script>
document.getElementById('importForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (!file) {
alert('Selecione um arquivo');
return;
}
const formData = new FormData();
formData.append('file', file);
formData.append('formato', file.name.endsWith('.xlsx') ? 'xlsx' : 'csv');
formData.append('modo', 'importar');
// Iniciar importação
const response = await fetch('/api/v1/agravos/casos/bulk', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`
},
body: formData
});
if (response.ok) {
const { job_id, status_url } = await response.json();
// Mostrar progresso
document.getElementById('progress').style.display = 'block';
// Verificar status periodicamente
const interval = setInterval(async () => {
const statusResponse = await fetch(status_url, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
const status = await statusResponse.json();
// Atualizar progresso
document.getElementById('progressBar').value = status.progress;
document.getElementById('progressText').textContent = `${status.progress}%`;
if (status.status === 'completed' || status.status === 'failed') {
clearInterval(interval);
if (status.status === 'completed') {
alert(`Importação concluída!
Total: ${status.resultado.total_linhas}
Importados: ${status.resultado.importados}
Erros: ${status.resultado.erros}`);
} else {
alert('Erro na importação');
}
}
}, 2000); // Verificar a cada 2 segundos
}
});
</script>Estatísticas
Retorna estatísticas resumidas dos casos.
GET /api/v1/agravos/casos/estatisticas/resumo
Authorization: Bearer {access_token}Query Parameters:
| Parâmetro | Tipo | Descrição |
|---|---|---|
data_inicio | date | Data inicial |
data_fim | date | Data final |
tipo_agravo | string | Filtrar por tipo |
estado | string | Filtrar por UF |
agregacao | string | Tipo: dia, semana, mes [Em breve] |
metricas | string | Métricas: casos,obitos,incidencia [Em breve] |
Permissão necessária: agravos.estatisticas
Success Response (200):
{
"periodo": {
"inicio": "2025-07-01",
"fim": "2025-07-29"
},
"total": 3456,
"por_classificacao": {
"confirmado": 2890,
"descartado": 566
},
"hospitalizados": 456,
"obitos": 23,
"taxa_hospitalizacao": 13.2,
"taxa_obito": 0.7,
// "por_tipo": {...} **[Em breve]**
// "serie_temporal": [...] **[Em breve]**
// "distribuicao_geografica": {...} **[Em breve]**
// "faixa_etaria": {...} **[Em breve]**
}Dashboard com Chart.js:
async function carregarEstatisticas() {
const params = new URLSearchParams({
data_inicio: '2025-07-01',
data_fim: '2025-07-29',
agregacao: 'dia',
metricas: 'casos,obitos,incidencia'
});
const response = await fetch(
`/api/v1/agravos/casos/estatisticas?${params}`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
const stats = await response.json();
// Gráfico de série temporal
const ctx = document.getElementById('graficoTemporal').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: stats.serie_temporal.map(d => d.data),
datasets: [{
label: 'Casos Novos',
data: stats.serie_temporal.map(d => d.casos_novos),
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}, {
label: 'Óbitos',
data: stats.serie_temporal.map(d => d.obitos_novos),
borderColor: 'rgb(255, 99, 132)',
tension: 0.1
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Evolução Temporal dos Casos'
}
}
}
});
// Gráfico de pizza por tipo
const ctx2 = document.getElementById('graficoPorTipo').getContext('2d');
new Chart(ctx2, {
type: 'doughnut',
data: {
labels: Object.keys(stats.por_tipo),
datasets: [{
data: Object.values(stats.por_tipo).map(t => t.casos),
backgroundColor: [
'rgb(255, 99, 132)',
'rgb(54, 162, 235)',
'rgb(255, 205, 86)'
]
}]
}
});
// Tabela de distribuição geográfica
const tbody = document.getElementById('tabelaGeografica');
tbody.innerHTML = '';
Object.entries(stats.distribuicao_geografica).forEach(([uf, dados]) => {
const row = tbody.insertRow();
row.insertCell(0).textContent = uf;
row.insertCell(1).textContent = dados.casos.toLocaleString();
row.insertCell(2).textContent = `${dados.incidencia.toFixed(1)}/100mil`;
row.insertCell(3).textContent = dados.municipios_afetados;
});
}Mapa de Calor [Em breve]
Retorna dados para visualização geográfica.
GET /api/v1/agravos/casos/mapa-calor
Authorization: Bearer {access_token}Query Parameters:
| Parâmetro | Tipo | Descrição |
|---|---|---|
nivel | string | Nível: municipio, bairro, cep |
tipo_agravo | string | Filtrar por tipo |
periodo | integer | Últimos N dias |
bounds | string | Limites do mapa (lat,lng,lat,lng) |
Success Response (200):
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-46.633308, -23.550520]
},
"properties": {
"municipio": "São Paulo",
"bairro": "Centro",
"casos": 45,
"intensidade": 0.8,
"radius": 500
}
}
],
"metadata": {
"total_pontos": 234,
"max_casos": 89,
"min_casos": 1,
"periodo": "ultimos_30_dias"
}
}Anexos [Em breve]
Upload de Anexo [Em breve]
POST /api/v1/agravos/casos/{id}/anexos
Authorization: Bearer {access_token}
Content-Type: multipart/form-dataForm Data:
| Campo | Tipo | Descrição |
|---|---|---|
arquivo | file | Arquivo (PDF, imagem) |
tipo | string | Tipo: exame, notificacao, outro |
descricao | string | Descrição do anexo |
Success Response (201):
{
"id": 789,
"nome": "hemograma_20250729.pdf",
"tipo": "exame",
"tamanho": 245678,
"url": "/api/v1/agravos/anexos/789/download",
"uploaded_at": "2025-07-29T11:30:00-03:00"
}