hubble-sdk-python 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/CHANGELOG.md +13 -0
  2. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/PKG-INFO +31 -2
  3. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/README.md +30 -1
  4. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/__init__.py +12 -0
  5. hubble_sdk_python-0.2.0/hubble_sdk/_generated/api/default/export_patient_fhir.py +262 -0
  6. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/default/export_patient_pdf.py +64 -14
  7. hubble_sdk_python-0.2.0/hubble_sdk/_generated/api/default/get_patient_pdf_export.py +261 -0
  8. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/default/list_consents.py +46 -0
  9. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/__init__.py +20 -0
  10. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/consent.py +1 -1
  11. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/consent_link_request.py +14 -13
  12. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/consent_list.py +16 -1
  13. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/consent_source.py +11 -1
  14. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/consent_source_status.py +1 -0
  15. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/device_authorization_request.py +14 -13
  16. hubble_sdk_python-0.2.0/hubble_sdk/_generated/models/export_patient_fhir_prefer.py +8 -0
  17. hubble_sdk_python-0.2.0/hubble_sdk/_generated/models/export_patient_fhir_response_202.py +105 -0
  18. hubble_sdk_python-0.2.0/hubble_sdk/_generated/models/export_patient_fhir_response_202_status.py +9 -0
  19. hubble_sdk_python-0.2.0/hubble_sdk/_generated/models/export_patient_pdf_prefer.py +8 -0
  20. hubble_sdk_python-0.2.0/hubble_sdk/_generated/models/export_patient_pdf_response_202.py +105 -0
  21. hubble_sdk_python-0.2.0/hubble_sdk/_generated/models/export_patient_pdf_response_202_status.py +9 -0
  22. hubble_sdk_python-0.2.0/hubble_sdk/_generated/models/get_patient_pdf_export_download.py +8 -0
  23. hubble_sdk_python-0.2.0/hubble_sdk/_generated/models/get_patient_pdf_export_response_200.py +114 -0
  24. hubble_sdk_python-0.2.0/hubble_sdk/_generated/models/get_patient_pdf_export_response_200_status.py +8 -0
  25. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/token_request.py +1 -1
  26. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/token_request_grant_type.py +0 -1
  27. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_hubble.py +34 -10
  28. hubble_sdk_python-0.2.0/hubble_sdk/_webhooks.py +200 -0
  29. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/pydoc-markdown.yml +6 -4
  30. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/pyproject.toml +1 -1
  31. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/tests/test_hubble.py +53 -10
  32. hubble_sdk_python-0.2.0/tests/test_webhooks.py +181 -0
  33. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/.gitignore +0 -0
  34. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/LICENSE +0 -0
  35. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/config.yaml +0 -0
  36. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/__init__.py +0 -0
  37. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/__init__.py +0 -0
  38. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/default/__init__.py +0 -0
  39. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/default/attach_client_authorization.py +0 -0
  40. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/default/create_consent_link.py +0 -0
  41. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/default/exchange_token.py +0 -0
  42. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/default/get_consent.py +0 -0
  43. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/default/read_fhir.py +0 -0
  44. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/api/default/revoke_consent.py +0 -0
  45. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/client.py +0 -0
  46. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/errors.py +0 -0
  47. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/api_error.py +0 -0
  48. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/attach_client_authorization_body.py +0 -0
  49. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/consent_error.py +0 -0
  50. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/consent_source_type.py +0 -0
  51. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/consent_status.py +0 -0
  52. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/device_authorization_response.py +0 -0
  53. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/fhir_resource.py +0 -0
  54. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/o_auth_error.py +0 -0
  55. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/o_auth_error_error.py +0 -0
  56. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/operation_outcome.py +0 -0
  57. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/operation_outcome_issue_item.py +0 -0
  58. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/patient_address.py +0 -0
  59. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/patient_demographics.py +0 -0
  60. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/provider_seed.py +0 -0
  61. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/models/token_response.py +0 -0
  62. {hubble_sdk_python-0.1.0 → hubble_sdk_python-0.2.0}/hubble_sdk/_generated/types.py +0 -0
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.0](https://github.com/hubble-dot-ai/hubble-sdks/compare/hubble-sdk-python-v0.1.0...hubble-sdk-python-v0.2.0) (2026-09-30)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * the `authorization_code` value is removed from the token `grant_type` enum (it was never available), and `ConsentList.next_cursor` / `ConsentSource.id` are now required response fields.
9
+
10
+ ### Features
11
+
12
+ * add revoked consent source status ([#51](https://github.com/hubble-dot-ai/hubble-sdks/issues/51)) ([b2dc24d](https://github.com/hubble-dot-ai/hubble-sdks/commit/b2dc24d3359ce1ea245bd881303ada2c425e3132))
13
+ * async record exports, consent pagination, and per-source ids ([#47](https://github.com/hubble-dot-ai/hubble-sdks/issues/47)) ([ddf8e5b](https://github.com/hubble-dot-ai/hubble-sdks/commit/ddf8e5b02436d6ae69d0ca20e723752ee21349a9))
14
+ * verify and parse webhook events ([#50](https://github.com/hubble-dot-ai/hubble-sdks/issues/50)) ([e71b0bc](https://github.com/hubble-dot-ai/hubble-sdks/commit/e71b0bc80b38fbd070a4b6fd5ddfcbada1e8e311))
15
+
3
16
  ## [0.1.0](https://github.com/hubble-dot-ai/hubble-sdks/compare/hubble-sdk-python-v0.0.3...hubble-sdk-python-v0.1.0) (2026-09-02)
4
17
 
5
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hubble-sdk-python
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Python client for the Hubble Patient Access API.
5
5
  Project-URL: Homepage, https://github.com/hubble-dot-ai/hubble-sdks
6
6
  Project-URL: Repository, https://github.com/hubble-dot-ai/hubble-sdks
@@ -64,6 +64,35 @@ for consent in hub.list_consents(status="active"):
64
64
  hub.revoke_consent(session.consent_id)
65
65
  ```
66
66
 
67
+ ## Receiving webhooks
68
+
69
+ Verify each delivery against your endpoint's signing secret (from the portal's
70
+ Webhooks page) and get the event back. Pass the **raw** body: a body that was
71
+ parsed and re-serialized will not verify.
72
+
73
+ ```python
74
+ from hubble_sdk import WebhookSignatureError, construct_webhook_event
75
+
76
+ # e.g. Flask
77
+ @app.post("/hubble/webhooks")
78
+ def hubble_webhook():
79
+ try:
80
+ event = construct_webhook_event(
81
+ request.get_data(), request.headers.get("Hubble-Signature"), WEBHOOK_SECRET
82
+ )
83
+ except WebhookSignatureError:
84
+ return "bad signature", 400
85
+ if event.type == "consent.source.available":
86
+ print(event.consent_id, event.source.id)
87
+ # newer event types may arrive; ignore what you don't handle
88
+ return "", 204
89
+ ```
90
+
91
+ In FastAPI, read the body with `await request.body()`. Deliveries are
92
+ at-least-once, so dedupe on `event.id`. Pass `secret` as a list to accept the old
93
+ and new secret during a rotation, and `tolerance` to change the default 5-minute
94
+ replay window.
95
+
67
96
  ## What the client handles for you
68
97
 
69
98
  - **Auth per plane** — HTTP Basic (your app credentials) for consent management
@@ -82,7 +111,7 @@ hub.revoke_consent(session.consent_id)
82
111
 
83
112
  ## Layout
84
113
 
85
- `Hubble` / `PatientSession` are hand-written. The low-level client generated
114
+ `Hubble` / `PatientSession` and the webhook helpers are hand-written. The low-level client generated
86
115
  from the OpenAPI contract with
87
116
  [openapi-python-client](https://github.com/openapi-generators/openapi-python-client)
88
117
  lives under `hubble_sdk._generated` (regenerated on every spec change;
@@ -32,6 +32,35 @@ for consent in hub.list_consents(status="active"):
32
32
  hub.revoke_consent(session.consent_id)
33
33
  ```
34
34
 
35
+ ## Receiving webhooks
36
+
37
+ Verify each delivery against your endpoint's signing secret (from the portal's
38
+ Webhooks page) and get the event back. Pass the **raw** body: a body that was
39
+ parsed and re-serialized will not verify.
40
+
41
+ ```python
42
+ from hubble_sdk import WebhookSignatureError, construct_webhook_event
43
+
44
+ # e.g. Flask
45
+ @app.post("/hubble/webhooks")
46
+ def hubble_webhook():
47
+ try:
48
+ event = construct_webhook_event(
49
+ request.get_data(), request.headers.get("Hubble-Signature"), WEBHOOK_SECRET
50
+ )
51
+ except WebhookSignatureError:
52
+ return "bad signature", 400
53
+ if event.type == "consent.source.available":
54
+ print(event.consent_id, event.source.id)
55
+ # newer event types may arrive; ignore what you don't handle
56
+ return "", 204
57
+ ```
58
+
59
+ In FastAPI, read the body with `await request.body()`. Deliveries are
60
+ at-least-once, so dedupe on `event.id`. Pass `secret` as a list to accept the old
61
+ and new secret during a rotation, and `tolerance` to change the default 5-minute
62
+ replay window.
63
+
35
64
  ## What the client handles for you
36
65
 
37
66
  - **Auth per plane** — HTTP Basic (your app credentials) for consent management
@@ -50,7 +79,7 @@ hub.revoke_consent(session.consent_id)
50
79
 
51
80
  ## Layout
52
81
 
53
- `Hubble` / `PatientSession` are hand-written. The low-level client generated
82
+ `Hubble` / `PatientSession` and the webhook helpers are hand-written. The low-level client generated
54
83
  from the OpenAPI contract with
55
84
  [openapi-python-client](https://github.com/openapi-generators/openapi-python-client)
56
85
  lives under `hubble_sdk._generated` (regenerated on every spec change;
@@ -28,6 +28,13 @@ from ._hubble import (
28
28
  PdfExport,
29
29
  TokenSet,
30
30
  )
31
+ from ._webhooks import (
32
+ WEBHOOK_SIGNATURE_HEADER,
33
+ WebhookEvent,
34
+ WebhookEventSource,
35
+ WebhookSignatureError,
36
+ construct_webhook_event,
37
+ )
31
38
 
32
39
  __all__ = [
33
40
  "Hubble",
@@ -36,6 +43,11 @@ __all__ = [
36
43
  "ConsentTimeout",
37
44
  "TokenSet",
38
45
  "PdfExport",
46
+ "construct_webhook_event",
47
+ "WebhookEvent",
48
+ "WebhookEventSource",
49
+ "WebhookSignatureError",
50
+ "WEBHOOK_SIGNATURE_HEADER",
39
51
  "Consent",
40
52
  "ConsentStatus",
41
53
  "DeviceAuthorizationResponse",
@@ -0,0 +1,262 @@
1
+ from http import HTTPStatus
2
+ from typing import Any
3
+
4
+ import httpx
5
+
6
+ from ... import errors
7
+ from ...client import AuthenticatedClient, Client
8
+ from ...models.export_patient_fhir_prefer import ExportPatientFhirPrefer
9
+ from ...models.export_patient_fhir_response_202 import ExportPatientFhirResponse202
10
+ from ...models.fhir_resource import FhirResource
11
+ from ...models.operation_outcome import OperationOutcome
12
+ from ...types import UNSET, Response, Unset
13
+
14
+
15
+ def _get_kwargs(
16
+ *,
17
+ field_count: int | Unset = UNSET,
18
+ field_offset: int | Unset = UNSET,
19
+ prefer: ExportPatientFhirPrefer | Unset = UNSET,
20
+ ) -> dict[str, Any]:
21
+ headers: dict[str, Any] = {}
22
+ if not isinstance(prefer, Unset):
23
+ headers["Prefer"] = str(prefer)
24
+
25
+ params: dict[str, Any] = {}
26
+
27
+ params["_count"] = field_count
28
+
29
+ params["_offset"] = field_offset
30
+
31
+ params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
32
+
33
+ _kwargs: dict[str, Any] = {
34
+ "method": "get",
35
+ "url": "/api/v1/fhir/Patient/$everything",
36
+ "params": params,
37
+ }
38
+
39
+ _kwargs["headers"] = headers
40
+ return _kwargs
41
+
42
+
43
+ def _parse_response(
44
+ *, client: AuthenticatedClient | Client, response: httpx.Response
45
+ ) -> ExportPatientFhirResponse202 | FhirResource | OperationOutcome | None:
46
+ if response.status_code == 200:
47
+ response_200 = FhirResource.from_dict(response.json())
48
+
49
+ return response_200
50
+
51
+ if response.status_code == 202:
52
+ response_202 = ExportPatientFhirResponse202.from_dict(response.json())
53
+
54
+ return response_202
55
+
56
+ if response.status_code == 400:
57
+ response_400 = OperationOutcome.from_dict(response.json())
58
+
59
+ return response_400
60
+
61
+ if response.status_code == 401:
62
+ response_401 = OperationOutcome.from_dict(response.json())
63
+
64
+ return response_401
65
+
66
+ if response.status_code == 403:
67
+ response_403 = OperationOutcome.from_dict(response.json())
68
+
69
+ return response_403
70
+
71
+ if response.status_code == 404:
72
+ response_404 = OperationOutcome.from_dict(response.json())
73
+
74
+ return response_404
75
+
76
+ if response.status_code == 429:
77
+ response_429 = OperationOutcome.from_dict(response.json())
78
+
79
+ return response_429
80
+
81
+ if response.status_code == 503:
82
+ response_503 = OperationOutcome.from_dict(response.json())
83
+
84
+ return response_503
85
+
86
+ if client.raise_on_unexpected_status:
87
+ raise errors.UnexpectedStatus(response.status_code, response.content)
88
+ else:
89
+ return None
90
+
91
+
92
+ def _build_response(
93
+ *, client: AuthenticatedClient | Client, response: httpx.Response
94
+ ) -> Response[ExportPatientFhirResponse202 | FhirResource | OperationOutcome]:
95
+ return Response(
96
+ status_code=HTTPStatus(response.status_code),
97
+ content=response.content,
98
+ headers=response.headers,
99
+ parsed=_parse_response(client=client, response=response),
100
+ )
101
+
102
+
103
+ def sync_detailed(
104
+ *,
105
+ client: AuthenticatedClient,
106
+ field_count: int | Unset = UNSET,
107
+ field_offset: int | Unset = UNSET,
108
+ prefer: ExportPatientFhirPrefer | Unset = UNSET,
109
+ ) -> Response[ExportPatientFhirResponse202 | FhirResource | OperationOutcome]:
110
+ """Read or asynchronously export the patient's FHIR record
111
+
112
+ Without Prefer, returns the existing paginated FHIR searchset. Send Prefer: respond-async without
113
+ query parameters to prepare health-records.json in the background. The file is a FHIR collection
114
+ Bundle containing the captured provider sources, including each source's Patient and warnings for
115
+ incomplete sources. It has no paging links, search metadata or total field, and is not a Bulk Data
116
+ $export/NDJSON response. Poll Content-Location with the same consent token no sooner than Retry-
117
+ After for a 303 download redirect. An organization can hold 20 exports in flight at once; a repeat
118
+ request for one already running reuses it. Payer records are not included.
119
+
120
+ Args:
121
+ field_count (int | Unset):
122
+ field_offset (int | Unset):
123
+ prefer (ExportPatientFhirPrefer | Unset):
124
+
125
+ Raises:
126
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
127
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
128
+
129
+ Returns:
130
+ Response[ExportPatientFhirResponse202 | FhirResource | OperationOutcome]
131
+ """
132
+
133
+ kwargs = _get_kwargs(
134
+ field_count=field_count,
135
+ field_offset=field_offset,
136
+ prefer=prefer,
137
+ )
138
+
139
+ response = client.get_httpx_client().request(
140
+ **kwargs,
141
+ )
142
+
143
+ return _build_response(client=client, response=response)
144
+
145
+
146
+ def sync(
147
+ *,
148
+ client: AuthenticatedClient,
149
+ field_count: int | Unset = UNSET,
150
+ field_offset: int | Unset = UNSET,
151
+ prefer: ExportPatientFhirPrefer | Unset = UNSET,
152
+ ) -> ExportPatientFhirResponse202 | FhirResource | OperationOutcome | None:
153
+ """Read or asynchronously export the patient's FHIR record
154
+
155
+ Without Prefer, returns the existing paginated FHIR searchset. Send Prefer: respond-async without
156
+ query parameters to prepare health-records.json in the background. The file is a FHIR collection
157
+ Bundle containing the captured provider sources, including each source's Patient and warnings for
158
+ incomplete sources. It has no paging links, search metadata or total field, and is not a Bulk Data
159
+ $export/NDJSON response. Poll Content-Location with the same consent token no sooner than Retry-
160
+ After for a 303 download redirect. An organization can hold 20 exports in flight at once; a repeat
161
+ request for one already running reuses it. Payer records are not included.
162
+
163
+ Args:
164
+ field_count (int | Unset):
165
+ field_offset (int | Unset):
166
+ prefer (ExportPatientFhirPrefer | Unset):
167
+
168
+ Raises:
169
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
170
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
171
+
172
+ Returns:
173
+ ExportPatientFhirResponse202 | FhirResource | OperationOutcome
174
+ """
175
+
176
+ return sync_detailed(
177
+ client=client,
178
+ field_count=field_count,
179
+ field_offset=field_offset,
180
+ prefer=prefer,
181
+ ).parsed
182
+
183
+
184
+ async def asyncio_detailed(
185
+ *,
186
+ client: AuthenticatedClient,
187
+ field_count: int | Unset = UNSET,
188
+ field_offset: int | Unset = UNSET,
189
+ prefer: ExportPatientFhirPrefer | Unset = UNSET,
190
+ ) -> Response[ExportPatientFhirResponse202 | FhirResource | OperationOutcome]:
191
+ """Read or asynchronously export the patient's FHIR record
192
+
193
+ Without Prefer, returns the existing paginated FHIR searchset. Send Prefer: respond-async without
194
+ query parameters to prepare health-records.json in the background. The file is a FHIR collection
195
+ Bundle containing the captured provider sources, including each source's Patient and warnings for
196
+ incomplete sources. It has no paging links, search metadata or total field, and is not a Bulk Data
197
+ $export/NDJSON response. Poll Content-Location with the same consent token no sooner than Retry-
198
+ After for a 303 download redirect. An organization can hold 20 exports in flight at once; a repeat
199
+ request for one already running reuses it. Payer records are not included.
200
+
201
+ Args:
202
+ field_count (int | Unset):
203
+ field_offset (int | Unset):
204
+ prefer (ExportPatientFhirPrefer | Unset):
205
+
206
+ Raises:
207
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
208
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
209
+
210
+ Returns:
211
+ Response[ExportPatientFhirResponse202 | FhirResource | OperationOutcome]
212
+ """
213
+
214
+ kwargs = _get_kwargs(
215
+ field_count=field_count,
216
+ field_offset=field_offset,
217
+ prefer=prefer,
218
+ )
219
+
220
+ response = await client.get_async_httpx_client().request(**kwargs)
221
+
222
+ return _build_response(client=client, response=response)
223
+
224
+
225
+ async def asyncio(
226
+ *,
227
+ client: AuthenticatedClient,
228
+ field_count: int | Unset = UNSET,
229
+ field_offset: int | Unset = UNSET,
230
+ prefer: ExportPatientFhirPrefer | Unset = UNSET,
231
+ ) -> ExportPatientFhirResponse202 | FhirResource | OperationOutcome | None:
232
+ """Read or asynchronously export the patient's FHIR record
233
+
234
+ Without Prefer, returns the existing paginated FHIR searchset. Send Prefer: respond-async without
235
+ query parameters to prepare health-records.json in the background. The file is a FHIR collection
236
+ Bundle containing the captured provider sources, including each source's Patient and warnings for
237
+ incomplete sources. It has no paging links, search metadata or total field, and is not a Bulk Data
238
+ $export/NDJSON response. Poll Content-Location with the same consent token no sooner than Retry-
239
+ After for a 303 download redirect. An organization can hold 20 exports in flight at once; a repeat
240
+ request for one already running reuses it. Payer records are not included.
241
+
242
+ Args:
243
+ field_count (int | Unset):
244
+ field_offset (int | Unset):
245
+ prefer (ExportPatientFhirPrefer | Unset):
246
+
247
+ Raises:
248
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
249
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
250
+
251
+ Returns:
252
+ ExportPatientFhirResponse202 | FhirResource | OperationOutcome
253
+ """
254
+
255
+ return (
256
+ await asyncio_detailed(
257
+ client=client,
258
+ field_count=field_count,
259
+ field_offset=field_offset,
260
+ prefer=prefer,
261
+ )
262
+ ).parsed
@@ -5,6 +5,8 @@ import httpx
5
5
 
6
6
  from ... import errors
7
7
  from ...client import AuthenticatedClient, Client
8
+ from ...models.export_patient_pdf_prefer import ExportPatientPdfPrefer
9
+ from ...models.export_patient_pdf_response_202 import ExportPatientPdfResponse202
8
10
  from ...models.operation_outcome import OperationOutcome
9
11
  from ...types import UNSET, Response, Unset
10
12
 
@@ -12,7 +14,11 @@ from ...types import UNSET, Response, Unset
12
14
  def _get_kwargs(
13
15
  *,
14
16
  connection: str | Unset = UNSET,
17
+ prefer: ExportPatientPdfPrefer | Unset = UNSET,
15
18
  ) -> dict[str, Any]:
19
+ headers: dict[str, Any] = {}
20
+ if not isinstance(prefer, Unset):
21
+ headers["Prefer"] = str(prefer)
16
22
 
17
23
  params: dict[str, Any] = {}
18
24
 
@@ -26,12 +32,18 @@ def _get_kwargs(
26
32
  "params": params,
27
33
  }
28
34
 
35
+ _kwargs["headers"] = headers
29
36
  return _kwargs
30
37
 
31
38
 
32
39
  def _parse_response(
33
40
  *, client: AuthenticatedClient | Client, response: httpx.Response
34
- ) -> OperationOutcome | None:
41
+ ) -> ExportPatientPdfResponse202 | OperationOutcome | None:
42
+ if response.status_code == 202:
43
+ response_202 = ExportPatientPdfResponse202.from_dict(response.json())
44
+
45
+ return response_202
46
+
35
47
  if response.status_code == 401:
36
48
  response_401 = OperationOutcome.from_dict(response.json())
37
49
 
@@ -47,6 +59,16 @@ def _parse_response(
47
59
 
48
60
  return response_404
49
61
 
62
+ if response.status_code == 429:
63
+ response_429 = OperationOutcome.from_dict(response.json())
64
+
65
+ return response_429
66
+
67
+ if response.status_code == 503:
68
+ response_503 = OperationOutcome.from_dict(response.json())
69
+
70
+ return response_503
71
+
50
72
  if client.raise_on_unexpected_status:
51
73
  raise errors.UnexpectedStatus(response.status_code, response.content)
52
74
  else:
@@ -55,7 +77,7 @@ def _parse_response(
55
77
 
56
78
  def _build_response(
57
79
  *, client: AuthenticatedClient | Client, response: httpx.Response
58
- ) -> Response[OperationOutcome]:
80
+ ) -> Response[ExportPatientPdfResponse202 | OperationOutcome]:
59
81
  return Response(
60
82
  status_code=HTTPStatus(response.status_code),
61
83
  content=response.content,
@@ -68,28 +90,35 @@ def sync_detailed(
68
90
  *,
69
91
  client: AuthenticatedClient,
70
92
  connection: str | Unset = UNSET,
71
- ) -> Response[OperationOutcome]:
93
+ prefer: ExportPatientPdfPrefer | Unset = UNSET,
94
+ ) -> Response[ExportPatientPdfResponse202 | OperationOutcome]:
72
95
  """Export records as PDF (one per source)
73
96
 
74
97
  A branded PDF of the patient's records, one per connected source (facility). By default returns a
75
98
  zip with a PDF per source plus a `manifest.json` naming each file and reporting its completeness
76
99
  (complete / partial / failed); pass `connection=<id>` to get that single source's PDF. The
77
100
  `X-Hubble-Sources-Total`, `X-Hubble-Sources-Failed`, and `X-Hubble-Resource-Count` response headers
78
- report how many sources were exported, how many were incomplete, and the total resource count.
101
+ report how many sources were exported, how many were incomplete, and the total resource count. Send
102
+ Prefer: respond-async to prepare the export in a background worker. Poll Content-Location with the
103
+ same consent token no sooner than Retry-After until it returns a 303 redirect to a short-lived
104
+ download URL. An organization can hold 20 exports in flight at once; a repeat request for one
105
+ already running reuses it. Without Prefer, the synchronous response is unchanged.
79
106
 
80
107
  Args:
81
108
  connection (str | Unset):
109
+ prefer (ExportPatientPdfPrefer | Unset):
82
110
 
83
111
  Raises:
84
112
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
85
113
  httpx.TimeoutException: If the request takes longer than Client.timeout.
86
114
 
87
115
  Returns:
88
- Response[OperationOutcome]
116
+ Response[ExportPatientPdfResponse202 | OperationOutcome]
89
117
  """
90
118
 
91
119
  kwargs = _get_kwargs(
92
120
  connection=connection,
121
+ prefer=prefer,
93
122
  )
94
123
 
95
124
  response = client.get_httpx_client().request(
@@ -103,29 +132,36 @@ def sync(
103
132
  *,
104
133
  client: AuthenticatedClient,
105
134
  connection: str | Unset = UNSET,
106
- ) -> OperationOutcome | None:
135
+ prefer: ExportPatientPdfPrefer | Unset = UNSET,
136
+ ) -> ExportPatientPdfResponse202 | OperationOutcome | None:
107
137
  """Export records as PDF (one per source)
108
138
 
109
139
  A branded PDF of the patient's records, one per connected source (facility). By default returns a
110
140
  zip with a PDF per source plus a `manifest.json` naming each file and reporting its completeness
111
141
  (complete / partial / failed); pass `connection=<id>` to get that single source's PDF. The
112
142
  `X-Hubble-Sources-Total`, `X-Hubble-Sources-Failed`, and `X-Hubble-Resource-Count` response headers
113
- report how many sources were exported, how many were incomplete, and the total resource count.
143
+ report how many sources were exported, how many were incomplete, and the total resource count. Send
144
+ Prefer: respond-async to prepare the export in a background worker. Poll Content-Location with the
145
+ same consent token no sooner than Retry-After until it returns a 303 redirect to a short-lived
146
+ download URL. An organization can hold 20 exports in flight at once; a repeat request for one
147
+ already running reuses it. Without Prefer, the synchronous response is unchanged.
114
148
 
115
149
  Args:
116
150
  connection (str | Unset):
151
+ prefer (ExportPatientPdfPrefer | Unset):
117
152
 
118
153
  Raises:
119
154
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
120
155
  httpx.TimeoutException: If the request takes longer than Client.timeout.
121
156
 
122
157
  Returns:
123
- OperationOutcome
158
+ ExportPatientPdfResponse202 | OperationOutcome
124
159
  """
125
160
 
126
161
  return sync_detailed(
127
162
  client=client,
128
163
  connection=connection,
164
+ prefer=prefer,
129
165
  ).parsed
130
166
 
131
167
 
@@ -133,28 +169,35 @@ async def asyncio_detailed(
133
169
  *,
134
170
  client: AuthenticatedClient,
135
171
  connection: str | Unset = UNSET,
136
- ) -> Response[OperationOutcome]:
172
+ prefer: ExportPatientPdfPrefer | Unset = UNSET,
173
+ ) -> Response[ExportPatientPdfResponse202 | OperationOutcome]:
137
174
  """Export records as PDF (one per source)
138
175
 
139
176
  A branded PDF of the patient's records, one per connected source (facility). By default returns a
140
177
  zip with a PDF per source plus a `manifest.json` naming each file and reporting its completeness
141
178
  (complete / partial / failed); pass `connection=<id>` to get that single source's PDF. The
142
179
  `X-Hubble-Sources-Total`, `X-Hubble-Sources-Failed`, and `X-Hubble-Resource-Count` response headers
143
- report how many sources were exported, how many were incomplete, and the total resource count.
180
+ report how many sources were exported, how many were incomplete, and the total resource count. Send
181
+ Prefer: respond-async to prepare the export in a background worker. Poll Content-Location with the
182
+ same consent token no sooner than Retry-After until it returns a 303 redirect to a short-lived
183
+ download URL. An organization can hold 20 exports in flight at once; a repeat request for one
184
+ already running reuses it. Without Prefer, the synchronous response is unchanged.
144
185
 
145
186
  Args:
146
187
  connection (str | Unset):
188
+ prefer (ExportPatientPdfPrefer | Unset):
147
189
 
148
190
  Raises:
149
191
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
150
192
  httpx.TimeoutException: If the request takes longer than Client.timeout.
151
193
 
152
194
  Returns:
153
- Response[OperationOutcome]
195
+ Response[ExportPatientPdfResponse202 | OperationOutcome]
154
196
  """
155
197
 
156
198
  kwargs = _get_kwargs(
157
199
  connection=connection,
200
+ prefer=prefer,
158
201
  )
159
202
 
160
203
  response = await client.get_async_httpx_client().request(**kwargs)
@@ -166,29 +209,36 @@ async def asyncio(
166
209
  *,
167
210
  client: AuthenticatedClient,
168
211
  connection: str | Unset = UNSET,
169
- ) -> OperationOutcome | None:
212
+ prefer: ExportPatientPdfPrefer | Unset = UNSET,
213
+ ) -> ExportPatientPdfResponse202 | OperationOutcome | None:
170
214
  """Export records as PDF (one per source)
171
215
 
172
216
  A branded PDF of the patient's records, one per connected source (facility). By default returns a
173
217
  zip with a PDF per source plus a `manifest.json` naming each file and reporting its completeness
174
218
  (complete / partial / failed); pass `connection=<id>` to get that single source's PDF. The
175
219
  `X-Hubble-Sources-Total`, `X-Hubble-Sources-Failed`, and `X-Hubble-Resource-Count` response headers
176
- report how many sources were exported, how many were incomplete, and the total resource count.
220
+ report how many sources were exported, how many were incomplete, and the total resource count. Send
221
+ Prefer: respond-async to prepare the export in a background worker. Poll Content-Location with the
222
+ same consent token no sooner than Retry-After until it returns a 303 redirect to a short-lived
223
+ download URL. An organization can hold 20 exports in flight at once; a repeat request for one
224
+ already running reuses it. Without Prefer, the synchronous response is unchanged.
177
225
 
178
226
  Args:
179
227
  connection (str | Unset):
228
+ prefer (ExportPatientPdfPrefer | Unset):
180
229
 
181
230
  Raises:
182
231
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
183
232
  httpx.TimeoutException: If the request takes longer than Client.timeout.
184
233
 
185
234
  Returns:
186
- OperationOutcome
235
+ ExportPatientPdfResponse202 | OperationOutcome
187
236
  """
188
237
 
189
238
  return (
190
239
  await asyncio_detailed(
191
240
  client=client,
192
241
  connection=connection,
242
+ prefer=prefer,
193
243
  )
194
244
  ).parsed