How to govern Medplum FHIR agent writes

AI scribes and clinical agents update patient records via FHIR APIs. Here is how Deedglass enforces pre-write policies, requires approval, and verifies committed Medplum rows.

The short answer

Place Deedglass between your AI agents or Medplum Bots and your Medplum project. Deedglass serves as a governed FHIR proxy and Model Context Protocol gateway.

Before an action runs, Deedglass checks tool arguments against your security policy. Safe reads proceed immediately, sensitive patient mutations pause for human authorization in the Console or Slack3, and every executed write is verified against the version Medplum committed in PostgreSQL rather than trusting HTTP response codes3.

A FHIR server can accept a write and quietly drop a field. Deedglass reads the resource back and names what differs. Made-up resource.

Why FHIR APIs need post-write checks

Autonomous clinical agents, ambient scribes, and intake bots interact with electronic health record backends by sending HTTP POST, PUT, and PATCH requests to FHIR endpoints. Standard gateway architectures assume that receiving a 201 Created or 200 OK status means the record stored matches what the agent transmitted.

In practice, healthcare APIs frequently transform or truncate payloads during ingestion:

  1. AccessPolicy field redaction: In Medplum, an AccessPolicy configured with readonlyFields permits the write to succeed but discards restricted fields without raising an error code. An AI scribe sending clinical notes will receive a 201 Created while the actual patient note was silently dropped.
  2. Version drift: Medplum generates a server-controlled versionId and meta.author for each resource version1. Agents cannot predict or verify the version identifier before the database commits.
  3. Partial batch application: If an agent dispatches a bundle without atomic transaction support, certain entries succeed while others fail, leaving clinical charts in a partial state.
  4. Lack of verifiable audit trails: Traditional API gateways log request URLs and response codes, but provide no cryptographic linkage between an agent’s reasoning session and the row committed in PostgreSQL.

Deedglass solves this by intercepting requests before execution and reading back the exact stored version after execution.

Connecting Medplum through the FHIR connector

To connect an agent to Medplum through Deedglass, create a ClientApplication in your Medplum project with an AccessPolicy scoped to the resources the agent requires.

Configure Deedglass’s FHIR connector in your connector settings (connectors.yaml):

Code A settings file: the rules a tool reads before it acts.

version: "connector/v1"
connectors:
  - id: "medplum-ehr"
    type: "fhir"
    base_url: "https://api.medplum.com/fhir/R4"
    auth:
      kind: "bearer"
      token: "${MEDPLUM_ACCESS_TOKEN}"
    allowed_resource_types:
      - "Patient"
      - "Observation"
      - "Condition"
      - "Encounter"
    interactions:
      - "read"
      - "search"
      - "create"
      - "update"
    witness:
      enabled: true
      server: "medplum"
      author: "ClientApplication/c8a4128f-9e20-4d51-b01a-7b3c20261030"

When running Medplum on your own PostgreSQL infrastructure, enabling witness allows Deedglass to read the underlying write-ahead log and verify row changes directly3. On managed Medplum cloud instances, Deedglass operates at the Checked level via HTTP read-back.

Configuring approval policies for clinical resources

Policy rules dictate which FHIR interactions proceed automatically and which require clinical human-in-the-loop oversight.

Here is an example policy configuration (policy.yaml):

Code A settings file: the rules a tool reads before it acts.

version: "policy/v1"
name: "clinical-agent-policy"

defaults:
  decision: "deny"
  reason: "No rule permits this clinical write."

rules:
  # Analytical reads proceed immediately
  - id: "allow-patient-reads"
    match:
      connector: "medplum-ehr"
      interaction: "read"
    action: "allow"

  # Routine vitals observation creates proceed with automated verification
  - id: "allow-vitals-observation"
    match:
      connector: "medplum-ehr"
      interaction: "create"
      resource_type: "Observation"
    action: "allow"

  # Diagnostic condition mutations require physician approval
  - id: "require-approval-for-diagnoses"
    match:
      connector: "medplum-ehr"
      interaction: "create"
      resource_type: "Condition"
    action: "require_approval"
    approval:
      channel: "slack-clinical-review"
      timeout: "45m"
      required: 1

  # Deletions are strictly prohibited for autonomous agents
  - id: "block-deletions"
    match:
      connector: "medplum-ehr"
      interaction: "delete"
    action: "deny"
    reason: "Clinical records cannot be deleted by autonomous agents."

