# On this page

The `$execute` operation runs a Medplum Bot on-demand, allowing you to trigger server-side automation logic via API calls. Bots can process incoming data, integrate with external systems, and return custom responses-making this operation the primary way to leverage Medplum's automation capabilities.

Whether you're processing HL7v2 messages from legacy systems, responding to webhooks, or building custom API endpoints, `$execute` provides the flexibility to handle virtually any healthcare integration scenario.

## Use Cases

- **Webhook Handling**: Receive and process incoming webhooks from external services (labs, pharmacies, payers)
- **HL7v2 Integration**: Parse and transform HL7v2 messages from legacy healthcare systems
- **Custom API Endpoints**: Build application-specific endpoints that return custom JSON responses
- **On-Demand Processing**: Trigger data transformations, validations, or calculations programmatically
- **External System Integration**: Connect to third-party APIs and synchronize data with your FHIR resources

### 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

```text
POST [base]/Bot/[id]/$execute
```

#### Examples

- TypeScript
- CLI
- cURL

The [MedplumClient](/content/docs/sdk/core.medplumclient) TypeScript class provides a `executeBot` convenience method

```ts
const result = await medplum.executeBot(id, { input: { foo: 'bar' } }, ContentType.JSON);

console.log(result);
```

```bash
medplum login

medplum post 'Bot/[id]/$execute' '{ "foo": "bar" }'
```

```bash
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](/content/docs/api/fhir/operations/bot-execute#content-types/index.html) below)

```text
GET [base]/Bot/[id]/$execute?params
```

- TypeScript
- CLI
- cURL

```ts
const getResult = await medplum.get(medplum.fhirUrl('Bot', id, '$execute').toString() + '?foo=bar');

console.log(getResult);
```

```bash
medplum login

medplum get 'Bot/[id]/$execute?foo=bar'
```

```bash
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

```text
POST [base]/Bot/$execute?identifier=[system]|[code]
```

#### Examples

- TypeScript
- CLI
- cURL

The [MedplumClient](/content/docs/sdk/core.medplumclient)`executeBot` convenience method supports both `id: string` and `identifier: Identifier`:

```ts
const result = await medplum.executeBot(

{

system: 'https://example.com/bots',

value: '1234',

},

{

foo: 'bar',

}
);

console.log(result);
```

```bash
medplum login

medplum post 'Bot/[id]/$execute?identifier=https://example.com/bots|1234' '{ "foo": "bar" }'
```

```bash
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

```text
GET [base]/Bot/$execute?identifier=[system]|[code]&params
```

- TypeScript
- CLI
- cURL

```ts
const getResult = await medplum.get(

medplum.fhirUrl('Bot', '$execute').toString() + '?identifier=https://example.com/bots|1234&foo=bar'
);

console.log(getResult);
```

```bash
medplum login

medplum get 'Bot/$execute?identifier=https://example.com/bots|1234'
```

```bash
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`](/content/docs/api/fhir/resources/index.html) | `<INPUT_DATA>` is parsed as a [FHIR Resource](/content/docs/fhir-basics#storing-data-resources/index.html) encoded as JSON |
| `x-application/hl7-v2+er7` | [`HL7Message`](/content/docs/sdk/core.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

```ts
const response = await medplum.executeBot('your-bot-id', { foo: 'bar' }, ContentType.JSON, {

headers: {

Prefer: 'respond-async',

},
});
```

```bash
medplum post 'Bot/[id]/$execute' '{ "foo": "bar" }' --prefer-async
```

```bash
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
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.

```bash
medplum get 'job/[job-id]/status'
```

```bash
curl 'https://api.medplum.com/fhir/R4/job/[job-id]/status' \
  -H "Authorization: Bearer $MY_ACCESS_TOKEN"
```

### Example Response

When the bot execution completes successfully:

```ts
{

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:

```ts
{

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',

},

},

],

},

},

],

},

};
```
