Medplum Elixir

Elixir client for Medplum's FHIR API.

Built for apps that want:

Installation

Add package to mix.exs:

def deps do
[
{:medplum_elixir, "~> 0.2.0"}
]
end

Then fetch deps:

mix deps.get

Quick start

Medplum uses OAuth client credentials. Set env vars first:

export MEDPLUM_BASE_URL="https://api.medplum.com"
export MEDPLUM_CLIENT_ID="..."
export MEDPLUM_CLIENT_SECRET="..."

Create reusable client:

client =
Medplum.new(
base_url: System.fetch_env!("MEDPLUM_BASE_URL"),
client_id: System.fetch_env!("MEDPLUM_CLIENT_ID"),
client_secret: System.fetch_env!("MEDPLUM_CLIENT_SECRET")
)

Read resource:

{:ok, patient} = Medplum.read(client, "Patient", "123")

Search resource:

{:ok, bundle} = Medplum.search(client, "Patient", %{"family" => "Smith"})

Patch resource:

ops = [%{"op" => "replace", "path" => "/active", "value" => true}]
{:ok, patient} = Medplum.patch(client, "Patient", "123", ops)

Stream paged search results:

entries =
client
|> Medplum.stream_search("Patient", %{"family" => "Smith"})
|> Enum.to_list()

Client options

Medplum.new/1 accepts:

Example with request tuning:

client =
Medplum.new(
base_url: System.fetch_env!("MEDPLUM_BASE_URL"),
client_id: System.fetch_env!("MEDPLUM_CLIENT_ID"),
client_secret: System.fetch_env!("MEDPLUM_CLIENT_SECRET"),
default_headers: [{"x-app-name", "my_app"}],
req_options: [receive_timeout: 15_000],
max_retries: 2,
token_refresh_skew: 60
)

Core API

Main module covers generic FHIR and Medplum operations:

Example:

{:ok, bundle} =
Medplum.batch(client, [
%{"request" => %{"method" => "GET", "url" => "Patient/123"}}
])
{:ok, graphql_result} =
Medplum.graphql(client, "query Demo { PatientList { id } }")

Resource helpers

Thin wrappers live under Medplum.Resources.*.

Common modules:

Example:

alias Medplum.Resources.Patient
alias Medplum.Resources.Task
{:ok, patient} = Patient.get(client, "123")
{:ok, matches} =
Patient.search_by_identifier(client, "http://hospital.example/mrn", "123")
{:ok, tasks} =
Task.search_by_owner(client, "Practitioner/dr-1", %{"status" => "requested"})

Many resource modules also expose identifier-based upsert helpers:

{:ok, patient} =
Patient.upsert(client, %{
"identifier" => [%{"system" => "http://hospital.example/mrn", "value" => "123"}],
"name" => [%{"family" => "Smith"}]
})

Workflow helpers

Higher-level modules live under Medplum.Workflows.*.

Available workflows:

Example:

alias Medplum.Workflows.Automation
alias Medplum.Workflows.Billing
alias Medplum.Workflows.CarePlanning
alias Medplum.Workflows.CareCoordination
alias Medplum.Workflows.Charting
alias Medplum.Workflows.Intake
alias Medplum.Workflows.Medications
alias Medplum.Workflows.Orders
alias Medplum.Workflows.Scheduling
alias Medplum.Workflows.Tasks
{:ok, open_tasks} =
Tasks.list_for_owner(client, "Practitioner/dr-1", %{"status" => "requested"})
{:ok, appointments} =
Scheduling.find_appointments(client, %{
"resourceType" => "Parameters",
"parameter" => [%{"name" => "start", "valueDateTime" => "2026-07-04T09:00:00Z"}]
})
{:ok, service_requests} =
Orders.service_requests_for_subject(client, "Patient/123")
{:ok, claim_pdf} =
Billing.export_claim(client, "claim-123")
{:ok, bot_result} =
Automation.execute_bot(client, "bot-123", %{
"resourceType" => "Patient",
"id" => "123"
})
{:ok, active_meds} =
Medications.active_medication_requests_for_subject(client, "Patient/123")
{:ok, care_goals} =
CarePlanning.active_goals_for_subject(client, "Patient/123")
{:ok, family_contacts} =
CareCoordination.active_family_for_patient(client, "Patient/123")
{:ok, observations} =
Charting.observations_for_patient(client, "Patient/123", %{"category" => "vital-signs"})
{:ok, responses} =
Intake.questionnaire_responses_for_subject(client, "Patient/123")

Error handling

All public functions return either success tuple or %Medplum.Error{}:

case Medplum.read(client, "Patient", "missing-id") do
{:ok, patient} ->
patient
{:error, %Medplum.Error{type: :api_error, status: 404}} ->
nil
{:error, %Medplum.Error{} = error} ->
raise error
end

Error types:

Phoenix config

# config/runtime.exs
config :my_app, :medplum,
base_url: System.fetch_env!("MEDPLUM_BASE_URL"),
client_id: System.fetch_env!("MEDPLUM_CLIENT_ID"),
client_secret: System.fetch_env!("MEDPLUM_CLIENT_SECRET")
client =
:my_app
|> Application.fetch_env!(:medplum)
|> Medplum.new()

Docs

Development

Run tests:

mix test

Build docs:

mix docs