fhir-sfdc-wrapper-lambda¶
Status: Current | Last reviewed: 2026-09-01
FHIR R4 façade over Salesforce Life Sciences / Health Cloud clinical objects,
built for the adverse-event-reporting and appointment-management capabilities
(capability packs). The capability
speaks standard OAuth 2.0 + FHIR to this API; every Salesforce fact
(connected app, JWT Bearer, object/field names) is the Lambda's concern only.
Source¶
Runtime¶
| Aspect | Value |
|---|---|
| Language | Python 3.12 (deps: PyJWT + cryptography for the SF JWT Bearer assertion; everything else stdlib) |
| Salesforce REST | API v67.0 (matches the sandbox; required for current ServiceAppointment fields) |
| Trigger | API Gateway v2 HTTP API ($default route, AWS_PROXY 2.0) |
| Deploy | bash deploy.sh — pure AWS CLI, no Terraform (deliberate) |
Endpoints¶
| Route | Maps to |
|---|---|
POST /oauth/token |
client-credentials → 15-min HS256 JWT (self-issued and self-verified) |
GET /fhir/Patient?name&birthdate&phone |
Person Account SOQL → FHIR searchset Bundle (id = Account id). phone is an optional format-tolerant filter (digits-tail SOQL narrow + Python suffix compare, so E.164 matches a free-form org copy); the appointment capability uses verified name + birthdate and Customer Authentication remains the access gate |
GET /fhir/Appointment?patient&date&status |
ServiceAppointment SOQL → FHIR searchset Bundle. patient is a Salesforce Account/Contact reference; date supports FHIR eq, ge, gt, le, and lt prefixes; status accepts FHIR Appointment status values |
GET /fhir/Appointment/{id} |
One ServiceAppointment → FHIR Appointment |
POST /fhir/AppointmentResponse |
Validates the patient actor and updates ServiceAppointment status/comments; accepted confirms the appointment and maps to Scheduled |
POST /fhir/PaymentNotice |
Idempotently writes the processor transaction and copay summary to ServiceAppointment, plus a completed Task on the patient timeline |
POST /fhir/AdverseEvent |
AdverseEventEntry + AdverseEventCause (Medication find-or-create) + narrative Task |
POST /fhir/AllergyIntolerance |
AllergyIntolerance + PatientHealthReaction children |
POST /fhir/Condition |
HealthCondition |
POST /fhir/Observation |
CareObservation |
POST /fhir/Communication |
Salesforce Task (decision: no Communication object mapping) |
POST /fhir/DocumentReference |
ContentVersion + ContentDocumentLink. Accepts inline FHIR base64 or an allowlisted s3:// conversation-image reference and can link the same document to both patient and appointment |
GET /healthz |
unauthenticated liveness |
Mapping modules are describe-driven: the org's sf sobject describe
output is the truth, and each fhir_mapping/*.py docstring records where it
diverges from the generic FHIR↔SFDC tables (e.g. AdverseEventEntry has no
outcome/narrative field, so outcome + note are preserved as one completed
Task — GVP verbatim must not be dropped; PatientHealthReaction requires
CausativeSubstanceId, which falls back to the allergy's own code bundle).
The Appointment timezone extension uses ServiceAppointment TimeZone, with
the deployment's APPOINTMENT_TIMEZONE (currently America/New_York) as a
fallback. Appointment start and end remain valid FHIR instants but are
rendered with that zone's date-specific UTC offset (11:00:00-04:00, not a
separate 15:00:00Z plus an instruction asking the model to convert it).
Date-only Appointment searches use midnight boundaries in the same local zone
before converting the SOQL filter to UTC, including daylight-saving changes.
Secrets (Secrets Manager, us-west-2)¶
| Secret | Holds |
|---|---|
fhir-sfdc-wrapper/salesforce |
{client_id, username, login_url, private_key} for the FHIR SFDC Wrapper connected app in the hlsorg sandbox (JWT Bearer) |
fhir-sfdc-wrapper/auth |
{signing_secret, clients} — the /oauth/token client registry |
The Lambda role reads exactly these two ARNs and has s3:GetObject only for
conversations/*/images/* in the configured attachment bucket. ATTACHMENT_BUCKETS
is the comma-separated source allowlist. Env fallback
(SF_CLIENT_ID/SF_USERNAME/SF_LOGIN_URL/SF_PRIVATE_KEY(_PATH),
WRAPPER_SIGNING_SECRET/WRAPPER_CLIENTS) serves the local runner.
Vanguard¶
cd apps/fhir-sfdc-wrapper-lambda
python3 -m pytest tests -q # SFDC_MODE=mock, no AWS/SF needed
SFDC_MODE=mock WRAPPER_SIGNING_SECRET=dev WRAPPER_CLIENTS=dev:dev \
python3 handler.py --serve 8790 # local HTTP bridge
Production / dev deployment¶
Deployed 2026-08-28 to account 465572510980 (us-west-2):
https://6v89u1v0r6.execute-api.us-west-2.amazonaws.com. The platform side is
one oauth2_api integration (base_url + token_url + client id/secret) that
the capability's config references — see
capability packs § findings.
Appointment confirmation workflow¶
Agent 3032 uses the appointment-management capability. After customer
authentication it resolves the patient, queries real appointments, records the
FHIR AppointmentResponse, sends the captured photo's object-storage reference
to DocumentReference, collects the copay, and sends the successful processor
transaction to PaymentNotice. The model receives only the compact attachment
reference. Conversation-api stores the photo in its trusted recordings bucket
and returns the object key, bucket, s3:// URI, and S3 ARN; the capability
passes that exact URI to the allowlisted Lambda. Image bytes travel directly
from S3 to the Lambda and Salesforce.
The capability manifest and topic prompt are source-controlled beside the
Lambda. The seeded demo appointment remains pending until the call so that
the confirmation is the state-changing test, not deployment verification.
The photo-capture step accepts the catalog's current submit_photo action as
well as photos_captured and the legacy upload_headshot name; all three must
resolve to the object-store metadata before DocumentReference is called.
For agent 3032, the draft also has an Agent Logic tool_result rule on
request_salesforce_approval that calls stream_transcript_to_supervisor.
This makes the configured Human Supervision stream deterministic rather than
depending on a second model-selected tool call.
The agent-specific runtime overlay is source-controlled as
agent-3032-runtime-overlay.json. For this dedicated Erik Golden demo draft it
uses a variable_changed rule on the authoritative customer_auth_status
variable to chain the Patient and Appointment reads immediately when passkey
verification completes. This covers out-of-band verification without depending
on the model to call get_customer_auth_status again. It captures the returned
appointment fields into session variables and prevents the general A2UI tool
from rendering an appointment card while the FHIR Appointment id is empty. A
native-voice response guard also interrupts any spoken appointment claim made
before that id exists. This is intentionally stronger than prompt wording
because Nova copied example values from the AppointmentSummary component during
the 2026-08-31 21:21 UTC test without calling either FHIR read.
Capability tools execute through the manual-API runtime, whose result shape is
{status, data}. Tool-logic captures therefore address $.data... (for
example $.data.entry[0].resource.id), not the raw FHIR body at $.entry....
Using the raw-body path lets the Patient request succeed but leaves the bound
Patient id empty, correctly preventing the Appointment request from running.
Seeded hlsorg demo records¶
| Record | Salesforce id | Test state |
|---|---|---|
| Erik Golden Patient Account | 001Va0000131yJFIAY |
Person Account with shadow Contact 003Va00000s2KefIAE |
ServiceAppointment SA-0001 |
08pVa0000014sAvIAI |
pending; 2026-09-03 11:00–11:30 America/New_York; 25 USD copay |
Do not pre-confirm or attach a placeholder image to this record: the end-to-end call is expected to create those state changes. PaymentNotice is idempotent on the processor transaction id so a successful tool retry does not duplicate the patient timeline Task.
devsdo Case live view (follow-on)¶
The wrapper's read endpoints are ready for a Case-page LWC, but that cross-org
UI is not deployed as part of this Lambda. The safe shape is an Apex callout via
a Salesforce Named/External Credential, not a browser call carrying an OAuth
client secret. Store only the FHIR Patient and Appointment references on the
Case; the Apex controller should return a minimal appointment DTO and the LWC
should fetch on demand. Do not copy headshot bytes, card data, or broader
clinical history into the service org. This preserves devsdo as the call/Case
system while hlsorg remains the clinical system of record.
See also¶
- capability-packs — the consuming capability,
oauth2_apiintegration source - salesforce-forward-lambda