Skip to main content

Systems

System plugins allow Automation to exchange data with external platforms such as EMRs, national surveillance systems, DHIS2, and other databases that need structured lab data.

Each system plugin must export a start function that receives a context object. The plugin does not manage queues or routing β€” Automation handles this and delivers the correct resources based on your declared capabilities.

πŸ”° Typing the Context​

Always type your context with your plugin’s automation definition:

import type { Context } from "automation/types/context";
import automation from "../path/to/automation.json";

export async function start(context: Context<typeof automation>) {
// plugin logic
}

βš™οΈ Accessing Configuration​

System plugins can define configFields such as:

configFields: [
{ key: "baseUrl", label: "API URL", type: "string", required: true },
{ key: "token", label: "Access Token", type: "password", required: true }
]

At runtime, values are available at:

context.connection.system.config

Example:

const { baseUrl, token } = context.connection.system.config;

πŸ“₯ Receiving Resources to Sync​

When your plugin declares a receive capability for a resource like DiagnosticReport, Automation will deliver those resources to your code via:

context.on("DiagnosticReport", async report => {
// sync logic
report.status("synced", "external-id-123");
});

Automation handles matching, delivery, tracking, and retries automatically.

πŸ›  Example: EMR or FHIR Server​

import axios from "axios";

export async function start(context) {
const { baseUrl, token } = context.connection.system.config;

context.on("DiagnosticReport", async report => {
try {
const { data } = await axios.post(`${baseUrl}/DiagnosticReport`, report, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/fhir+json"
}
});

report.status("synced", data.id ?? `report-${Date.now()}`);
} catch (err: any) {
if (err.response?.status === 400 || err.response?.status === 403) {
report.status("failed", err.message); // permanent failure
} else {
report.status("error", err.message); // retryable
}
}
});

context.status("connected");
}

πŸ›  Example: DHIS2 (Aggregate Reporting)​

import axios from "axios";

export async function start(context) {
const { baseUrl, username, password } = context.connection.system.config;

const auth = { auth: { username, password } };

context.on("Observation", async observation => {
try {
const payload = {
dataValues: [
{
dataElement: "MAL_POS",
value: 1,
period: "202405",
orgUnit: "XYZ001"
}
]
};

await axios.post(`${baseUrl}/api/dataValueSets`, payload, auth);

observation.status("synced", "XYZ001-MAL_POS-202405");
} catch (err: any) {
if (err.response?.status === 400) {
observation.status("failed", err.message);
} else {
observation.status("error", err.message);
}
}
});

context.status("connected");
}

πŸ›  Example: Custom Surveillance API​

import axios from "axios";

export async function start(context) {
const { baseUrl, token } = context.connection.system.config;

context.on("DiagnosticReport", async report => {
try {
const { data } = await axios.post(`${baseUrl}/api/results`, {
patientId: report.subject?.reference,
test: report.code?.text,
result: report.result,
issued: report.issued
}, {
headers: { Authorization: `Bearer ${token}` }
});

report.status("synced", data.id ?? `surv-${Date.now()}`);
} catch (err: any) {
if (err.response?.status === 422) {
report.status("failed", err.message);
} else {
report.status("error", err.message);
}
}
});

context.status("connected");
}

πŸ” Handling Auth​

Supported authMethod values include:

  • "none"
  • "basic"
  • "token"
  • "custom" (use configFields manually)

Automation doesn’t enforce auth. Your plugin uses the values directly.

πŸ“¦ Understanding .status(...)​

Every received resource includes a .status() method:

βœ”οΈ synced​

Used when delivery is successful. Requires a secondaryId:

resource.status("synced", "external-uuid-123");

❌ failed​

Use this for unrecoverable issues β€” bad config, missing fields, 400 errors. Requires a human-readable error message:

resource.status("failed", "Missing patient ID");

Automation will not retry.

⚠️ error​

Use for temporary issues β€” timeouts, 5xx errors, etc. This tells Automation to retry later.

resource.status("error", "DHIS2 server unavailable");

Automation tracks this and will try again automatically.

βœ… Recap​

You WriteAutomation Handles
context.on("X", ...)Delivers matched resources
resource.status(...)Tracks sync outcome and retries
context.status(...)Monitors connection state
StatusTracked in UIRetries?Required info
syncedβœ…βŒsecondaryId
failedβœ…βŒError message
errorβœ…βœ…Retryable error text