Worklist Integration
The TranscriMed Worklist API enables seamless bidirectional integration with RIS/PACS systems, allowing healthcare providers to streamline their medical documentation workflow.
Overview
TranscriMed's worklist integration provides:
- Inbound Integration: Receive worklist items from your RIS/PACS system
- Processing: Doctors view, select, and process exams in TranscriMed
- Outbound Integration: Automatically send completed medical documents back to the source system
How It Works
graph LR
A[RIS/PACS System] -->|Send Worklist| B[TranscriMed API]
B --> C[Doctor Dashboard]
C -->|Select Exam| D[Audio Recording]
D -->|Process| E[Medical Document]
E -->|Auto-delivery| A
- Worklist Ingestion: Your RIS/PACS system sends exam data to TranscriMed
- Doctor Selection: Medical professionals see available exams and select relevant ones
- Documentation: Doctors record audio for selected exams
- Processing: TranscriMed generates structured medical documents
- Delivery: Completed documents are automatically sent back to your system
Data Format
We use DICOM-inspired field names in snake_case format for REST API compatibility:
Core Fields
| Field | Type | Description | Example |
|---|---|---|---|
accession_number | string | Unique exam identifier | "2024-001234" |
study_uid | string | DICOM Study Instance UID | "1.2.826.0..." |
patient_id | string | Patient identifier | "PAT-456789" |
patient_name | string | Patient full name | "John Smith" |
modality | string | Exam modality | "CR", "CT", "MR" |
exam_datetime | string | Exam date/time (ISO 8601) | "2024-01-15T10:30:00Z" |
exam_description | string | Procedure description | "Chest X-Ray PA/Lateral" |
Optional Fields
| Field | Type | Description |
|---|---|---|
patient_sex | string | Patient gender (M/F/O/U) |
patient_birth_date | string | Birth date (ISO 8601) |
patient_age | string | Age in free format |
exam_room | string | Room where exam is performed |
referring_physician | string | Requesting physician |
hospital_name | string | Institution name |
location | string | Department/location within institution |
procedure_id | string | Procedure identifier |
metadata | object | Additional system-specific data |
Authentication
1. Register Your Application
- Visit the Developer Portal
- Create an account or sign in
- Register your application with:
- Application name
- Company/organization name
- Redirect URI (for OAuth2 flow)
2. Request Scopes
Your application needs these scopes:
worklists:write- Send worklist items to TranscriMedworklists:manage- Update worklist item status (optional)
3. Obtain Access Token
Use the standard OAuth2 authorization code flow:
// Step 1: Redirect user to authorization URL
const authUrl = `https://api.transcrimed.com.br/api/oauth/authorize?` +
`client_id=${clientId}&` +
`response_type=code&` +
`scope=worklists:write&` +
`redirect_uri=${redirectUri}`;
// Step 2: Exchange code for access token
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();
Sending Worklist Items
Basic Example
const worklistItems = [
{
accession_number: "2024-001234",
patient_id: "PAT-456789",
patient_name: "John Smith",
patient_sex: "M",
patient_birth_date: "1980-05-15",
modality: "CR",
exam_datetime: "2024-01-15T10:30:00Z",
exam_room: "Room 1",
exam_description: "Chest X-Ray PA/Lateral",
referring_physician: "Dr. Maria Garcia",
hospital_name: "Central Hospital"
}
];
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': 'unique-request-id-12345'
},
body: JSON.stringify(worklistItems)
});
const result = await response.json();
console.log('Ingestion result:', result);
Response Format
{
"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"
}
}
Managing Worklist Status
You can optionally update the status of worklist items:
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: 'Exam started in Room 1'
})
});
Available Status Values
new- Newly received itemin_progress- Currently being processedcompleted- Processing completedcancelled- Exam cancelledarchived- Item archived
Receiving Completed Documents
When a doctor completes a medical document for a worklist item, TranscriMed will automatically attempt to deliver it back to your system using the configured webhook endpoint.
Setting Up Webhooks
- In the Developer Portal, configure your webhook endpoint
- Set the endpoint URL where you want to receive completed documents
- Generate and securely store your webhook secret for signature verification
Webhook Payload Example
{
"event": "document.completed",
"worklist_item_id": "550e8400-e29b-41d4-a716-446655440000",
"accession_number": "2024-001234",
"document": {
"id": "doc-uuid-here",
"title": "Chest X-Ray Report",
"content": "<html>Full medical report content...</html>",
"format": "html",
"created_at": "2024-01-15T11:30:00Z"
},
"patient": {
"id": "PAT-456789",
"name": "John Smith"
},
"metadata": {
"processing_duration_ms": 45000,
"template_used": "radiology-report"
}
}
Webhook Security
All webhook requests are signed with 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}`;
}
// In your webhook endpoint
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('Invalid signature');
}
// Process the webhook
console.log('Document completed:', req.body);
res.status(200).send('OK');
});
Best Practices
1. Idempotency
Always include an Idempotency-Key header to prevent duplicate processing:
const idempotencyKey = `${systemId}-${timestamp}-${accessionNumber}`;
fetch('/api/v1/worklists/ingest', {
headers: {
'Idempotency-Key': idempotencyKey
}
// ... other options
});
2. Error Handling
Implement proper error handling and retry logic:
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) {
// Refresh access token
await refreshAccessToken();
continue;
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} catch (error) {
console.error(`Attempt ${attempt} failed:`, error);
if (attempt === maxRetries) {
throw error;
}
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
}
}
3. Data Validation
Validate your data before sending:
function validateWorklistItem(item) {
const errors = [];
// At least one identifier required
if (!item.accession_number && !item.study_uid) {
errors.push('Either accession_number or study_uid is required');
}
// Validate modality
const validModalities = ['CR', 'CT', 'MR', 'US', 'XA', 'RF', 'DX', 'MG'];
if (item.modality && !validModalities.includes(item.modality)) {
errors.push(`Invalid modality: ${item.modality}`);
}
// Validate date format
if (item.exam_datetime && !isValidISO8601(item.exam_datetime)) {
errors.push('exam_datetime must be in ISO 8601 format');
}
return errors;
}
4. Rate Limiting
Respect rate limits and implement backoff strategies:
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);
}
// Small delay between requests
await new Promise(resolve => setTimeout(resolve, 100));
}
this.processing = false;
}
}
Testing
Test Mode
Use test credentials to experiment without affecting production:
- Request test credentials with
test_tc_prefix - Use the same production endpoint
- The API automatically returns mock data for test clients
const testClient = {
client_id: 'test_tc_abc123',
client_secret: 'test_tcs_xyz789'
};
// This will automatically activate test mode
const response = await fetch('/api/v1/worklists/ingest', {
headers: {
'Authorization': `Bearer ${testAccessToken}`
},
body: JSON.stringify(testWorklistItems)
});
Sample Test Data
Use realistic test data:
const testWorklistItems = [
{
accession_number: "TEST-2024-001",
patient_id: "TEST-PAT-001",
patient_name: "Test Patient",
patient_sex: "M",
patient_birth_date: "1980-01-01",
modality: "CR",
exam_datetime: "2024-01-15T10:00:00Z",
exam_description: "Test Chest X-Ray",
referring_physician: "Dr. Test Physician",
hospital_name: "Test Hospital"
}
];
Troubleshooting
Common Issues
Authentication Errors (401)
- Verify your access token is valid and not expired
- Check that you have the required scopes (
worklists:write) - Ensure you're using the correct client credentials
Validation Errors (400)
- Verify all required fields are present
- Check date formats are ISO 8601
- Ensure modality codes are valid
- At least one of
accession_numberorstudy_uidmust be provided
Rate Limiting (429)
- Implement exponential backoff
- Respect the
Retry-Afterheader - Consider reducing request frequency
Webhook Delivery Issues
- Verify your webhook endpoint is accessible
- Check signature verification implementation
- Ensure your endpoint responds with 200 status
- Review webhook logs in the Developer Portal
Debug Mode
Enable detailed logging to troubleshoot issues:
const DEBUG = process.env.NODE_ENV === 'development';
async function debugRequest(url, options) {
if (DEBUG) {
console.log('Request:', { url, options });
}
const response = await fetch(url, options);
const data = await response.json();
if (DEBUG) {
console.log('Response:', {
status: response.status,
headers: Object.fromEntries(response.headers),
data
});
}
return { response, data };
}
Need Help?
- API Documentation: Interactive API Reference
- Code Examples: Additional examples
- Support: Contact developers@transcrimed.com.br
Ready to integrate? Start with our Getting Started Guide or explore the API Reference.