Layman manual
For a clinician operator who is about to perform an indispensable-care
block lift under PDL Ch 4 § 5. Cross-linked from the admin lift UI;
keep both pointed at the latest commit so the wording stays in sync.
This page is opinionated about when you may lift; it does not
replace clinical judgement on whether a given case meets the
indispensable-care bar. If in doubt, treat as out-of-scope and seek
your medical director's sign-off before lifting.
The right to override a patient's spärr lives in PDL Ch 4 § 5
(patientdatalagen, 2008:355). The lift is lawful only when the
patient's data is indispensable (Swedish: oundgänglig) to the
care the patient needs now. "Useful", "convenient", or "likely to be
informative" do not meet the bar.
| Situation | Indispensable? |
|---|---|
| Unconscious trauma patient; no medication history available locally | Yes — drug interactions / allergies are life-safety |
| Active acute psychiatric episode; clinician needs prior crisis plan | Yes — the prior plan materially changes immediate management |
| Outpatient visit; clinician is curious about earlier diagnoses | No — convenience, not indispensable |
| Patient declined to share specifically; you disagree with the choice | No — the patient has the right to block, full stop |
| Research / quality-improvement work | No — never indispensable; out of scope of § 5 |
The test is not "would this help?" — it is "without this, can I
deliver the care the patient needs?". If the answer is "I could but
it would be worse," don't lift.
lift_expires_at (default 24 hours, see § 4).concept_guids — that's the mechanical filter (legal confirmedindispensable_care_notification). The patient willFor a patient under 18, a vårdnadshavare (parent/guardian) cannot
lift the child's block from the patient portal — that path is gated
to the patient's own session. A vårdnadshavare who has lawful access
and meets the indispensable-care bar must lift via the same admin
route a clinician uses (this runbook). The lift is recorded with the
clinician's user_guid as the actor, never with the
vårdnadshavare's — the audit row is the source of truth for the
chain of custody.
The admin UI fronts POST /api/v1/admin/blocks/<block_guid>/lift.
is_superuser=True or aIPS_INDISPENSABLE_LIFT_ROLES (defaultphysician,admin — operators on the macmini can broaden this viaFind the block. From the patient lookup, open the patient's
active blocks list. Each row shows the source caregiver / clinic,
the date it was created, and any prior lift history.
Open the lift form for the block in question. The form has
four required fields and two optional ones:
| Field | Required | Notes |
|---|---|---|
reason |
Yes | Free text, persisted verbatim into the audit row. See § 3 for what makes a good reason. |
concept_guids |
Yes | Mechanical filter — list of concept GUIDs you need to see. The lift exposes ONLY these concepts. |
expires_in |
No | Seconds; defaults to 24 × 3600. Shorten if you only need an hour; do not lengthen casually. |
from_date |
No | ISO 8601; further narrows the window. Use when the relevant clinical event is from a known prior episode. |
until_date |
No | ISO 8601; pairs with from_date. |
Confirm and submit. The UI shows you a final summary including
the projected lift_expires_at timestamp. Read it before clicking
submit — the audit row is going to quote you.
Read the data you came for. Do not browse beyond the concept
set you listed; every read against the lifted scope is audited
under the same lift_expires_at window.
The lift auto-re-imposes at lift_expires_at via the
flask sweep-blocks cron (#202). You do not need to do anything
to restore the block.
If you needed less than the full 24 hours, you may explicitly lift
the lift (i.e., re-impose the block early) from the same admin form;
the audit row notes the early re-impose.
Every indispensable-care lift writes one AuditLog row with:
event_type = "block.lifted"patient_guid = the patientresource_type = "PatientBlock", resource_guid = the blockactor_guid = your user GUIDactor_type = "user"detail.mechanism = "indispensable_care"detail.reason = your reason verbatimdetail.lift_concept_guids, detail.lift_from_date,detail.lift_until_date, detail.lift_expires_atdetail.admin_route = true (distinguishes this from the staffsession_id = your SSO session (ticket #203 / #191)ip_address, request_path, request_methodThat row is immutable — no UI route can edit it. The patient sees
the same row's projection through the audit-link in their
indispensable-care notification (#210).
PatientClinicAssignment.reason looks likeThe reason is the load-bearing field for any later compliance
review. It must say why the lift was indispensable, not just what
you did. Examples:
Write the reason as if a reviewer who wasn't there will read it six
months from now.
lift_expires_at is set at lift time (default utcnow() + 24h,
override via expires_in). The block does not stay lifted past
that time:
PatientBlock.is_active() method treats the block as activelift_expires_at passes — even before theflask sweep-blocks cron (#202) walks expired lifts hourlyblock.re_imposed webhook so cache subscribersThe 24-hour default was confirmed lawful by counsel 2026-06-04. Do
not raise it without a corresponding case-by-case justification in
the reason field.
Every indispensable-care lift triggers a patient-portal notification
following the template at
gateway/app/copy/sparr_copy.json::indispensable_care_notification
(ticket #210). At time of writing the copy is draft pending legal
review; once approved, the portal will deliver it via the channels
the patient has configured (email primary; SMS if registered).
The notification carries:
reason verbatim.You do not need to (and should not) inform the patient separately —
the notification is the patient's record of the access. Calling the
patient on top of the notification is fine when the clinical context
warrants it; it is not required by the law or by this runbook.
Set on the macmini via the service's .env (see § 1 of CLAUDE.md for
the env-file contract):
| Env var | Default | Purpose |
|---|---|---|
IPS_INDISPENSABLE_LIFT_ROLES |
physician,admin |
Comma-separated list of roles permitted on POST /api/v1/admin/blocks/<g>/lift. SU admins always pass regardless. |
IPS_WEBHOOK_SECRET |
unset | Required for block.lifted / block.re_imposed webhooks to fire. |
IPS_WEBHOOK_TARGETS |
empty list | Comma-separated URLs that should be notified on block events. |
gateway/app/api/admin_blocks_routes.py (route),gateway/app/services/block_expiry_service.py (sweep),gateway/app/copy/sparr_copy.json (patient notification text).gateway/tests/test_admin_blocks_lift.py,gateway/tests/test_block_expiry_and_webhook.py,gateway/tests/test_sparr_copy.py.This page MUST be reviewed by one clinical lead before the spärr
admin UI is exposed in production, per spärr_implementation_plan.md
§ 12.3. Record the review here when it happens:
| Reviewer (role, caregiver) | Date | Version reviewed | Notes |
|---|---|---|---|
| pending | pending | initial draft |
When you click "lift" from the admin UI for the first time, this
section should already carry a signed-off row.