hubble-sdk-python 0.2.1__tar.gz → 0.3.1__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 (82) hide show
  1. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/CHANGELOG.md +44 -0
  2. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/PKG-INFO +45 -16
  3. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/README.md +44 -15
  4. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/__init__.py +24 -4
  5. hubble_sdk_python-0.3.1/hubble_sdk/_generated/api/default/create_consent.py +193 -0
  6. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/create_consent_link.py +8 -4
  7. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/export_patient_fhir.py +49 -12
  8. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/export_patient_pdf.py +93 -12
  9. hubble_sdk_python-0.3.1/hubble_sdk/_generated/api/default/get_application_token.py +194 -0
  10. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/get_patient_pdf_export.py +55 -20
  11. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/list_consents.py +16 -4
  12. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/read_fhir.py +70 -13
  13. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/__init__.py +24 -0
  14. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/application_token_error.py +54 -0
  15. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/application_token_error_error.py +31 -0
  16. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/application_token_request.py +72 -0
  17. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/application_token_response.py +69 -0
  18. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/attach_client_authorization_body.py +1 -1
  19. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/client_supplied_discloser.py +107 -0
  20. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/client_supplied_discloser_facility_type.py +28 -0
  21. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/consent.py +5 -4
  22. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/created_consent.py +152 -0
  23. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/export_patient_pdf_combined.py +27 -0
  24. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/export_patient_pdf_hubble_version.py +27 -0
  25. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/facility_address.py +78 -0
  26. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/facility_address_state.py +77 -0
  27. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/operation_outcome.py +1 -3
  28. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/patient_address.py +13 -5
  29. hubble_sdk_python-0.3.1/hubble_sdk/_generated/models/patient_address_state.py +77 -0
  30. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/patient_demographics.py +1 -1
  31. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/provider_seed.py +2 -1
  32. hubble_sdk_python-0.3.1/hubble_sdk/_hubble.py +805 -0
  33. hubble_sdk_python-0.3.1/hubble_sdk/_params.py +86 -0
  34. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_webhooks.py +2 -2
  35. hubble_sdk_python-0.3.1/hubble_sdk/py.typed +0 -0
  36. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/pydoc-markdown.yml +4 -3
  37. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/pyproject.toml +1 -1
  38. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/tests/test_hubble.py +424 -9
  39. hubble_sdk_python-0.3.1/tests/test_params.py +68 -0
  40. hubble_sdk_python-0.2.1/hubble_sdk/_hubble.py +0 -472
  41. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/.gitignore +0 -0
  42. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/LICENSE +0 -0
  43. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/config.yaml +0 -0
  44. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/__init__.py +0 -0
  45. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/__init__.py +0 -0
  46. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/__init__.py +0 -0
  47. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/attach_client_authorization.py +0 -0
  48. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/exchange_token.py +0 -0
  49. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/get_consent.py +0 -0
  50. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/api/default/revoke_consent.py +0 -0
  51. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/client.py +0 -0
  52. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/errors.py +0 -0
  53. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/api_error.py +0 -0
  54. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/consent_error.py +0 -0
  55. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/consent_link_request.py +0 -0
  56. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/consent_list.py +0 -0
  57. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/consent_source.py +0 -0
  58. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/consent_source_status.py +0 -0
  59. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/consent_source_type.py +0 -0
  60. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/consent_status.py +0 -0
  61. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/device_authorization_request.py +0 -0
  62. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/device_authorization_response.py +0 -0
  63. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/export_patient_fhir_prefer.py +0 -0
  64. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/export_patient_fhir_response_202.py +0 -0
  65. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/export_patient_fhir_response_202_status.py +0 -0
  66. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/export_patient_pdf_prefer.py +0 -0
  67. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/export_patient_pdf_response_202.py +0 -0
  68. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/export_patient_pdf_response_202_status.py +0 -0
  69. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/fhir_resource.py +0 -0
  70. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/get_patient_pdf_export_download.py +0 -0
  71. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/get_patient_pdf_export_response_200.py +0 -0
  72. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/get_patient_pdf_export_response_200_status.py +0 -0
  73. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/o_auth_error.py +0 -0
  74. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/o_auth_error_error.py +0 -0
  75. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/operation_outcome_issue_item.py +0 -0
  76. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/token_request.py +0 -0
  77. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/token_request_grant_type.py +0 -0
  78. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/models/token_response.py +0 -0
  79. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/hubble_sdk/_generated/types.py +0 -0
  80. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/templates/property_templates/enum_property.py.jinja +0 -0
  81. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/templates/str_enum.py.jinja +0 -0
  82. {hubble_sdk_python-0.2.1 → hubble_sdk_python-0.3.1}/tests/test_webhooks.py +0 -0
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.1](https://github.com/hubble-dot-ai/hubble-sdks/compare/hubble-sdk-python-v0.3.0...hubble-sdk-python-v0.3.1) (2026-11-05)
4
+
5
+
6
+ ### Features
7
+
8
+ * Support `Hubble-Version` through the `version` option on `export_pdf`, including waiting for background generation and downloading the completed records archive. ([#82](https://github.com/hubble-dot-ai/hubble-sdks/issues/82)) ([8f295db](https://github.com/hubble-dot-ai/hubble-sdks/commit/8f295db3f67c8606a29db0f7dfd99752e963c99e))
9
+ * Request a single PDF containing a source's summary and documents with `combined=true` in the generated client. ([#84](https://github.com/hubble-dot-ai/hubble-sdks/issues/84)) ([3a70d41](https://github.com/hubble-dot-ai/hubble-sdks/commit/3a70d417c9cc27700679b3e957a748dd251fcd71))
10
+ * Limit FHIR reads and exports to one connected source with the generated client's `connection` parameter. ([#81](https://github.com/hubble-dot-ai/hubble-sdks/issues/81)) ([0f812d0](https://github.com/hubble-dot-ai/hubble-sdks/commit/0f812d06ac59e23285fd72238529eb21afcbc80a))
11
+
12
+
13
+ ### Documentation
14
+
15
+ * Document records archives and 30-day export availability after completion, with download links valid for at most 60 seconds. ([#81](https://github.com/hubble-dot-ai/hubble-sdks/issues/81)) ([0f812d0](https://github.com/hubble-dot-ai/hubble-sdks/commit/0f812d06ac59e23285fd72238529eb21afcbc80a))
16
+ * Clarify that `consent.completed` means the patient finished adding sources; their records may still be processing. ([#78](https://github.com/hubble-dot-ai/hubble-sdks/issues/78)) ([b16e458](https://github.com/hubble-dot-ai/hubble-sdks/commit/b16e45869dd7a5f531318e58c182022b12e3139b))
17
+
18
+ ## [0.3.0](https://github.com/hubble-dot-ai/hubble-sdks/compare/hubble-sdk-python-v0.2.1...hubble-sdk-python-v0.3.0) (2026-10-27)
19
+
20
+
21
+ ### ⚠ BREAKING CHANGES
22
+
23
+ * type the consent status filter as a literal ([#70](https://github.com/hubble-dot-ai/hubble-sdks/issues/70))
24
+ * each discloser needs an address (line1, city, state, postal_code) next to its name. The API now refuses a name-only discloser with a 400, and the TypeScript type no longer accepts one.
25
+
26
+ ### Features
27
+
28
+ * create consents without the device flow ([#66](https://github.com/hubble-dot-ai/hubble-sdks/issues/66)) ([f82bed9](https://github.com/hubble-dot-ai/hubble-sdks/commit/f82bed9d31192144cd592c25890639b1576bf2bd))
29
+ * read consents with an application access token ([#65](https://github.com/hubble-dot-ai/hubble-sdks/issues/65)) ([66f9703](https://github.com/hubble-dot-ai/hubble-sdks/commit/66f97031f9eef15bc39c2e5a8f6059c11c4bbf93))
30
+ * send facility details with each discloser when attaching an authorization ([#69](https://github.com/hubble-dot-ai/hubble-sdks/issues/69)) ([6d49b68](https://github.com/hubble-dot-ai/hubble-sdks/commit/6d49b68b49768efb5715cc7a3379ee0123ddd466))
31
+ * ship a py.typed marker so type checkers read the SDK's types ([#72](https://github.com/hubble-dot-ai/hubble-sdks/issues/72)) ([bf1f4b0](https://github.com/hubble-dot-ai/hubble-sdks/commit/bf1f4b0a0ecadc17d89e0eb477cbda98f9648aec))
32
+ * sync the spec for application tokens and POST /consents ([#64](https://github.com/hubble-dot-ai/hubble-sdks/issues/64)) ([8981d57](https://github.com/hubble-dot-ai/hubble-sdks/commit/8981d57c7dee236a1668effc477adbd77ef38ba3))
33
+ * type the consent status filter as a literal ([#70](https://github.com/hubble-dot-ai/hubble-sdks/issues/70)) ([fb7fd4b](https://github.com/hubble-dot-ai/hubble-sdks/commit/fb7fd4b26ba0e05874a01a307ca8e16a02000979))
34
+
35
+
36
+ ### Bug Fixes
37
+
38
+ * harden application-token reads (dot ids, relative next links, shared token) ([#68](https://github.com/hubble-dot-ai/hubble-sdks/issues/68)) ([0d9d41e](https://github.com/hubble-dot-ai/hubble-sdks/commit/0d9d41e85f5d45f98a7cc08d7efb1ede6a1aabbd))
39
+ * send create_consent's body as given so a misspelt field is refused ([#73](https://github.com/hubble-dot-ai/hubble-sdks/issues/73)) ([559cf0f](https://github.com/hubble-dot-ai/hubble-sdks/commit/559cf0f7471f277f77d874f4454d5202d3a16e62))
40
+ * sync the consent status and error descriptions from the portal ([#62](https://github.com/hubble-dot-ai/hubble-sdks/issues/62)) ([2bd1606](https://github.com/hubble-dot-ai/hubble-sdks/commit/2bd160636fa5ccd8d5f324d0d69f29ab19ff57fc))
41
+
42
+
43
+ ### Documentation
44
+
45
+ * keep the disclosers description in one argument entry ([#74](https://github.com/hubble-dot-ai/hubble-sdks/issues/74)) ([53e5163](https://github.com/hubble-dot-ai/hubble-sdks/commit/53e51634c5a29c0b3bd96dab06b8965f7b6cfdda))
46
+
3
47
  ## [0.2.1](https://github.com/hubble-dot-ai/hubble-sdks/compare/hubble-sdk-python-v0.2.0...hubble-sdk-python-v0.2.1) (2026-10-14)
4
48
 
5
49
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hubble-sdk-python
3
- Version: 0.2.1
3
+ Version: 0.3.1
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
@@ -32,8 +32,8 @@ Description-Content-Type: text/markdown
32
32
 
33
33
  # hubble-sdk-python
34
34
 
35
- Python client for the Hubble Patient Access API: create patient authorization
36
- links, exchange them for consent-scoped access tokens, and read FHIR R4 data.
35
+ Python client for the Hubble Patient Access API: create consents and patient
36
+ links, and read FHIR R4 data with your application's access token.
37
37
 
38
38
  ```bash
39
39
  pip install hubble-sdk-python
@@ -44,24 +44,53 @@ from hubble_sdk import Hubble
44
44
 
45
45
  hub = Hubble(client_id="hbl_client_prod_...", client_secret="hbl_prod_...")
46
46
 
47
- # 1. Create a patient link and send the patient to it. Optionally prefill with
47
+ # 1. Create a consent and send the patient its link. Optionally prefill with
48
48
  # patient={"first_name": ...} demographics, providers=[{"name": ...}], and
49
49
  # the optional organization your verified Hubble organization represents.
50
- link = hub.create_link(patient_ref="abc123", on_behalf_of_organization_name="Acme Health")
51
- print(link.verification_uri_complete)
52
-
53
- # 2. Wait for the patient to authorize (polls the token endpoint for you).
54
- session = hub.wait_for_authorization(link)
50
+ consent = hub.create_consent(patient_ref="abc123", on_behalf_of_organization_name="Acme Health")
51
+ print(consent.link_url) # returned only here; store it if you may resend it
55
52
 
56
- # 3. Read the patient's FHIR data (Bearer token + auto-refresh handled for you).
57
- bundle = session.everything()
58
- observations = session.fhir("Observation", params={"category": "vital-signs"})
59
- export = session.export_pdf() # export.content bytes; export.sources_failed counts failed sources
53
+ # 2. Read the patient's FHIR data whenever you like (see below for every read).
54
+ summary = hub.summary(consent.id)
55
+ labs = hub.search(consent.id, "Observation", {"category": "laboratory"})
60
56
 
61
57
  # Consent management
62
- for consent in hub.list_consents(status="active"):
63
- print(consent.id, consent.patient_ref)
64
- hub.revoke_consent(session.consent_id)
58
+ for c in hub.list_consents(status="active"):
59
+ print(c.id, c.patient_ref)
60
+ hub.revoke_consent(consent.id)
61
+ ```
62
+
63
+ ## Reading with an application token
64
+
65
+ Read any consent in your environment by its id, without the device flow: while
66
+ the patient is still adding sources, after they finish, or after the link
67
+ expires. The client gets the application access token from `POST /api/v1/token`,
68
+ caches it and renews it for you, and names the consent on every request. One
69
+ `Hubble` client is safe to share across threads: they share the one token, and
70
+ only one thread fetches or renews it at a time.
71
+
72
+ ```python
73
+ obs = hub.read(consent_id, "Observation", "abc-123") # one resource
74
+ page = hub.search(consent_id, "Observation", {"category": "laboratory"}) # first page (a Bundle)
75
+ for resource in hub.iter_search(consent_id, "Observation", {"category": "laboratory"}):
76
+ ... # every page, lazily
77
+ summary = hub.summary(consent_id)
78
+ record = hub.everything(consent_id, {"_count": 100})
79
+ export = hub.export_pdf(consent_id, connection=connection_id) # one source's PDF
80
+ ```
81
+
82
+ Reuse one `Hubble` client so its token is reused too.
83
+
84
+ ### The device flow
85
+
86
+ Existing integrations can keep using the device flow, which waits for the
87
+ patient to finish and reads with a consent-scoped token:
88
+
89
+ ```python
90
+ link = hub.create_link(patient_ref="abc123")
91
+ print(link.verification_uri_complete)
92
+ session = hub.wait_for_authorization(link) # polls until the patient finishes
93
+ bundle = session.everything() # refreshes the consent token for you
65
94
  ```
66
95
 
67
96
  ## Receiving webhooks
@@ -1,7 +1,7 @@
1
1
  # hubble-sdk-python
2
2
 
3
- Python client for the Hubble Patient Access API: create patient authorization
4
- links, exchange them for consent-scoped access tokens, and read FHIR R4 data.
3
+ Python client for the Hubble Patient Access API: create consents and patient
4
+ links, and read FHIR R4 data with your application's access token.
5
5
 
6
6
  ```bash
7
7
  pip install hubble-sdk-python
@@ -12,24 +12,53 @@ from hubble_sdk import Hubble
12
12
 
13
13
  hub = Hubble(client_id="hbl_client_prod_...", client_secret="hbl_prod_...")
14
14
 
15
- # 1. Create a patient link and send the patient to it. Optionally prefill with
15
+ # 1. Create a consent and send the patient its link. Optionally prefill with
16
16
  # patient={"first_name": ...} demographics, providers=[{"name": ...}], and
17
17
  # the optional organization your verified Hubble organization represents.
18
- link = hub.create_link(patient_ref="abc123", on_behalf_of_organization_name="Acme Health")
19
- print(link.verification_uri_complete)
20
-
21
- # 2. Wait for the patient to authorize (polls the token endpoint for you).
22
- session = hub.wait_for_authorization(link)
18
+ consent = hub.create_consent(patient_ref="abc123", on_behalf_of_organization_name="Acme Health")
19
+ print(consent.link_url) # returned only here; store it if you may resend it
23
20
 
24
- # 3. Read the patient's FHIR data (Bearer token + auto-refresh handled for you).
25
- bundle = session.everything()
26
- observations = session.fhir("Observation", params={"category": "vital-signs"})
27
- export = session.export_pdf() # export.content bytes; export.sources_failed counts failed sources
21
+ # 2. Read the patient's FHIR data whenever you like (see below for every read).
22
+ summary = hub.summary(consent.id)
23
+ labs = hub.search(consent.id, "Observation", {"category": "laboratory"})
28
24
 
29
25
  # Consent management
30
- for consent in hub.list_consents(status="active"):
31
- print(consent.id, consent.patient_ref)
32
- hub.revoke_consent(session.consent_id)
26
+ for c in hub.list_consents(status="active"):
27
+ print(c.id, c.patient_ref)
28
+ hub.revoke_consent(consent.id)
29
+ ```
30
+
31
+ ## Reading with an application token
32
+
33
+ Read any consent in your environment by its id, without the device flow: while
34
+ the patient is still adding sources, after they finish, or after the link
35
+ expires. The client gets the application access token from `POST /api/v1/token`,
36
+ caches it and renews it for you, and names the consent on every request. One
37
+ `Hubble` client is safe to share across threads: they share the one token, and
38
+ only one thread fetches or renews it at a time.
39
+
40
+ ```python
41
+ obs = hub.read(consent_id, "Observation", "abc-123") # one resource
42
+ page = hub.search(consent_id, "Observation", {"category": "laboratory"}) # first page (a Bundle)
43
+ for resource in hub.iter_search(consent_id, "Observation", {"category": "laboratory"}):
44
+ ... # every page, lazily
45
+ summary = hub.summary(consent_id)
46
+ record = hub.everything(consent_id, {"_count": 100})
47
+ export = hub.export_pdf(consent_id, connection=connection_id) # one source's PDF
48
+ ```
49
+
50
+ Reuse one `Hubble` client so its token is reused too.
51
+
52
+ ### The device flow
53
+
54
+ Existing integrations can keep using the device flow, which waits for the
55
+ patient to finish and reads with a consent-scoped token:
56
+
57
+ ```python
58
+ link = hub.create_link(patient_ref="abc123")
59
+ print(link.verification_uri_complete)
60
+ session = hub.wait_for_authorization(link) # polls until the patient finishes
61
+ bundle = session.everything() # refreshes the consent token for you
33
62
  ```
34
63
 
35
64
  ## Receiving webhooks
@@ -5,10 +5,12 @@ Import the high-level client from here:
5
5
  from hubble_sdk import Hubble
6
6
 
7
7
  hub = Hubble(client_id="hbl_client_...", client_secret="hbl_prod_...")
8
- link = hub.create_link(patient_ref="abc123")
9
- print(link.verification_uri_complete) # send the patient here
10
- session = hub.wait_for_authorization(link)
11
- bundle = session.everything() # reads FHIR with the consent token
8
+ consent = hub.create_consent(patient_ref="abc123")
9
+ print(consent.link_url) # send the patient here
10
+ bundle = hub.everything(consent.id) # read whenever you like
11
+
12
+ The device flow (``create_link`` / ``wait_for_authorization``) still works for
13
+ existing integrations.
12
14
 
13
15
  The generated low-level client lives under ``hubble_sdk._generated``
14
16
  if you need direct access to raw operations and models.
@@ -17,6 +19,7 @@ if you need direct access to raw operations and models.
17
19
  from ._generated.models import (
18
20
  Consent,
19
21
  ConsentStatus,
22
+ CreatedConsent,
20
23
  DeviceAuthorizationResponse,
21
24
  TokenResponse,
22
25
  )
@@ -28,6 +31,15 @@ from ._hubble import (
28
31
  PdfExport,
29
32
  TokenSet,
30
33
  )
34
+ from ._params import (
35
+ ConsentStatusFilter,
36
+ DiscloserParams,
37
+ FacilityAddressParams,
38
+ PatientAddressParams,
39
+ PatientParams,
40
+ ProviderSeedParams,
41
+ UsStateCode,
42
+ )
31
43
  from ._webhooks import (
32
44
  WEBHOOK_SIGNATURE_HEADER,
33
45
  WebhookEvent,
@@ -43,6 +55,13 @@ __all__ = [
43
55
  "ConsentTimeout",
44
56
  "TokenSet",
45
57
  "PdfExport",
58
+ "ConsentStatusFilter",
59
+ "PatientParams",
60
+ "PatientAddressParams",
61
+ "ProviderSeedParams",
62
+ "DiscloserParams",
63
+ "FacilityAddressParams",
64
+ "UsStateCode",
46
65
  "construct_webhook_event",
47
66
  "WebhookEvent",
48
67
  "WebhookEventSource",
@@ -50,6 +69,7 @@ __all__ = [
50
69
  "WEBHOOK_SIGNATURE_HEADER",
51
70
  "Consent",
52
71
  "ConsentStatus",
72
+ "CreatedConsent",
53
73
  "DeviceAuthorizationResponse",
54
74
  "TokenResponse",
55
75
  ]
@@ -0,0 +1,193 @@
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.api_error import ApiError
9
+ from ...models.consent_link_request import ConsentLinkRequest
10
+ from ...models.created_consent import CreatedConsent
11
+ from ...types import UNSET, Response, Unset
12
+
13
+
14
+ def _get_kwargs(
15
+ *,
16
+ body: ConsentLinkRequest | Unset = UNSET,
17
+ ) -> dict[str, Any]:
18
+ headers: dict[str, Any] = {}
19
+
20
+ _kwargs: dict[str, Any] = {
21
+ "method": "post",
22
+ "url": "/api/v1/consents",
23
+ }
24
+
25
+ if not isinstance(body, Unset):
26
+ _kwargs["json"] = body.to_dict()
27
+
28
+ headers["Content-Type"] = "application/json"
29
+
30
+ _kwargs["headers"] = headers
31
+ return _kwargs
32
+
33
+
34
+ def _parse_response(
35
+ *, client: AuthenticatedClient | Client, response: httpx.Response
36
+ ) -> ApiError | CreatedConsent | None:
37
+ if response.status_code == 201:
38
+ response_201 = CreatedConsent.from_dict(response.json())
39
+
40
+ return response_201
41
+
42
+ if response.status_code == 400:
43
+ response_400 = ApiError.from_dict(response.json())
44
+
45
+ return response_400
46
+
47
+ if response.status_code == 401:
48
+ response_401 = ApiError.from_dict(response.json())
49
+
50
+ return response_401
51
+
52
+ if response.status_code == 403:
53
+ response_403 = ApiError.from_dict(response.json())
54
+
55
+ return response_403
56
+
57
+ if client.raise_on_unexpected_status:
58
+ raise errors.UnexpectedStatus(response.status_code, response.content)
59
+ else:
60
+ return None
61
+
62
+
63
+ def _build_response(
64
+ *, client: AuthenticatedClient | Client, response: httpx.Response
65
+ ) -> Response[ApiError | CreatedConsent]:
66
+ return Response(
67
+ status_code=HTTPStatus(response.status_code),
68
+ content=response.content,
69
+ headers=response.headers,
70
+ parsed=_parse_response(client=client, response=response),
71
+ )
72
+
73
+
74
+ def sync_detailed(
75
+ *,
76
+ client: AuthenticatedClient,
77
+ body: ConsentLinkRequest | Unset = UNSET,
78
+ ) -> Response[ApiError | CreatedConsent]:
79
+ """Create a consent
80
+
81
+ Creates a pending consent and its patient link, and returns the consent with `link_url`. Deliver the
82
+ link to the patient, then read the consent with an application access token from POST /api/v1/token
83
+ and `consent=<id>` on /api/v1/fhir. Every field is optional.
84
+
85
+ Args:
86
+ body (ConsentLinkRequest | Unset):
87
+
88
+ Raises:
89
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
90
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
91
+
92
+ Returns:
93
+ Response[ApiError | CreatedConsent]
94
+ """
95
+
96
+ kwargs = _get_kwargs(
97
+ body=body,
98
+ )
99
+
100
+ response = client.get_httpx_client().request(
101
+ **kwargs,
102
+ )
103
+
104
+ return _build_response(client=client, response=response)
105
+
106
+
107
+ def sync(
108
+ *,
109
+ client: AuthenticatedClient,
110
+ body: ConsentLinkRequest | Unset = UNSET,
111
+ ) -> ApiError | CreatedConsent | None:
112
+ """Create a consent
113
+
114
+ Creates a pending consent and its patient link, and returns the consent with `link_url`. Deliver the
115
+ link to the patient, then read the consent with an application access token from POST /api/v1/token
116
+ and `consent=<id>` on /api/v1/fhir. Every field is optional.
117
+
118
+ Args:
119
+ body (ConsentLinkRequest | Unset):
120
+
121
+ Raises:
122
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
123
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
124
+
125
+ Returns:
126
+ ApiError | CreatedConsent
127
+ """
128
+
129
+ return sync_detailed(
130
+ client=client,
131
+ body=body,
132
+ ).parsed
133
+
134
+
135
+ async def asyncio_detailed(
136
+ *,
137
+ client: AuthenticatedClient,
138
+ body: ConsentLinkRequest | Unset = UNSET,
139
+ ) -> Response[ApiError | CreatedConsent]:
140
+ """Create a consent
141
+
142
+ Creates a pending consent and its patient link, and returns the consent with `link_url`. Deliver the
143
+ link to the patient, then read the consent with an application access token from POST /api/v1/token
144
+ and `consent=<id>` on /api/v1/fhir. Every field is optional.
145
+
146
+ Args:
147
+ body (ConsentLinkRequest | Unset):
148
+
149
+ Raises:
150
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
151
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
152
+
153
+ Returns:
154
+ Response[ApiError | CreatedConsent]
155
+ """
156
+
157
+ kwargs = _get_kwargs(
158
+ body=body,
159
+ )
160
+
161
+ response = await client.get_async_httpx_client().request(**kwargs)
162
+
163
+ return _build_response(client=client, response=response)
164
+
165
+
166
+ async def asyncio(
167
+ *,
168
+ client: AuthenticatedClient,
169
+ body: ConsentLinkRequest | Unset = UNSET,
170
+ ) -> ApiError | CreatedConsent | None:
171
+ """Create a consent
172
+
173
+ Creates a pending consent and its patient link, and returns the consent with `link_url`. Deliver the
174
+ link to the patient, then read the consent with an application access token from POST /api/v1/token
175
+ and `consent=<id>` on /api/v1/fhir. Every field is optional.
176
+
177
+ Args:
178
+ body (ConsentLinkRequest | Unset):
179
+
180
+ Raises:
181
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
182
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
183
+
184
+ Returns:
185
+ ApiError | CreatedConsent
186
+ """
187
+
188
+ return (
189
+ await asyncio_detailed(
190
+ client=client,
191
+ body=body,
192
+ )
193
+ ).parsed
@@ -77,7 +77,8 @@ def sync_detailed(
77
77
  Mints a pending consent and a one-time patient link. Send the patient to
78
78
  `verification_uri_complete`, then poll the token endpoint with `device_code` until they authorize.
79
79
  Accepts a nested JSON body (recommended) or the flat RFC 8628 form encoding; both prefill the same
80
- consent.
80
+ consent. To read with an application access token instead, create the consent with POST
81
+ /api/v1/consents, which has no device code.
81
82
 
82
83
  Args:
83
84
  body (ConsentLinkRequest | Unset):
@@ -112,7 +113,8 @@ def sync(
112
113
  Mints a pending consent and a one-time patient link. Send the patient to
113
114
  `verification_uri_complete`, then poll the token endpoint with `device_code` until they authorize.
114
115
  Accepts a nested JSON body (recommended) or the flat RFC 8628 form encoding; both prefill the same
115
- consent.
116
+ consent. To read with an application access token instead, create the consent with POST
117
+ /api/v1/consents, which has no device code.
116
118
 
117
119
  Args:
118
120
  body (ConsentLinkRequest | Unset):
@@ -142,7 +144,8 @@ async def asyncio_detailed(
142
144
  Mints a pending consent and a one-time patient link. Send the patient to
143
145
  `verification_uri_complete`, then poll the token endpoint with `device_code` until they authorize.
144
146
  Accepts a nested JSON body (recommended) or the flat RFC 8628 form encoding; both prefill the same
145
- consent.
147
+ consent. To read with an application access token instead, create the consent with POST
148
+ /api/v1/consents, which has no device code.
146
149
 
147
150
  Args:
148
151
  body (ConsentLinkRequest | Unset):
@@ -175,7 +178,8 @@ async def asyncio(
175
178
  Mints a pending consent and a one-time patient link. Send the patient to
176
179
  `verification_uri_complete`, then poll the token endpoint with `device_code` until they authorize.
177
180
  Accepts a nested JSON body (recommended) or the flat RFC 8628 form encoding; both prefill the same
178
- consent.
181
+ consent. To read with an application access token instead, create the consent with POST
182
+ /api/v1/consents, which has no device code.
179
183
 
180
184
  Args:
181
185
  body (ConsentLinkRequest | Unset):