diff --git a/docs/api/bulk-api/export.md b/docs/api/bulk-api/export.md index bc847ca02..30fba1f65 100644 --- a/docs/api/bulk-api/export.md +++ b/docs/api/bulk-api/export.md @@ -230,6 +230,8 @@ The `$export` operation accepts several parameters to customize the export: **qu | `_until` | Includes only resources whose **last modification time** is **before** the given instant (`ts < _until`). ISO 8601 format. Together with `_since`, this defines an open window on the modification timestamp. | | `_typeFilter` | Restricts exported rows using FHIR search criteria **per resource type**, in the form `ResourceType?searchParams` (same idea as the [FHIR Bulk Data `_typeFilter`](https://hl7.org/fhir/uv/bulkdata/export.html) parameter). You may repeat `_typeFilter` multiple times. Multiple filters for the **same** resource type are combined with **OR**. If `_type` is present, every type used in `_typeFilter` must also appear in `_type`; types listed in `_type` but without a filter are exported in full. Standard FHIR search parameters such as `_id` are allowed. Not allowed inside the search part: `_sort`, `_count`, `_page`, `_total`, `_summary`, `_elements`, `_include`, `_revinclude`, `_has`, `_assoc`, `_with`. | | `patient` | Restricts the export to specific patients. **GET:** comma-separated patient **ids** (not full references), e.g. `patient=pt-1,pt-2`. Supported only on **patient-level** GET. **POST:** repeat a `parameter` named `patient`, each with `valueReference.reference` set to `Patient/{id}`. Supported on **patient-level** and **group-level** POST; for group export, every listed patient must exist and be a **member** of that group. | +| `onPatientError` | Controls behavior when `patient` references are invalid or not group members. `fail` (default): abort with **422**. `ignore`: drop invalid refs, proceed with valid ones, report dropped in export status `error[]`. See [Lenient patient handling](#lenient-patient-handling). | +| `_elements` | *Experimental.* Comma-separated list of root element names to include in exported resources. Mandatory elements (`resourceType`, `id`, `meta`) are always included. Resources are tagged with `SUBSETTED`. Server may ignore this parameter if `Prefer: handling=lenient` is not set. | ### POST with a Parameters body (patient and group) @@ -278,6 +280,48 @@ Prefer: respond-async } ``` +## Storage override parameters + +{% hint style="info" %} +Available since version 2605. +{% endhint %} + +You can override the system-level cloud storage configuration on a per-request basis using Aidbox-specific parameters. This is useful when different export jobs need to write to different buckets or storage accounts. + +Storage override parameters use the `_aidbox.` prefix to distinguish them from standard FHIR parameters: + +| Parameter | Type | Description | +| -------------------------- | ----------- | -------------------------------------------------------------- | +| `_aidbox.storageProvider` | `string` | Storage provider type: `gcp`, `aws`, or `azure`. | +| `_aidbox.storageBucket` | `string` | Bucket name (GCP or AWS S3). | +| `_aidbox.storageAccount` | `Reference` | Cloud account resource reference (e.g. `AwsAccount/my-acct`). | +| `_aidbox.azureStorage` | `string` | Azure storage account name. | +| `_aidbox.azureContainer` | `string` | Azure blob container name. | + +These parameters can be passed as GET query parameters or in a POST Parameters body. Only the parameters you specify are overridden; unspecified values fall back to the system-level settings. + +{% tabs %} +{% tab title="GET" %} +```http +GET /fhir/Group/grp-1/$export?_type=Patient&_aidbox.storageBucket=custom-bucket&_aidbox.storageProvider=gcp +``` +{% endtab %} + +{% tab title="POST" %} +```json +{ + "resourceType": "Parameters", + "parameter": [ + { "name": "_type", "valueString": "Patient" }, + { "name": "_aidbox.storageProvider", "valueString": "aws" }, + { "name": "_aidbox.storageBucket", "valueString": "custom-bucket" }, + { "name": "_aidbox.storageAccount", "valueReference": { "reference": "AwsAccount/my-acct" } } + ] +} +``` +{% endtab %} +{% endtabs %} + ## Patient-level export Patient-level export extracts all Patient resources and resources associated with them. The association is defined by [FHIR Patient Compartment](http://hl7.org/fhir/r4/compartmentdefinition-patient.html), which specifies which resource types reference patients and through which fields. @@ -444,3 +488,198 @@ Prefer: respond-async {% endtabs %} The status endpoint works the same way as other export levels. Poll `/fhir/$export-status/` to check progress, and send a DELETE request to cancel. + +## Consent-based patient filtering + +{% hint style="info" %} +Available since version 2605. +{% endhint %} + +Group-level export can filter patients based on FHIR [Consent](https://hl7.org/fhir/r4/consent.html) resources. This enables workflows where patient data sharing requires explicit agreement (opt-in) or where patients can refuse sharing (opt-out) — for example, [Da Vinci PDex](https://hl7.org/fhir/us/davinci-pdex/STU2.1/) payer-to-payer exchange and provider access. + +| Parameter | Type | Cardinality | Description | +| -------------------------------- | ------ | ----------- | -------------------------------------------------------------------------------------- | +| `consentStrategy` | `code` | 0..1 | `opt-in` or `opt-out`. Determines how consents affect patient inclusion. | +| `consentProfile` | `uri` | 0..1 | StructureDefinition URL to filter consents by `meta.profile`. Omit to match any Consent.| +| `organizationIdentifierSystem` | `uri` | 0..1 | Which `Organization.identifier.system` to use for actor matching (opt-in only). | + +{% hint style="warning" %} +For `opt-in`, both `organizationIdentifierSystem` and a JWT token with `extensions.hl7-b2b.organization_id` are required. Missing either returns **422**. +{% endhint %} + +### Consent resource requirements + +Consent resources must have: + +- `status` = `active` +- `provision.type` = `permit` (for opt-in) or `deny` (for opt-out) +- `provision.period` with `start` and `end` covering the current date +- `meta.profile` matching `consentProfile` (if specified) + +For **opt-in**, the Consent must also have `provision.actor` entries with: + +- An actor with `role.coding[0].code` = `performer` referencing the **source** Organization (the requesting payer) +- An actor with `role.coding[0].code` = `IRCP` referencing the **recipient** Organization (resolved from `Group.managingEntity`) + +Both organizations are matched by their `identifier` where `system` equals `organizationIdentifierSystem`. + +### Opt-out strategy + +All group members are included by default. Members are excluded only if they have an active Consent with `provision.type = "deny"` matching the specified profile and a valid period covering the current date. + +{% tabs %} +{% tab title="GET" %} +```http +GET /fhir/Group/grp-1/$export?_type=Patient&consentStrategy=opt-out&consentProfile=http://hl7.org/fhir/us/davinci-pdex/StructureDefinition/pdex-provider-consent +``` +{% endtab %} + +{% tab title="POST" %} +```json +{ + "resourceType": "Parameters", + "parameter": [ + { "name": "_type", "valueString": "Patient" }, + { "name": "consentStrategy", "valueCode": "opt-out" }, + { "name": "consentProfile", "valueUri": "http://hl7.org/fhir/us/davinci-pdex/StructureDefinition/pdex-provider-consent" } + ] +} +``` +{% endtab %} +{% endtabs %} + +### Opt-in strategy + +No members are included by default. Members are included only if they have an active Consent with `provision.type = "permit"`, matching source and recipient organization actors, and a valid period. + +The **source organization** is identified from the JWT access token's [UDAP B2B Authorization Extension](https://build.fhir.org/ig/HL7/fhir-udap-security-ig/branches/master/b2b.html) (`extensions.hl7-b2b.organization_id`). The **recipient organization** is resolved from `Group.managingEntity`, using the `organizationIdentifierSystem` to select the correct identifier. + +{% tabs %} +{% tab title="GET" %} +```http +GET /fhir/Group/grp-1/$export?_type=Patient&consentStrategy=opt-in&consentProfile=http://hl7.org/fhir/us/davinci-hrex/StructureDefinition/hrex-consent&organizationIdentifierSystem=urn:oid:2.16.840.1.113883.4.4 +``` +{% endtab %} + +{% tab title="POST" %} +```json +{ + "resourceType": "Parameters", + "parameter": [ + { "name": "_type", "valueString": "Patient" }, + { "name": "consentStrategy", "valueCode": "opt-in" }, + { "name": "consentProfile", "valueUri": "http://hl7.org/fhir/us/davinci-hrex/StructureDefinition/hrex-consent" }, + { "name": "organizationIdentifierSystem", "valueUri": "urn:oid:2.16.840.1.113883.4.4" } + ] +} +``` +{% endtab %} +{% endtabs %} + +### UDAP B2B token configuration + +For opt-in workflows, the requesting client must present a **JWT** access token containing the B2B extension. The Client must be configured with `token_format: "jwt"` in the `client_credentials` auth settings — opaque tokens do not carry B2B claims. + +Add the `client-hl7B2b` extension to your Client resource: + +```json +{ + "resourceType": "Client", + "id": "payer-client", + "grant_types": ["client_credentials"], + "auth": { + "client_credentials": { + "token_format": "jwt" + } + }, + "extension": [ + { + "url": "http://health-samurai.io/fhir/core/StructureDefinition/client-hl7B2b", + "extension": [ + { + "url": "organization", + "valueReference": { "reference": "Organization/my-org" } + }, + { + "url": "organizationIdentifierSystem", + "valueUri": "urn:oid:2.16.840.1.113883.4.4" + }, + { + "url": "purposeOfUse", + "valueCoding": { + "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", + "code": "HPAYMT" + } + } + ] + } + ] +} +``` + +When this extension is present, `/auth/token` includes the [UDAP B2B Authorization Extension](https://build.fhir.org/ig/HL7/fhir-udap-security-ig/branches/master/b2b.html) in the JWT: + +```json +{ + "extensions": { + "hl7-b2b": { + "version": "1", + "organization_id": "urn:oid:2.16.840.1.113883.4.4#12-3456789", + "organization_name": "My Organization", + "purpose_of_use": ["http://terminology.hl7.org/CodeSystem/v3-ActReason#HPAYMT"] + } + } +} +``` + +The `organization_id` is resolved from the referenced Organization's identifier where `system` matches `organizationIdentifierSystem`. + +## Lenient patient handling + +{% hint style="info" %} +Available since version 2605. +{% endhint %} + +By default, `$export` returns **422** when a `patient` reference points to a non-existent Patient or a Patient that is not a member of the specified Group. This follows the base [FHIR Bulk Data Export](https://hl7.org/fhir/uv/bulkdata/export.html) specification. + +The [Da Vinci `$davinci-data-export`](http://hl7.org/fhir/us/davinci-atr/OperationDefinition-davinci-data-export.html) specification requires different behavior: invalid patient references should be silently ignored, and the export should proceed with valid ones. + +Set `onPatientError=ignore` to enable this behavior: + +| `onPatientError` | Behavior | +| ---------------- | -------- | +| `fail` (default) | Abort with **422** if any patient reference is invalid or not a group member. | +| `ignore` | Drop invalid references. Proceed with valid ones. Report each dropped patient as an OperationOutcome warning in the export status `error[]` array. | + +When all requested patients are invalid, the server returns an immediate **200** with an empty `output[]` and warnings in `error[]` — no async export is started. + +{% tabs %} +{% tab title="POST" %} +```json +{ + "resourceType": "Parameters", + "parameter": [ + { "name": "_type", "valueString": "Patient" }, + { "name": "onPatientError", "valueCode": "ignore" }, + { "name": "patient", "valueReference": { "reference": "Patient/valid-member" } }, + { "name": "patient", "valueReference": { "reference": "Patient/non-existent" } } + ] +} +``` +{% endtab %} + +{% tab title="Export status error[]" %} +```json +{ + "status": "completed", + "output": [ + { "type": "Patient", "url": "https://storage/...", "count": 1 } + ], + "error": [ + { "type": "OperationOutcome", + "url": "urn:warning:Patient/non-existent ignored: not found or not a member of the group" } + ] +} +``` +{% endtab %} +{% endtabs %}