Projo FHIR Documentation
0.1.0 - ci-build
Projo FHIR Documentation - Local Development build (v0.1.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions
Projo's FHIR data model spans 27 resource types. Together they model the tenancy graph, the conversational identities that can receive WhatsApp messages, the care-plan schedule layer produced by PlanDefinition/$apply, the dispatch (Task) → contract (CommunicationRequest) → audit (Communication) → answer (QuestionnaireResponse) → extraction (Observation/Condition/MedicationStatement) pipeline, and the idempotency/routing anchors (Provenance, Flag) that keep the orchestrator replay-safe.
| # | Resource | Role |
|---|---|---|
| 1 | Organization | Customer/tenant; carries WhatsApp phone (telecom), plan-slots, cached phone-number-id |
| 2 | Patient | End user; linked to Organization via managingOrganization; carries telecom (phone) and BSUID identifier |
| 3 | Practitioner | Alternative conversational identity (telemedicine line owner) |
| 4 | RelatedPerson | Alternative conversational identity |
| 5 | Person | Walked via Person.link for non-conversational-only reverse-CR reason |
| 6 | CarePlan | Schedule container; produced by PlanDefinition/$apply; carries subject, status, timezone extension |
| 7 | PlanDefinition | Care-plan template; canonical URLs pinned via plan-slot extension |
| 8 | Library | CQL library for clinical-routing $apply evaluation |
| 9 | Task | Single questionnaire dispatch; carries for, status, businessStatus, output, restriction.period, delivery-time extension |
| 10 | Questionnaire | Form definition; carries SDC extraction annotations, constraints, itemMedia, translation, ordinalValue |
| 11 | QuestionnaireResponse | Patient's answers; status, questionnaire, subject, authored, nested item[] |
| 12 | CommunicationRequest | Contract surface between projo and sidecar/web; medium, category, status, recipient[], subject, about[], payload[], occurrencePeriod, basedOn, identifier[], many extensions |
| 13 | Communication | Per-message audit + projection state; status, category, medium, partOf, sender, recipient[], payload[].contentString, identifier (wamid), sent/received |
| 14 | Provenance | Extraction + routing idempotency anchor; meta.tag, target[], entity[0].what=QR, activity=DERIVE |
| 15 | Observation | Extracted clinical fact (observation-based extraction); code = Questionnaire.item.code, derivedFrom → QR |
| 16 | Condition | Extracted clinical fact (definition-based extraction); linked only via Provenance |
| 17 | MedicationStatement | Extracted from treatment questionnaire items (definition-based) |
| 18 | Flag | Raised by route-to-review bounded action when router cannot enroll |
| 19 | Subscription | 4 managed Subscriptions wiring the apps together |
| 20 | SearchParameter | Single custom SearchParameter: web-answer-token on CommunicationRequest |
| 21 | Binary | Wraps JSON-Patch payload for Patient write-back; also FHIR Binary storage for media attachments |
| 22 | Parameters | Used for $apply, $subscribe-due-events, $extract operation I/O |
| 23 | Bundle | Transaction bundles for atomic writes (CR+Task, extraction, routing, inbound+reverseCR, inbound+answer) |
| 24 | Basic | FHIR-capacity probe resource (measure-fhir-capacity CLI) |
| 25 | StructureDefinition | Read side-effect during $extract (CQF-FHIR DAO needs it) |
| 26 | PractitionerRole | Read/created by orchestrator tooling for admin role seeding |
| 27 | RequestGroup | Returned by $apply (PlanDefinition output) |
The resources are not a flat list; they form a directed reference graph rooted at Task (the dispatch) and CommunicationRequest (the contract between projo and the sidecar/web front-end). The links below are the ones the orchestrator writes and reads at runtime. References that are intentionally absent are called out separately in the following sections.
graph
Task -- "for" --> Pt["Patient / Practitioner / RelatedPerson"]
Task -- "basedOn" --> CarePlan
Task -- "output: communication-request" --> CR["CommunicationRequest"]
Task -- "output: questionnaire-response" --> QR["QuestionnaireResponse"]
Task -- "input: valueReference" --> Q["Questionnaire"]
CR -- "recipient" --> Pt
CR -- "subject" --> Pt
CR -- "about" --> Task
CR -- "payload.contentReference (initial)" --> Q
CR -- "payload.contentReference (appended)" --> QR
CR -- "basedOn" --> ParentCR["Parent CommunicationRequest"]
Comm["Communication"] -- "partOf" --> CR
Comm -- "sender" --> Pt
Comm -- "identifier: wamid" --> Wamid[("wamid")]
QR -- "questionnaire" --> Q
QR -- "subject" --> Pt
Obs["Observation"] -- "code from Questionnaire.item.code" --> Q
Obs -- "derivedFrom" --> QR
Obs -- "identifier: extracted-resource" --> DetId[("deterministic id")]
Prov["Provenance"] -- "entity.what" --> QR
Prov -- "target" --> Extracted["Observation / Condition / MedicationStatement"]
Prov -- "meta.tag" --> Tag[("extraction-provenance / routing-provenance")]
Cond["Condition"] -.->|"linked only via Provenance"| Prov
MedStat["MedicationStatement"] -.->|"linked only via Provenance"| Prov
Flag -- "subject" --> Pt
Flag -- "identifier: routing-resource" --> RoutingId[("deterministic id")]
Pt -- "managingOrganization" --> Org["Organization"]
Org -- "partOf" --> RootOrg["Root Organization"]
Sub["Subscription"] -- "meta.tag: managed-subscription" --> SubTag[("managed-subscription")]
Task.for → Patient/Practitioner/RelatedPersonTask.basedOn → CarePlanTask.output[] → CommunicationRequest (code=communication-request), QuestionnaireResponse (code=questionnaire-response)Task.input[] → Questionnaire (valueReference)CommunicationRequest.recipient[] → Patient/Practitioner/RelatedPerson (or identifier-only Reference)CommunicationRequest.subject → same as Task.forCommunicationRequest.about[] → TaskCommunicationRequest.payload[].contentReference → Questionnaire (initial), QuestionnaireResponse (appended by sidecar)CommunicationRequest.basedOn → parent CommunicationRequest (for reminder CRs)Communication.partOf[] → CommunicationRequestCommunication.sender → resolved Reference or identifier-only ReferenceCommunication.identifier → wamidQuestionnaireResponse.questionnaire → Questionnaire canonical URLQuestionnaireResponse.subject → Patient/Practitioner/RelatedPersonQuestionnaireResponse.basedOn — intentionally NOT set (HAPI-0931; link is on CR.payload)Observation.code ← Questionnaire.item.codeObservation.derivedFrom → QuestionnaireResponseObservation.identifier → extracted-resource (deterministic)Provenance.entity[0].what → QuestionnaireResponse (source)Provenance.target[] → extracted resources or routing outputsProvenance.meta.tag → extraction-provenance|sdc-extraction or routing-provenance|clinical-routingFlag.subject → PatientFlag.identifier → routing-resourcePatient.managingOrganization → OrganizationOrganization.partOf → root OrganizationSubscription.meta.tag → managed-subscription codeQuestionnaireResponse.basedOn looks like the obvious place to link a response back to the Task or CommunicationRequest that triggered it. It is not used in Projo, for two compounding reasons:
QuestionnaireResponse.basedOn is Reference(CarePlan | ServiceRequest). A CommunicationRequest (or Task) is not a permitted target type at the R4 definition.basedOn at a disallowed resource type, surfacing as validation failure HAPI-0931. Any attempt to thread the CR↔QR link through basedOn is rejected at the server.Instead, the CR↔QR link is carried on CommunicationRequest.payload[].contentReference: the initial payload points at the Questionnaire, and when the sidecar receives a completed QuestionnaireResponse it appends an additional payload entry whose contentReference points at that QuestionnaireResponse. The CR thus remains the single contract surface that knows about both the form and every answer that has come back against it.
The reverse direction — finding the CommunicationRequest that a Task produced, or conversely finding the Task that owns a given CommunicationRequest — is not searched via CommunicationRequest.about. Although Projo does populate CommunicationRequest.about with the Task reference for traceability, HAPI does not auto-index CommunicationRequest.about (tracked as HAPI-0524), so a search like CommunicationRequest?about=Task/123 does not return results reliably.
The load-bearing Task↔CR link is therefore carried on Task.output[] as an output parameter with code communication-request and a valueReference pointing at the CommunicationRequest. Queries that need to resolve the CR for a Task read Task.output directly; queries that need the reverse (CR → Task) read CommunicationRequest.about for the reference value but must not rely on it being indexed for search. This asymmetry is deliberate and is the reason the link is authored in both directions.