When an agent issues a POST /Condition, Deedglass halts the call, generates a structured approval card with the clinical context and proposed coding, and sends it to the configured review channel3. The reviewing clinician can inspect the diagnostic code, status flag, and encounter reference before deciding whether to permit the write.

If the reviewer denies the request or the approval window expires, Deedglass halts the transaction and returns a structured rejection payload to the agent runtime. The autonomous agent can then acknowledge the decision or seek guidance without corrupting patient records.

How read-back verification catches dropped fields

Once a write executes, Deedglass performs an immediate read-back check (the Checked level)3:

  1. Deedglass reads the ETag header from Medplum’s HTTP response to retrieve the assigned version identifier.
  2. It fetches the resource at that exact version URL (/fhir/R4/<Type>/<id>/_history/<versionId>).
  3. It performs a field-by-field diff between what the agent sent and what Medplum stored.
  4. Server-assigned metadata fields (meta.versionId, meta.lastUpdated, and system tags) are normalized.
  5. If any field the agent set is missing or altered, the action is marked as Doesn’t match3.

In automated testing against Medplum, an agent attempted to write a patient observation with a private clinical note while under an AccessPolicy that marked notes as read-only. Medplum returned HTTP 201 Created, but Deedglass flagged a Doesn’t match result because the stored version held note: null3. The agent’s logs claimed success, but Deedglass surfaced the discrepancy immediately.

Change-log verification with PostgreSQL history tables

For self-hosted Medplum instances, Deedglass verifies the database commit directly by inspecting PostgreSQL’s logical replication stream3.

Medplum stores each historical resource version as full JSON text inside dedicated per-type history tables in PostgreSQL, such as "Observation_History" and "Condition_History"1.

When an action commits:

  • The replication reader inspects the write-ahead log for the transaction.
  • It extracts the JSON payload from the content column of the history table1.
  • It compares the database delta against the agent’s intent.
  • It verifies that the row author matches the expected ClientApplication identity1.

If a background database trigger or manual intervention altered additional patient tables during the transaction, Deedglass detects the divergence and flags the action record.

Offline verification with Ed25519 audit chains

Every governed clinical action produces a signed action record. Each record is sealed using Ed25519 cryptographic signatures over RFC 8785 canonical JSON, and hash-linked to prior records to form a hash-linked audit trail3.

Hospital security teams and clinical safety officers can inspect these records offline without requiring network access or database credentials:

Code Commands typed in a terminal. The text around it says what happens.

deedglass verify ./medplum-audit.json --public-keys ./hospital-keys.json

The verification command checks cryptographic signatures, sequence counters, and row deltas offline3. This means clinical chart changes can be audited years later, regardless of vendor infrastructure changes.

For related patterns in healthcare systems and agent safety, see AI scribes and EHR write approvals, AI agents writing to patient records, and FHIR terminology in our glossary; the human-in-the-loop approval guide details multi-reviewer workflows. To see what the agents configured on your machine can change, paste the config into the check page or run npx deedglass scan (how the grade works); to try approval rules in your browser, open the playground.

Written with AI assistance and checked against our claims register.

The takeaway

Deedglass gates FHIR writes before they execute; it reads back reported resource versions and verifies committed Medplum PostgreSQL history rows.

Read nextAI agents are writing to patient records. Who checks what they changed? Try it: install in 7 s

Related on this site

More posts

Sources

  1. 1Medplum server FHIR repository implementation · github.com · checked 30 October 2026
  2. 2Medplum documentation: Batch and transaction requests · docs.medplum.com · checked 30 October 2026
  3. 3Deedglass documentation: Medplum integration guide · docs.deedglass.com · checked 30 October 2026

Published 30 October 2026, sources last checked 30 October 2026. If something here is out of date, write to hello@deedglass.com.

Try it on your own setup

npx deedglass scan

Grades only your own setup. Nothing is uploaded.

Pricing