Haidy-Analyst
This guide explains how to submit clinical records (documents, audio, or plain text) for AI-powered structure extraction and receive the results as either a structured HolisticPatient object or a FHIR Bundle.
The API processes clinical data through multiple stages:
- Content acquisition - Downloads URLs or decodes base64
- Preprocessing - Document understanding or audio transcription
- Structure extraction - AI-powered medical entity recognition
- Output formatting - HolisticPatient or FHIR Bundle
Authentication
All requests must include a Bearer token:
Authorization: Bearer <YOUR_TOKEN>
Example (JavaScript):
const headers = { Authorization: `Bearer ${token}` };
Endpoints Overview
| Purpose | Method | Path | Description |
|---|---|---|---|
| Structure records | POST | /v2/structure | Extract structured medical data from clinical records. Returns HolisticPatient. |
| Structure to FHIR | POST | /v2/structure/fhir | Same as above but returns a FHIR R4B Bundle. |
| Structure text | POST | /v2/structure/text | Quick endpoint for structuring plain text strings. |
| Check task status | POST | /v2/tasks | Poll for task status using the task_id. |
The older /v2/structure/documents/fhir/base64 endpoint is deprecated. Use /v2/structure/fhir instead.
Input Types
The structure API accepts three types of clinical record inputs:
1. Plain Text
{
"type": "plain_text",
"text": "Patient presents with headache and fever for 3 days."
}
2. Documents (PDF, Images)
Via URL:
{
"type": "document",
"url": "https://example.com/medical-report.pdf"
}
Via Base64:
{
"type": "document",
"base64": "JVBERi0xLjQKJeLjz9MK..."
}
3. Audio
Via URL:
{
"type": "audio",
"url": "https://example.com/consultation.m4a"
}
Via Base64:
{
"type": "audio",
"base64": "SUQzBAAAAAAAI1RTU0UAAAA..."
}
With existing transcript:
{
"type": "audio",
"transcript": {
"bubbles": [
{ "text": "Patient has hypertension", "start": 0.0, "end": 2.5 }
]
}
}
Method 1: Structure Records (Recommended)
Use this endpoint to extract structured medical data from any type of clinical record.
Endpoint
Method: POST
URL: https://api.44ai.ch/v2/structure
Headers: Authorization: Bearer <YOUR_TOKEN>
Request Body
{
"new_records": [
{
"record_id": "123e4567-e89b-12d3-a456-426614174000",
"source": {
"type": "plain_text",
"text": "Patient presents with hypertension. Current medications: Lisinopril 10mg daily."
}
},
{
"record_id": "223e4567-e89b-12d3-a456-426614174001",
"source": {
"type": "document",
"url": "https://example.com/lab-results.pdf"
}
}
],
"holistic_patient": null,
"config": {
"structure_engine_version": "1.0.0",
"include_record_structured": true,
"output_language": "en"
}
}
Request Parameters
Required:
new_records(array): List of clinical records to processsource(object): Record content (see Input Types)record_id(string, optional): UUID for tracking this record
Optional:
holistic_patient(object): Existing patient data for incremental updatesconfig(object): Processing configurationstructure_engine_version(string): Engine version to useoutput_language(string): Output language (en,de,fr,it)include_record_metadata(boolean): Extract document metadatainclude_record_content(boolean): Include processed contentinclude_record_structured(boolean): Include structured entitiesinclude_patient_metadata(boolean): Extract patient demographicsinclude_patient_structured(boolean): Include patient-level aggregations
Example Request (Documents)
import axios from "axios";
async function structureDocuments(token, documentUrls) {
const url = "https://api.44ai.ch/v2/structure";
const body = {
new_records: documentUrls.map((docUrl) => ({
source: {
type: "document",
url: docUrl,
},
})),
config: {
structure_engine_version: "1.0.0",
include_record_structured: true,
output_language: "en",
},
};
const response = await axios.post(url, body, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});
return response.data;
}
// Usage
const result = await structureDocuments("your-token", [
"https://example.com/report1.pdf",
"https://example.com/report2.pdf",
]);
console.log("Task ID:", result.task_id);
Example Request (Base64 Documents)
import fs from "fs";
import axios from "axios";
function fileToBase64(filePath) {
return fs.readFileSync(filePath).toString("base64");
}
async function structureBase64Documents(token, filePaths) {
const url = "https://api.44ai.ch/v2/structure";
const body = {
new_records: filePaths.map((filePath) => ({
source: {
type: "document",
base64: fileToBase64(filePath),
},
})),
config: {
structure_engine_version: "1.0.0",
include_record_structured: true,
},
};
const response = await axios.post(url, body, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});
return response.data;
}
// Usage
const result = await structureBase64Documents("your-token", [
"./medical-report.pdf",
"./lab-results.pdf",
]);
console.log("Task ID:", result.task_id);
Example Request (Plain Text)
async function structureText(token, textRecords) {
const url = "https://api.44ai.ch/v2/structure";
const body = {
new_records: textRecords.map((text) => ({
source: {
type: "plain_text",
text: text,
},
})),
config: {
structure_engine_version: "1.0.0",
output_language: "en",
},
};
const response = await axios.post(url, body, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});
return response.data;
}
// Usage
const result = await structureText("your-token", [
"Patient presents with fever and cough for 3 days.",
"Vital signs: BP 120/80, HR 72, Temp 38.5°C",
]);
Example Response (Task Created)
{
"task_id": "abc123def456",
"status": "QUEUED",
"percentage": 0,
"started_at": "2024-12-22T10:30:00Z",
"completed_at": null,
"status_changes_at": {
"CREATED": "2024-12-22T10:30:00Z",
"QUEUED": "2024-12-22T10:30:01Z"
},
"result": null,
"additional_info": null
}
Response Fields:
task_id(string): Use this ID to poll for completionstatus(string): Current status (see Task Statuses)percentage(number): Progress from 0-100started_at(string): ISO 8601 timestamp when task startedcompleted_at(string|null): Completion timestamp (null while processing)result(object|null): Structured data (available when status is DONE)
Method 2: Structure to FHIR Bundle
Use this endpoint to get results as a FHIR R4B Bundle instead of HolisticPatient format.
Endpoint
Method: POST
URL: https://api.44ai.ch/v2/structure/fhir
Headers: Authorization: Bearer <YOUR_TOKEN>
Query Parameters
include_patient(boolean, default: true): Include Patient resource in the Bundleinclude_document_references(boolean, default: true): Include DocumentReference resourcesinclude_data(boolean, default: true): Include URLs/data in DocumentReferences
Request Body
Same format as /v2/structure endpoint.
Example Request
async function structureToFhir(token, documents, options = {}) {
const params = new URLSearchParams({
include_patient: options.includePatient ?? true,
include_document_references: options.includeDocRefs ?? true,
include_data: options.includeData ?? true,
});
const url = `https://api.44ai.ch/v2/structure/fhir?${params}`;
const body = {
new_records: documents.map((doc) => ({
source: {
type: "document",
url: doc.url,
},
})),
config: {
structure_engine_version: "1.0.0",
record_metadata_profiles: ["EPDMetaData_v1"],
include_record_metadata: true,
},
};
const response = await axios.post(url, body, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});
return response.data;
}
// Usage
const result = await structureToFhir(
"your-token",
[{ url: "https://example.com/lab-results.pdf" }],
{
includePatient: true,
includeDocRefs: true,
includeData: true,
}
);
Method 3: Quick Text Structuring
For simple plain text structuring, use the dedicated text endpoint.
Endpoint
Method: POST
URL: https://api.44ai.ch/v2/structure/text
Headers: Authorization: Bearer <YOUR_TOKEN>
Request Body
{
"inputs": [
"Patient presents with fever and cough for 3 days.",
"Vital signs: BP 120/80, HR 72, Temp 38.5°C",
"Diagnosis: Upper respiratory tract infection"
],
"config": {
"output_language": "en"
}
}
Example Request
async function structureTextQuick(token, textInputs) {
const url = "https://api.44ai.ch/v2/structure/text";
const response = await axios.post(
url,
{
inputs: textInputs,
config: {
output_language: "en",
},
},
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
}
);
return response.data;
}
// Usage
const result = await structureTextQuick("your-token", [
"Patient has type 2 diabetes",
"Current medications: Metformin 1000mg twice daily",
]);
Polling for Task Completion
After creating a task, poll the /v2/tasks endpoint until the task is complete.
Endpoint
Method: POST
URL: https://api.44ai.ch/v2/tasks
Request Body
{
"task_id": "abc123def456"
}
Task Statuses
Tasks progress through these statuses:
- CREATED: Task has been initialized
- QUEUED: Task is waiting in the processing queue
- STARTED: Processing has begun
- DOCUMENT_DOWNLOAD_DONE: URLs have been downloaded
- DOCUMENT_PARSING_DONE: Documents have been parsed
- AUDIO_PROCESSING_DONE: Audio has been transcribed
- SERVICE_DONE: Structure extraction complete
- DONE: Task is fully complete, results available
- FAILED: Task failed due to an error
Polling Example
async function checkTaskStatus(taskId, token) {
const url = "https://api.44ai.ch/v2/tasks";
const response = await axios.post(
url,
{ task_id: taskId },
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
}
);
return response.data;
}
async function waitForCompletion(taskId, token, maxAttempts = 120) {
let delay = 1000; // Start with 1 second
const maxDelay = 8000; // Max 8 seconds between polls
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const task = await checkTaskStatus(taskId, token);
console.log(`Status: ${task.status} (${task.percentage}%)`);
if (task.status === "DONE") {
console.log("Processing complete!");
return task.result;
}
if (task.status === "FAILED") {
throw new Error(`Task failed: ${JSON.stringify(task.additional_info)}`);
}
// Exponential backoff
await new Promise((resolve) => setTimeout(resolve, delay));
delay = Math.min(delay * 1.5, maxDelay);
}
throw new Error("Task timed out");
}
// Usage
try {
const result = await waitForCompletion("abc123def456", "your-token");
console.log("Structured data:", result);
} catch (error) {
console.error("Error:", error.message);
}
Example Complete Response (HolisticPatient)
{
"task_id": "abc123def456",
"status": "DONE",
"percentage": 100,
"result": {
"patient_id": "550e8400-e29b-41d4-a716-446655440000",
"holistic_patient_id": "650e8400-e29b-41d4-a716-446655440000",
"clinical_records": [
{
"record_id": "123e4567-e89b-12d3-a456-426614174000",
"content_hash": "sha256:abc...",
"record_structured": {
"input_text": "Patient presents with hypertension...",
"structured_output": {
"medications": [
{
"identity_signature": "med_001",
"identity_fields": {
"name": "Lisinopril",
"dose": "10mg",
"frequency": "daily"
},
"instantiations": [...]
}
],
"conditions": [...],
"procedures": [...],
"allergies": [...],
"immunizations": [...],
"observations": [...]
}
}
}
],
"patient_metadata": {
"name": "John Doe",
"gender": "male",
"birthday": "1990-01-01T00:00:00Z"
},
"created_at": "2024-12-22T10:30:00Z",
"last_modified": "2024-12-22T10:32:15Z"
},
"started_at": "2024-12-22T10:30:00Z",
"completed_at": "2024-12-22T10:32:15Z",
"status_changes_at": {
"CREATED": "2024-12-22T10:30:00Z",
"STARTED": "2024-12-22T10:30:01Z",
"DONE": "2024-12-22T10:32:15Z"
}
}
Example Complete Response (FHIR Bundle)
{
"task_id": "xyz789abc123",
"status": "DONE",
"percentage": 100,
"result": {
"resourceType": "Bundle",
"type": "collection",
"entry": [
{
"fullUrl": "Patient/123e4567-e89b-12d3-a456-426614174000",
"resource": {
"resourceType": "Patient",
"id": "123e4567-e89b-12d3-a456-426614174000",
"active": true,
"name": [
{
"text": "John Doe",
"given": ["John"],
"family": "Doe"
}
],
"gender": "male",
"birthDate": "1990-01-01"
}
},
{
"fullUrl": "DocumentReference/223e4567-e89b-12d3-a456-426614174001",
"resource": {
"resourceType": "DocumentReference",
"id": "223e4567-e89b-12d3-a456-426614174001",
"status": "current",
"subject": {
"reference": "Patient/123e4567-e89b-12d3-a456-426614174000"
},
"content": [
{
"attachment": {
"contentType": "application/pdf",
"url": "https://example.com/lab-results.pdf",
"title": "Laboratory Results"
}
}
]
}
}
]
},
"started_at": "2024-12-22T10:30:00Z",
"completed_at": "2024-12-22T10:32:15Z"
}
Once a task reaches DONE or FAILED status and you retrieve the result, it will no longer be available for subsequent fetches. Make sure to save the results.
Structured Output Format
HolisticPatient Structure
The /v2/structure endpoint returns a HolisticPatient object containing:
Clinical Records
Each record includes:
- record_id: Unique identifier
- content: Original content (text, transcript, or binary)
- content_hash: SHA-256 hash for deduplication
- record_structured: Extracted medical entities
- medications
- conditions
- procedures
- allergies
- immunizations
- observations
Patient Metadata (if extractable)
- name: Patient full name
- gender: male | female | other | unknown
- birthday: ISO 8601 date
Patient Structured Output
Aggregated medical entities across all records.
Medical Entities
Each entity type contains deduplicated unique entities with:
- identity_signature: Hash of identity fields
- identity_fields: Key attributes defining uniqueness
- instantiations: All original mentions with context
Example medication entity:
{
"identity_signature": "med_lisinopril_10mg_daily",
"identity_fields": {
"name": "Lisinopril",
"dose": "10mg",
"frequency": "daily"
},
"instantiations": [
{
"display_str": "Lisinopril 10mg daily",
"span": { "start": 45, "end": 65 },
"tagging_certainty": 0.95,
"time_references": [...],
"status": {
Complete Examples
### Example 1: Structure Documents to HolisticPatient
```js
import fs from "fs";
import axios from "axios";
const TOKEN = process.env.API_TOKEN;
async function structureDocumentsExample() {
// 1) Create structuring task
const createResponse = await axios.post(
"https://api.44ai.ch/v2/structure",
{
new_records: [
{
source: {
type: "document",
url: "https://example.com/medical-report.pdf"
}
},
{
source: {
type: "plain_text",
text: "Patient has type 2 diabetes. Current medications: Metformin 1000mg twice daily."
}
}
],
config: {
structure_engine_version: "1.0.0",
include_record_structured: true,
output_language: "en"
}
},
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${TOKEN}`
}
}
);
const { task_id } = createResponse.data;
console.log("Task created:", task_id);
// 2) Poll for completion
let delay = 1000;
const maxDelay = 8000;
while (true) {
const statusResponse = await axios.post(
"https://api.44ai.ch/v2/tasks",
{ task_id },
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${TOKEN}`
}
}
);
const { status, percentage, result } = statusResponse.data;
console.log(`Status: ${status} (${percentage}%)`);
if (status === "DONE") {
console.log("Structure extraction complete!");
// Access structured medical entities
const medications = result.clinical_records[0]?.record_structured?.structured_output?.medications || [];
const conditions = result.clinical_records[0]?.record_structured?.structured_output?.conditions || [];
console.log(`Found ${medications.length} unique medications`);
console.log(`Found ${conditions.length} unique conditions`);
return result;
}
if (status === "FAILED") {
throw new Error(`Task failed: ${JSON.stringify(statusResponse.data.additional_info)}`);
}
await new Promise(resolve => setTimeout(resolve, delay));
delay = Math.min(delay * 1.5, maxDelay);
}
}
structureDocumentsExample()
.then(result => {
console.log("Success!");
// Process your structured data here
})
.catch(error => {
console.error("Error:", error.message);
process.exit(1);
});
Example 2: Structure to FHIR Bundle
import axios from "axios";
const TOKEN = process.env.API_TOKEN;
async function structureToFhirExample() {
// Create task with FHIR output
const params = new URLSearchParams({
include_patient: true,
include_document_references: true,
include_data: true
});
const createResponse = await axios.post(
`https://api.44ai.ch/v2/structure/fhir?${params}`,
{
new_records: [
{
record_id: "123e4567-e89b-12d3-a456-426614174000",
source: {
type: "plain_text",
text: "Patient John Doe, DOB 1990-01-01, diagnosed with type 2 diabetes."
}
}
],
config: {
structure_engine_version: "1.0.0",
record_metadata_profiles: ["EPDMetaData_v1"],
include_record_metadata: true
}
},
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${TOKEN}`
}
}
);
const { task_id } = createResponse.data;
console.log("FHIR task created:", task_id);
// Poll for FHIR Bundle
lError Handling
### HTTP Status Codes
- **200 OK**: Task created successfully
- **401 Unauthorized**: Missing or invalid API token
- **422 Validation Error**: Invalid request body or parameters
- **500 Internal Server Error**: Server error during processing
### Example Error Responses
**Invalid Request:**
```json
{
"detail": [
{
"loc": ["body", "new_records"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
Task Failed:
{
"task_id": "abc123",
"status": "FAILED",
"percentage": 0,
"result": null,
"additional_info": {
"error": "Unable to download document from URL",
"details": "Connection timeout after 30s"
}
}
Common Issues
Document Download Failures:
- Ensure URLs are publicly accessible
- Check that URLs don't require authentication
- Verify the document format is supported (PDF, PNG, JPEG)
Base64 Encoding Errors:
- Don't include data URI prefixes (
data:application/pdf;base64,) - Ensure proper encoding without line breaks
- Check file size limits
Transcription Issues:
- For audio, ensure format is supported (MP3, WAV, WebM, MP4, M4A)
- Check audio quality and clarity
- Verify sample rate is appropriate (16kHz+ recommended)
Configuration Options
Structure Engine Versions
Use semantic versioning to ensure consistent processing:
{
"config": {
"structure_engine_version": "1.0.0"
}
}
Version format: MAJOR.MINOR.PATCH
- MAJOR: Breaking changes
- MINOR: New features, backward compatible
- PATCH: Bug fixes
Output Language
Specify the language for structured output:
{
"config": {
"output_language": "en"
}
}
Supported languages: en, de, fr, it
Metadata Extraction
Enable specific metadata extraction:
{
"config": {
"record_metadata_profiles": ["EPDMetaData_v1"],
"include_record_metadata": true,
"include_patient_metadata": true
}
}
Output Control
Control what's included in the response:
{
"config": {
"include_record_content": true, // Include preprocessed content
"include_record_structured": true, // Include extracted entities
"include_patient_metadata": true, // Extract patient demographics
"include_patient_structured": true // Patient-level aggregations
}
}
FAQ
Q: What file formats are supported?
Documents: PDF, PNG, JPEG. Audio: MP3, WAV, WebM, MP4, M4A. Text: Plain UTF-8 text.
Q: Can I mix different input types in one request?
Yes! You can submit documents, audio, and plain text in the same request. Each will be processed appropriately.
Q: How long does processing take?
Processing time varies based on content type and length:
- Plain text: < 10 seconds
- Documents: 30 seconds to 2 minutes
- Audio: 1-3 minutes for transcription + structuring
Q: What's the difference between HolisticPatient and FHIR output?
HolisticPatient is our internal structured format with rich medical entity details. FHIR Bundle is a standard healthcare interoperability format (R4B) suitable for EHR integration.
Q: Can I incrementally update a patient record?
Yes! Pass the existing holistic_patient object in the request along with new_records to add new information to an existing patient.
Q: Are results cached?
No. Once you retrieve a completed task via /v2/tasks, it's removed from the system. Save results immediately.
Q: What medical entities are extracted?
The system extracts 6 main entity types:
- Medications: Drugs, dosages, frequencies
- Conditions: Diagnoses, symptoms, medical conditions
- Procedures: Medical procedures, surgeries, interventions
- Allergies: Allergies and intolerances
- Immunizations: Vaccinations and immunizations
- Observations: Vital signs, lab results, physical findings
Q: Is PHI/PII data secure?
Yes. All API communications use HTTPS. Data is processed according to healthcare privacy standards. Consult our security documentation for details.
Q: What's the rate limit?
Contact your account manager for rate limit information specific to your plan
### Example 3: Base64 Documents
```js
import fs from "fs";
import axios from "axios";
const TOKEN = process.env.API_TOKEN;
function fileToBase64(filePath) {
return fs.readFileSync(filePath).toString("base64");
}
async function structureBase64Example() {
const createResponse = await axios.post(
"https://api.44ai.ch/v2/structure",
{
new_records: [
{
source: {
type: "document",
base64: fileToBase64("./medical-report.pdf")
}
},
{
source: {
type: "document",
base64: fileToBase64("./lab-results.pdf")
}
}
],
config: {
structure_engine_version: "1.0.0",
include_record_structured: true
}
},
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${TOKEN}`
}
}
);
const { task_id } = createResponse.data;
// Poll for results (same as previous examples)
// ...
}
Example 4: Mixed Input Types
async function mixedInputExample() {
const createResponse = await axios.post(
"https://api.44ai.ch/v2/structure",
{
new_records: [
{
source: {
type: "document",
url: "https://example.com/lab-report.pdf"
}
},
{
source: {
type: "audio",
transcript: {
bubbles: [
{
text: "Patient presents with headache and nausea",
start: 0.0,
end: 3.5
}
]
}
}
},
{
source: {
type: "plain_text",
text: "Vital signs: BP 140/90, HR 85, Temp 37.2°C"
}
}
],
config: {
structure_engine_version: "1.0.0",
output_language: "en"
}
},
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${TOKEN}`
}
}
);
// Process task...
} if (status === "FAILED") {
throw new Error("Task failed");
}
// exponential backoff
await new Promise((r) => setTimeout(r, delay));
attempts += 1;
delay = Math.min(delay * 2, maxDelay);
}
}
mainWithUrls().catch((e) => {
console.error(e.response?.data ?? e.message);
process.exit(1);
});
Response Reference
Task Object
| Field | Type | Description |
|---|---|---|
status | string | CREATED | RUNNING | DONE | FAILED |
percentage | number | Progress from 0–100. |
result | object | null | When DONE, contains a FHIR Bundle (e.g., type: "transaction" with entry[]). |
task_id | string | Use to poll via /v2/tasks. |
started_at | string (ISO 8601) | Time when processing started. |
completed_at | string (ISO 8601) | null | Time when processing ended (if DONE/FAILED). |
status_changes_at | object | Timestamps keyed by status. |
additional_info | any | null | Optional diagnostic / metadata. |
Errors & Troubleshooting
Base64 Upload Errors
- 401 Unauthorized: Missing/invalid token. Ensure
Authorization: Bearer <YOUR_TOKEN>is present. - 400 Bad Request: Malformed JSON, missing
files, or invalid Base64. - 422 Unprocessable Entity: File type not supported or content unreadable.
- 5xx Server Error: Temporary issue; try again later and contact support if persistent.
URL Upload Errors
- 401 Unauthorized: Missing/invalid token. Ensure
Authorization: Bearer <YOUR_TOKEN>is present. - 400 Bad Request: Malformed JSON, missing
file_urls, or invalid URL format. - 422 Unprocessable Entity: File type not supported or content unreadable from URL.
- 5xx Server Error: Temporary issue; try again later and contact support if persistent.
FAQ
Q: Can I submit multiple files in one request?
Yes. For Base64: provide multiple { file_name, file_base64 } objects under files. For URLs: provide multiple URLs in the file_urls array.
Q: What's the difference between Base64 and URL upload?
Base64 upload sends file content directly in the request, while URL upload provides links to documents. Use Base64 for files you have locally, URLs for documents already hosted online.
Q: Can I update an existing FHIR bundle?
Yes, when using the URL endpoint, pass the existing bundle as a JSON string in the current_fhir_bundle field to extend it with new documents.
Q: What do I get back on success?
A FHIR Bundle in result once the task is DONE.