Integração de Lista de Trabalho
A API de Lista de Trabalho do TranscriMed permite integração bidirecional perfeita com sistemas RIS/PACS, possibilitando que prestadores de saúde otimizem seu fluxo de documentação médica.
Visão Geral
A integração de lista de trabalho do TranscriMed oferece:
- Integração de Entrada: Receber itens de lista de trabalho do seu sistema RIS/PACS
- Processamento: Médicos visualizam, selecionam e processam exames no TranscriMed
- Integração de Saída: Enviar automaticamente documentos médicos completos de volta ao sistema de origem
Como Funciona
- Ingestão de Lista de Trabalho: Seu sistema RIS/PACS envia dados de exames para o TranscriMed
- Seleção do Médico: Profissionais médicos visualizam exames disponíveis e selecionam os relevantes
- Documentação: Médicos gravam áudio para os exames selecionados
- Processamento: TranscriMed gera documentos médicos estruturados
- Entrega: Documentos completos são enviados automaticamente de volta ao seu sistema
Formato de Dados
Utilizamos nomes de campos inspirados no DICOM em formato snake_case para compatibilidade com API REST:
Campos Principais
| Campo | Tipo | Descrição | Exemplo |
|---|---|---|---|
accession_number | string | Identificador único do exame | "2024-001234" |
study_uid | string | UID da Instância do Estudo DICOM | "1.2.826.0..." |
patient_id | string | Identificador do paciente | "PAT-456789" |
patient_name | string | Nome completo do paciente | "João Silva" |
modality | string | Modalidade do exame | "CR", "CT", "MR" |
exam_datetime | string | Data/hora do exame (ISO 8601) | "2024-01-15T10:30:00Z" |
exam_description | string | Descrição do procedimento | "Raio-X de Tórax PA/Perfil" |
Campos Opcionais
| Campo | Tipo | Descrição |
|---|---|---|
patient_sex | string | Sexo do paciente (M/F/O/U) |
patient_birth_date | string | Data de nascimento (ISO 8601) |
patient_age | string | Idade em formato livre |
exam_room | string | Sala onde o exame é realizado |
referring_physician | string | Médico solicitante |
hospital_name | string | Nome da instituição |
location | string | Departamento/localização dentro da instituição |
procedure_id | string | Identificador do procedimento |
metadata | object | Dados adicionais específicos do sistema |
Autenticação
1. Registrar Sua Aplicação
- Visite o Portal do Desenvolvedor
- Crie uma conta ou faça login
- Registre sua aplicação com:
- Nome da aplicação
- Nome da empresa/organização
- URI de redirecionamento (para fluxo OAuth2)
2. Solicitar Escopos
Sua aplicação precisa destes escopos:
worklists:write- Enviar itens de lista de trabalho para o TranscriMedworklists:manage- Atualizar status de item da lista de trabalho (opcional)
3. Obter Token de Acesso
Use o fluxo padrão de código de autorização OAuth2:
// Passo 1: Redirecionar usuário para URL de autorização
const authUrl = `https://api.transcrimed.com.br/api/oauth/authorize?` +
`client_id=${clientId}&` +
`response_type=code&` +
`scope=worklists:write&` +
`redirect_uri=${redirectUri}`;
// Passo 2: Trocar código por token de acesso
const response = await fetch('https://api.transcrimed.com.br/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: clientId,
client_secret: clientSecret,
code: authorizationCode,
redirect_uri: redirectUri
})
});
const { access_token } = await response.json();
Enviando Itens de Lista de Trabalho
Exemplo Básico
const worklistItems = [
{
accession_number: "2024-001234",
patient_id: "PAT-456789",
patient_name: "João Silva",
patient_sex: "M",
patient_birth_date: "1980-05-15",
modality: "CR",
exam_datetime: "2024-01-15T10:30:00Z",
exam_room: "Sala 1",
exam_description: "Raio-X de Tórax PA/Perfil",
referring_physician: "Dr. Maria Garcia",
hospital_name: "Hospital Central"
}
];
const response = await fetch('https://api.transcrimed.com.br/api/v1/worklists/ingest', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'id-unico-requisicao-12345'
},
body: JSON.stringify(worklistItems)
});
const result = await response.json();
console.log('Resultado da ingestão:', result);
Formato de Resposta
{
"success": true,
"data": {
"batch_id": "batch_12345-67890",
"inserted": 1,
"updated": 0,
"deduped": 0,
"errors": []
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2024-01-15T10:30:00Z"
}
}
Gerenciando Status da Lista de Trabalho
Você pode opcionalmente atualizar o status dos itens da lista de trabalho:
const response = await fetch(`https://api.transcrimed.com.br/api/v1/worklists/items/${itemId}`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
status: 'in_progress',
reason: 'Exame iniciado na Sala 1'
})
});
Valores de Status Disponíveis
released– Documento liberado (novo padrão)cancelled– Exame cancelado
Compatibilidade: a API ainda aceita completed e o converte automaticamente em released.
Recebendo Documentos Completos
Quando um médico completa um documento médico para um item da lista de trabalho, o TranscriMed tentará automaticamente entregá-lo de volta ao seu sistema usando o endpoint webhook configurado.
Configurando Webhooks
- No Portal do Desenvolvedor, configure seu endpoint webhook
- Defina a URL do endpoint onde deseja receber documentos completos
- Gere e armazene com segurança seu segredo webhook para verificação de assinatura
Exemplo de Payload do Webhook
{
"event": "document.completed",
"worklist_item_id": "550e8400-e29b-41d4-a716-446655440000",
"accession_number": "2024-001234",
"document": {
"id": "doc-uuid-aqui",
"title": "Laudo de Raio-X de Tórax",
"content": "<html>Conteúdo completo do laudo médico...</html>",
"format": "html",
"created_at": "2024-01-15T11:30:00Z"
},
"patient": {
"id": "PAT-456789",
"name": "João Silva"
},
"metadata": {
"processing_duration_ms": 45000,
"template_used": "laudo-radiologia"
}
}
Segurança do Webhook
Todas as requisições webhook são assinadas com HMAC-SHA256:
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return signature === `sha256=${expectedSignature}`;
}
// No seu endpoint webhook
app.post('/webhooks/transcrimed', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature, webhookSecret)) {
return res.status(401).send('Assinatura inválida');
}
// Processar o webhook
console.log('Documento completo:', req.body);
res.status(200).send('OK');
});
Melhores Práticas
1. Idempotência
Sempre inclua um cabeçalho Idempotency-Key para evitar processamento duplicado:
const idempotencyKey = `${systemId}-${timestamp}-${accessionNumber}`;
fetch('/api/v1/worklists/ingest', {
headers: {
'Idempotency-Key': idempotencyKey
}
// ... outras opções
});
2. Tratamento de Erros
Implemente tratamento adequado de erros e lógica de repetição:
async function ingestWorklistItems(items, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch('/api/v1/worklists/ingest', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'Idempotency-Key': generateIdempotencyKey()
},
body: JSON.stringify(items)
});
if (response.ok) {
return await response.json();
}
if (response.status === 401) {
// Renovar token de acesso
await refreshAccessToken();
continue;
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} catch (error) {
console.error(`Tentativa ${attempt} falhou:`, error);
if (attempt === maxRetries) {
throw error;
}
// Backoff exponencial
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
}
}
3. Validação de Dados
Valide seus dados antes de enviar:
function validateWorklistItem(item) {
const errors = [];
// Pelo menos um identificador obrigatório
if (!item.accession_number && !item.study_uid) {
errors.push('accession_number ou study_uid é obrigatório');
}
// Validar modalidade
const validModalities = ['CR', 'CT', 'MR', 'US', 'XA', 'RF', 'DX', 'MG'];
if (item.modality && !validModalities.includes(item.modality)) {
errors.push(`Modalidade inválida: ${item.modality}`);
}
// Validar formato de data
if (item.exam_datetime && !isValidISO8601(item.exam_datetime)) {
errors.push('exam_datetime deve estar no formato ISO 8601');
}
return errors;
}
4. Limitação de Taxa
Respeite os limites de taxa e implemente estratégias de backoff:
class RateLimitedClient {
constructor(accessToken) {
this.accessToken = accessToken;
this.requestQueue = [];
this.processing = false;
}
async makeRequest(url, options) {
return new Promise((resolve, reject) => {
this.requestQueue.push({ url, options, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.processing || this.requestQueue.length === 0) return;
this.processing = true;
while (this.requestQueue.length > 0) {
const { url, options, resolve, reject } = this.requestQueue.shift();
try {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
await new Promise(resolve =>
setTimeout(resolve, (retryAfter || 60) * 1000)
);
this.requestQueue.unshift({ url, options, resolve, reject });
continue;
}
resolve(await response.json());
} catch (error) {
reject(error);
}
// Pequeno atraso entre requisições
await new Promise(resolve => setTimeout(resolve, 100));
}
this.processing = false;
}
}
Testando
Modo de Teste
Use credenciais de teste para experimentar sem afetar a produção:
- Solicite credenciais de teste com prefixo
test_tc_ - Use o mesmo endpoint de produção
- A API automaticamente retorna dados fictícios para clientes de teste
const testClient = {
client_id: 'test_tc_abc123',
client_secret: 'test_tcs_xyz789'
};
// Isso ativará automaticamente o modo de teste
const response = await fetch('/api/v1/worklists/ingest', {
headers: {
'Authorization': `Bearer ${testAccessToken}`
},
body: JSON.stringify(testWorklistItems)
});
Dados de Teste Exemplo
Use dados de teste realistas:
const testWorklistItems = [
{
accession_number: "TEST-2024-001",
patient_id: "TEST-PAT-001",
patient_name: "Paciente Teste",
patient_sex: "M",
patient_birth_date: "1980-01-01",
modality: "CR",
exam_datetime: "2024-01-15T10:00:00Z",
exam_description: "Raio-X de Tórax Teste",
referring_physician: "Dr. Médico Teste",
hospital_name: "Hospital Teste"
}
];
Solução de Problemas
Problemas Comuns
Erros de Autenticação (401)
- Verifique se seu token de acesso é válido e não expirou
- Confirme que você tem os escopos necessários (
worklists:write) - Certifique-se de estar usando as credenciais de cliente corretas
Erros de Validação (400)
- Verifique se todos os campos obrigatórios estão presentes
- Confirme que os formatos de data são ISO 8601
- Certifique-se de que os códigos de modalidade são válidos
- Pelo menos um de
accession_numberoustudy_uiddeve ser fornecido
Limitação de Taxa (429)
- Implemente backoff exponencial
- Respeite o cabeçalho
Retry-After - Considere reduzir a frequência de requisições
Problemas de Entrega de Webhook
- Verifique se seu endpoint webhook está acessível
- Confirme a implementação da verificação de assinatura
- Certifique-se de que seu endpoint responde com status 200
- Revise os logs de webhook no Portal do Desenvolvedor
Modo de Debug
Habilite log detalhado para solucionar problemas:
const DEBUG = process.env.NODE_ENV === 'development';
async function debugRequest(url, options) {
if (DEBUG) {
console.log('Requisição:', { url, options });
}
const response = await fetch(url, options);
const data = await response.json();
if (DEBUG) {
console.log('Resposta:', {
status: response.status,
headers: Object.fromEntries(response.headers),
data
});
}
return { response, data };
}
Precisa de Ajuda?
- Documentação da API: Referência Interativa da API
- Exemplos de Código: Exemplos adicionais
- Suporte: Entre em contato com developers@transcrimed.com.br
Pronto para integrar? Comece com nosso Guia de Início ou explore a Referência da API.