Bot Execute
Invoke a Medplum Bot using the custom $execute
operation.
If you're not familiar with Medplum Bots, you may want to review Bot Basics documentation first.
Invoke a bot by ID
Invoke a bot by ID when you know the Bot's ID in advance.
Finding your Bot Id
You can find the id
of your Bot by clicking on the Details tab of the Bot resource. In this example, it is 43ac3060-ff20-49e8-9682-bf91ab3a5191

Using POST
POST [base]/Bot/[id]/$execute
Examples
- TypeScript
- CLI
- cURL
The MedplumClient TypeScript class provides a executeBot
convenience method
const result = await medplum.executeBot(id, { input: { foo: 'bar' } }, ContentType.JSON);
console.log(result);
medplum login
medplum post 'Bot/[id]/$execute' '{ "foo": "bar" }'
curl 'https://api.medplum.com/fhir/R4/Bot/[id]/$execute' \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $MY_ACCESS_TOKEN" \
-d '{"foo":"bar"}'
Using GET
Query parameters will be passed to the bot as type Record<string, string>
(see Content Types below)
GET [base]/Bot/[id]/$execute?params
- TypeScript
- CLI
- cURL
const getResult = await medplum.get(medplum.fhirUrl('Bot', id, '$execute').toString() + '?foo=bar');
console.log(getResult);
medplum login
medplum get 'Bot/[id]/$execute?foo=bar'
curl 'https://api.medplum.com/fhir/R4/Bot/[id]/$execute?foo=bar' \
-H "Authorization: Bearer $MY_ACCESS_TOKEN"
Invoke a bot by identifier
Sometimes you may not know the Medplum Bot ID in advance. In that case, you can invoke a Bot by Identifier
.
This is also useful when the same conceptual bot exists in multiple Medplum projects. Each bot will have a different ID, but they can all have the same identifier.
Using POST
POST [base]/Bot/$execute?identifier=[system]|[code]
Examples
- TypeScript
- CLI
- cURL
The MedplumClient executeBot
convenience method supports both id: string
and identifier: Identifier
:
const result = await medplum.executeBot(
{
system: 'https://example.com/bots',
value: '1234',
},
{
foo: 'bar',
}
);
console.log(result);
medplum login
medplum post 'Bot/[id]/$execute?identifier=https://example.com/bots|1234' '{ "foo": "bar" }'
curl 'https://api.medplum.com/fhir/R4/Bot/$execute?identifier=https://example.com/bots|1234' \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $MY_ACCESS_TOKEN" \
-d '{"foo":"bar"}'
Using GET
Query parameters will be passed to the bot as type Record<string, string>
(see Content Types below)
GET [base]/Bot/$execute?identifier=[system]|[code]¶ms
- TypeScript
- CLI
- cURL
const getResult = await medplum.get(
medplum.fhirUrl('Bot', '$execute').toString() + '?identifier=https://example.com/bots|1234&foo=bar'
);
console.log(getResult);
medplum login
medplum get 'Bot/$execute?identifier=https://example.com/bots|1234'
curl 'https://api.medplum.com/fhir/R4/Bot/$execute?identifier=https://example.com/bots|1234' \
-H "Authorization: Bearer $MY_ACCESS_TOKEN"
Content Types
Medplum Bots support a variety of input content types. Specify the input content type using the standard Content-Type
HTTP header, or as an optional parameter to MedplumClient.executeBot()
.
Content-Type | typeof event.input | Description |
---|---|---|
text/plain | string | <INPUT_DATA> is parsed as plaintext string |
application/json | Record<string, any> | <INPUT_DATA> is parsed as JSON-encoded object |
application/x-www-form-urlencoded | Record<string, string> | <INPUT_DATA> is parsed as URL-encoded string, resulting in a key/value map |
application/fhir+json | Resource | <INPUT_DATA> is parsed as a FHIR Resource encoded as JSON |
x-application/hl7-v2+er7 | HL7Message | <INPUT_DATA> is a string that should be parsed as a pipe-delimited HL7v2 message. HL7v2 is a common text-based message protocol used in legacy healthcare systems |
The input data that will be parsed according to CONTENT_TYPE
and passed into your Bot as event.input
.
Asynchronous Execution
To run bots asynchronously, you can specify the Prefer: respond-async
header to move execution of the bot to the background. Asynchronous execution will result in an HTTP 202 Accepted
response immediately, and the bot will continue running in the background.
Making an Async Request
Add the Prefer: respond-async
header to any bot execution request:
- TypeScript
- CLI
- cURL
const response = await medplum.executeBot('your-bot-id', { foo: 'bar' }, ContentType.JSON, {
headers: {
Prefer: 'respond-async',
},
});
medplum post 'Bot/[id]/$execute' '{ "foo": "bar" }' --prefer-async
curl 'https://api.medplum.com/fhir/R4/Bot/[id]/$execute' \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $MY_ACCESS_TOKEN" \
-H "Prefer: respond-async" \
-d '{"foo":"bar"}'
Checking Job Status
When using async execution, the server returns a 202 Accepted
response with a Content-Location
header containing the URL to check the job status:
HTTP/1.1 202 Accepted
Content-Location: https://api.medplum.com/fhir/R4/job/[job-id]/status
You can poll this URL to check the status of your bot execution. In general, for long running jobs it's best to poll no more frequently than 1 second.
- TypeScript
- CLI
- cURL
The MedplumClient
provides helper utilities to poll job status automatically
const response = await medplum.executeBot('your-bot-id', { foo: 'bar' }, ContentType.JSON, {
headers: {
Prefer: 'respond-async',
},
pollStatusOnAccepted: true, // Poll the status until completion
pollStatusPeriod: 2000, // Poll every 2 seconds
});
However, you can also manually poll the status URL
// Example: Get the status URL from the Content-Location header
const accessToken = 'your-access-token';
const response = await fetch('https://api.medplum.com/fhir/R4/Bot/bot-id/$execute', {
headers: {
Prefer: 'respond-async',
Authorization: `Bearer ${accessToken}`,
},
});
// Get the status URL from the Content-Location header
const statusUrl = response.headers.get('Content-Location');
if (!statusUrl) {
throw new Error('No Content-Location header found');
}
// Poll the status until completion, using a for loop with max attempts
let result: any = undefined;
for (let attempt = 1; attempt <= 30; attempt++) {
const statusResponse = await fetch(statusUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const asyncJob = await statusResponse.json();
if (asyncJob.status === 'completed') {
// Extract the result from the output parameters
const responseBody = asyncJob.output?.parameter?.find((p: any) => p.name === 'responseBody');
result = responseBody?.valueString || responseBody?.valueBoolean || responseBody?.valueInteger;
} else if (asyncJob.status === 'error') {
// Handle error case
const outcome = asyncJob.output?.parameter?.find((p: any) => p.name === 'outcome');
throw new Error(`Bot execution failed: ${outcome?.resource?.issue?.[0]?.details?.text}`);
}
// Wait 2 second before polling again
await sleep(2000);
}
// If the result is undefined, throw an error
if (result === undefined) {
throw new Error('Max polling attempts reached without completion');
}
# Check job status
medplum get 'job/[job-id]/status'
# Check job status
curl 'https://api.medplum.com/fhir/R4/job/[job-id]/status' \
-H "Authorization: Bearer $MY_ACCESS_TOKEN"
AsyncJob Resource Structure
The status endpoint returns an AsyncJob
resource with the following key fields:
-
status
: Current job statusaccepted
- Job has been queuedactive
- Job is currently runningcompleted
- Job finished successfullyerror
- Job failedcancelled
- Job was cancelled
-
output
: Contains the results when status iscompleted
orerror
- For successful bot execution:
parameter[0].name
will beresponseBody
- For failed execution:
parameter[0].name
will beoutcome
with error details
- For successful bot execution:
Example Response
When the bot execution completes successfully:
{
resourceType: 'AsyncJob',
id: 'job-id',
status: 'completed',
request: 'https://api.medplum.com/fhir/R4/Bot/:bot-id/$execute',
requestTime: '2023-01-01T00:00:00.000Z',
transactionTime: '2023-01-01T00:00:05.000Z',
output: {
resourceType: 'Parameters',
parameter: [
{
name: 'responseBody',
valueString: 'Bot execution result',
},
],
},
};
When the bot execution fails:
{
resourceType: 'AsyncJob',
id: 'job-id',
status: 'error',
request: 'https://api.medplum.com/fhir/R4/Bot/:bot-id/$execute',
requestTime: '2023-01-01T00:00:00.000Z',
transactionTime: '2023-01-01T00:00:05.000Z',
output: {
resourceType: 'Parameters',
parameter: [
{
name: 'outcome',
resource: {
resourceType: 'OperationOutcome',
issue: [
{
severity: 'error',
code: 'processing',
details: {
text: 'Bot execution failed',
},
},
],
},
},
],
},
};