CareAtlas Integration Guide
Overview
This guide describes how to implement order submission using only the HaaS APIs, with no dependency on the provider-portal UI. It is intended for developers building headless or custom integrations (e.g. external provider portals, scripts, or server-to-server flows) that need to create orders in the same way as this app.
API contract: HTTP paths below match [public/specs/CareAtlas-Unified-API-oas3-v0.1.json](../public/specs/CareAtlas-Unified-API-oas3-v0.1.json) (CareAtlas Unified API). Combine them with the API base URL (e.g. staging https://qa.api.thecareatlas.com). Hub webhook routes are documented in §8 Webhooks (Hub API). To re-check that guide paths still exist in that spec, run python3 scripts/verify-careatlas-guide-paths.py from the repo root.
Goal: From a provider/tenant context, get to a successfully submitted order (prescription-based flow.
1. 1. Getting started
1.1 High-level flow (registration → screening → consult → prescription → order)
The app’s provider-portal flow is:
- Tenant & auth – Resolve tenant ID and partner app ID (Fetch Tenant ID and Partner App ID); ensure all requests use the same tenant and Bearer token.
- Patient – Ensure the patient exists and has
account.id(forcustomer.accountId) and a valid address (required for order import). - Clinic – Ensure the clinic exists (or create/link) and you have clinic details (name, address, email, phone) required for
OrderImportRequest.clinic(ClinicInfo). - Practitioner – Ensure the practitioner exists and you have
practitionerIdfor consults, prescriptions, and order context as needed. Note: Practitioner registration requires a valid NPI verifiable from NPPES NPI Registry. - Product / variant – Resolve
productVariantIdfrom the catalog if not already on the prescription. Product Variant is the actual medication that will be ordered. - Screening – Complete the questionnaire session and obtain screeningSessionId (§4 Patient Screening).
- Consult (when your tenant requires it) – Create and drive a consult tied to that screening session, patient, practitioner, and product (§5 Consult). Skip if you prescribe directly after screening.
- Prescription – Create (or find) a prescription with
productIdandproductVariantIdvia POST /clinical/v1/prescriptions/create, or use GET /clinical/v1/prescriptions/search for an existing record (§6 Step 2). - Order import – Build
OrderImportRequestand callPOST /commerce/v1/orders/import(§7 Creating Orders).
For outbound webhooks (register your HTTPS URL, signing secret, test and retry deliveries), see §8 Webhooks (Hub API). For a compact endpoint index across services, see §9 API Reference.
2. 2. Access & Authentication
- API base URL –
Staging:
https://qa.api.thecareatlas.comProduction:https://api.thecareatlas.com - Authentication – Once you receive your App Client credentials from the CareAtlas team, you can request auth token from AWS Cognito:
https://qa.app-auth.thecareatlas.comusing the credentials. You need a valid Bearer access token with the following scopes per API:- Identity (
/identity/v1/*on the API base URL) –api://haas.identity/haas.api.read,api://haas.identity/haas.api.write - Catalog (
/catalog/v1/*) –api://haas.catalog/haas.api.read,api://haas.catalog/haas.api.write - Clinical (
/clinical/v1/*) –api://haas.clinical/haas.api.read,api://haas.clinical/haas.api.write - Commerce (
/commerce/v1/*) –api://haas.commerce/haas.api.read,api://haas.commerce/haas.api.write - Screening (
/screening/v1/*) –api://haas.screening/haas.api.read,api://haas.screening/haas.api.write(as required by each operation in the spec) - Consult (
/consult/v1/*) –api://haas.consult/haas.api.read,api://haas.consult/haas.api.write(as required by each operation in the spec; see §5 Consult) - Hub (
/hub/v1/*) –api://haas.hub/haas.api.read,api://haas.hub/haas.api.writefor webhook subscriptions, deliveries, and related operations (see §8).
- Identity (
- Tenant context – X-Tenant-Id header is required on Hub subscription and delivery calls (and most other tenant-scoped routes). You obtain it from
GET /identity/v1/tenants/me(see Fetch Tenant ID and Partner App ID).GET /hub/v1/webhooks/event-types/searchhas no tenant header in the bundled OpenAPI—confirm with your deployment if the gateway differs.
Fetch Tenant ID and Partner App ID
Endpoint: GET /identity/v1/tenants/me (getMyTenants)
Headers: Authorization: Bearer <access_token>
Response type: MyTenantsResponse — partnerApp may be null when the caller has no linked partner application.
What to use:
| Need | Field |
|---|---|
| X-Tenant-Id on tenant-scoped routes | partnerApp.tenants[].tenantId for the clinic/tenant your user or app is bound to (often the first row, or match by name/role in multi-tenant apps). |
| source.partnerAppId on order import | partnerApp.id |
| Partner identity | partnerApp.partner (MyTenantsPartnerResponse) — id, name, role |
| Linked pharmacy tenants | partnerApp.tenantProviders — same shape as tenants (MyTenantsTenantResponse); use role (e.g. Pharmacy) and tenantId when resolving pharmacyId for prescriptions or order display. |
Sample response:
{
"partnerApp": {
"id": "01933a7e-5f2a-7000-8000-000000000001",
"appId": "provider-portal",
"name": "Provider Portal",
"partner": {
"id": "01933a7e-5f2a-7000-8000-000000000002",
"name": "Acme Health",
"role": "partner"
},
"tenants": [
{
"tenantId": "01933a7e-6a1b-7123-8000-000000000003",
"name": "Acme Clinic",
"role": "provider",
"attributes": []
}
],
"tenantProviders": [
{
"tenantId": "0193ace2-7048-7692-9f57-91e59b94b40a",
"name": "Example Pharmacy",
"role": "Pharmacy",
"attributes": []
}
],
"attributes": []
}
}
3. 3. Patient Registration
Prerequisites — registration
Complete §2 Access & Authentication — Bearer token, Identity read/write scopes, and X-Tenant-Id from Fetch Tenant ID and Partner App ID.
Register or resolve the patient in Identity so you have a patientId (UUID) for screening (POST /screening/v1/sessions/create), prescriptions, and orders.
Endpoint: POST /identity/v1/patients/resolve-by-email
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>, Content-Type: application/json
The API resolves an existing patient by email in the tenant or creates one when none exists. Use patient.id from the response as patientId in later steps.
Request body: PatientResolveRequest
| Field | Type | Notes |
|---|---|---|
email | string | Patient email (lookup key). |
firstName, lastName | string | Legal name. |
dateOfBirth | string (date) | ISO date, e.g. 1990-05-15. |
gender | string | "Male" or "Female" — plain text. |
height, weight | object | Height and weight payload per API contract (shape may be numeric or structured). |
isOnGlp | boolean | Whether the patient is on GLP therapy. |
stateCode | string | US state or region code. |
address | Address | Full Address — addressLine1, city, state, zipCode, phone; optional label (e.g. Shipping Address). |
partnerPatientKey | string | Stable external key (often same as email). |
phoneNumber | string | Contact phone. |
stageId | string (uuid) | Funnel/stage id when your tenant requires it. |
referrerId | string | Referrer id when your tenant requires it. |
externalId | string | External/reference identifier from your system (often a generated UUID). |
The sample below sets gender to "Female" (use "Male" for male patients).
Sample request (POST /identity/v1/patients/resolve-by-email):
{
"email": "jane.doe@example.com",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1990-05-15",
"gender": "Female",
"height": {},
"weight": {},
"isOnGlp": false,
"stateCode": "CA",
"address": {
"addressLine1": "123 Main St",
"addressLine2": "Apt 4",
"city": "San Francisco",
"state": "CA",
"zipCode": "94102",
"phone": "5551234567",
"label": "Shipping Address"
},
"partnerPatientKey": "jane.doe@example.com",
"phoneNumber": "5551234567",
"stageId": "01933a7e-5f2a-7000-8000-000000000020",
"referrerId": "01933a7e-5f2a-7000-8000-000000000021",
"externalId": "01933a7e-5f2a-7000-8000-000000000022"
}
Response: PatientResolveResponse — the nested patient object is a PatientResponse.
| Field | Type | Notes |
|---|---|---|
found | boolean | true if a patient already existed for this email. |
created | boolean | true if a new patient was created. |
patient | PatientResponse | Full patient record; use patient.id as patientId. |
Sample response:
{
"found": false,
"created": true,
"patient": {
"id": "01933a7e-7b2c-7456-8000-000000000010",
"tenantId": "01933a7e-6a1b-7123-8000-000000000003",
"accountId": "01933a7e-8c3d-7567-8000-000000000011",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1990-05-15",
"height": {},
"weight": {},
"isOnGlp": false,
"phone": "5551234567",
"gender": "Female",
"state": "CA",
"stateName": "California",
"smsVerified": false,
"email": "jane.doe@example.com",
"emailVerified": true,
"phoneNumber": "5551234567",
"phoneNumberVerified": false,
"etag": "W/\"abc123\"",
"address": {
"addressLine1": "123 Main St",
"addressLine2": "Apt 4",
"city": "San Francisco",
"state": "CA",
"zipCode": "94102",
"phone": "5551234567"
}
}
}
Lookup by id or email (GET /identity/v1/patients/search)
When you need to find or verify a patient by id, email, or accountId (instead of or after resolve-by-email) above, use search.
Endpoint: GET /identity/v1/patients/search
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>
Query parameters: Pass the patient’s id (and/or email, accountId, etc. per spec) to retrieve a paged list. When filtering by unique id, use the matching row from data.
Response: PagedResponseOfPatientResponse — each element in data is a PatientResponse.
For order import you need:
- patient.id →
customer.patientId - patient.account.id →
customer.accountId(required; resolve from profile/session if not on patient in your flow) - patient.address → must be a full Address object with at least:
addressLine1,addressLine2,city,state,zipCode,phone
If the patient has no address or no account.id, create or update the patient via the appropriate Identity APIs before calling order import.
Sample response (GET /identity/v1/patients/search?id=01933a7e-7b2c-7456-8000-000000000010&limit=1): Example shape for data[0]:
{
"data": [
{
"id": "01933a7e-7b2c-7456-8000-000000000010",
"account": {
"id": "01933a7e-8c3d-7567-8000-000000000011",
"username": "jane.doe@example.com",
"displayName": "Jane Doe",
"etag": "W/\"acct-etag\""
},
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1990-05-15",
"height": 65,
"weight": 140,
"isOnGlp": false,
"gender": "Female",
"state": "CA",
"stateName": "California",
"smsVerified": false,
"email": "jane.doe@example.com",
"emailVerified": true,
"phoneNumber": "5551234567",
"phoneNumberVerified": false,
"etag": "W/\"abc123\"",
"address": {
"addressLine1": "123 Main St",
"addressLine2": "Apt 4",
"city": "San Francisco",
"state": "CA",
"zipCode": "94102",
"phone": "5551234567"
}
}
],
"metadata": {
"limit": {},
"nextCursor": "",
"hasMore": false
}
}
4. 4. Patient Screening
Before prescribing, many flows require the patient to complete a screening questionnaire tied to a medication (catalog product + variant). A typical headless sequence matches the provider portal: choose the product/variant and questionnaire, then start a screening session. Subsequent calls (answer questions, consent, submit) use the session id returned here.
Prerequisites — screening
Complete these before Step 1: Select medication:
- §3 Patient Registration — Prerequisites — registration, resolve-by-email, optional Lookup by id or email (
GET /identity/v1/patients/search); you need patientId forCreateSessionRequest. - Screening routes — Confirm Screening scopes on your Bearer token (§2 Access & Authentication) for
/screening/v1/*.
Step 1: Select medication
A screening session is bound to a catalog product (productId), a product variant (productVariantId), and a questionnaire (questionnaireId). Resolve the product first (to pick a variant), then load active questionnaires and pick questionnaireId for your care path (use the bundle endpoint when you need full question/option payloads for the UI).
2.1 Search catalogs
Source: GET /catalog/v1/catalogs/search (searchCatalogs). Lists catalogs available to the tenant (each catalog groups products). Use this when you need catalogId to scope Search products (§2.2) or to show a catalog picker in the UI.
Endpoint: GET /catalog/v1/catalogs/search
Headers: Authorization: Bearer <token>; X-Tenant-Id (required — current clinic/tenant UUID); optional If-None-Match
Query parameters (all optional unless noted)
| Name | Description |
|---|---|
id | Filter by catalog UUID. |
name | Filter by catalog name (matching behavior depends on deployment). |
includeCategories | When supported, include category rows on each catalog (string flag per OpenAPI — confirm with your client). |
limit, next | Pagination; next is the cursor from metadata on the previous page. |
sortBy, sortOrder | Sorting. |
(Query parameters are optional; omit If-None-Match on first call.)
Sample response (200) — PagedResponseOfCatalogSearchResponse:
{
"data": [
{
"id": "01933a7e-7b2c-7456-8000-000000000040",
"name": "Provider catalog",
"etag": "W/\"c1\"",
"createdAtUtc": "2024-01-01T00:00:00Z",
"createdBy": "system",
"updatedAtUtc": "2024-01-01T00:00:00Z",
"updatedBy": "system",
"categories": [
{
"id": "01933a7e-8c3d-8567-8000-000000000041",
"name": "Weight management",
"etag": "W/\"cat1\""
}
]
}
],
"metadata": {
"limit": {},
"nextCursor": "",
"hasMore": false
}
}
Use data[].id as catalogId when calling GET /catalog/v1/products/search. If includeCategories is omitted or unsupported, categories may be an empty array.
Errors: 401, 403, 404 (per spec). 304 when If-None-Match matches.
Note: Skip this step if catalogId is already known (config, or from product.catalog.id on a product returned by search).
2.2 Get product and variant
Source: GET /catalog/v1/products/search (searchProducts) and optionally GET /catalog/v1/products/{id}/find (findProductById). Prefer search—many deployments only expose /products/search; /find can be absent on the gateway even when it appears in the spec. Catalog calls require X-Tenant-Id on the request headers.
Search products (recommended)
Endpoint: GET /catalog/v1/products/search
Headers: Authorization: Bearer <token> (catalog may use a separate client or token); X-Tenant-Id (tenant UUID); optional If-None-Match
Query parameters (all optional; combine per your catalog)
| Name | Description |
|---|---|
variantId | Filter by product variant UUID (useful when you already know the variant). |
sku, variantSkus, name, brand | Product / variant text filters. |
catalogId, categoryId, pharmacy | Scope to catalog or category. |
limit, next, sortBy, sortOrder | Pagination, sorting. |
Request: No body.
Response: 200 — PagedResponseOfProductResponse. Use data[].id as productId. Choose productVariantId from data[].variants[] (match the variant you need).
Sample request
GET /catalog/v1/products/search?variantId=01933a7e-7b2c-7456-8000-000000000031&limit=5&catalogId=01933a7e-7b2c-7456-8000-000000000040 HTTP/1.1
Authorization: Bearer <access_token>
X-Tenant-Id: 01933a7e-6a1b-7123-8000-000000000003
(The catalogId query parameter is optional; use it to scope to a catalog from §2.1.)
Sample response (200) — full page body (example GET /catalog/v1/products/search?variantId=01933a7e-7b2c-7456-8000-000000000031&limit=5):
{
"data": [
{
"id": "01933a7e-7b2c-7456-8000-000000000030",
"sku": "COMP-GLP-001",
"name": "Sample GLP-1 medication",
"description": "Example catalog product",
"brand": "CareAtlas",
"isBundle": false,
"effectiveStartUtc": "2024-01-01T00:00:00Z",
"effectiveEndUtc": "2099-12-31T23:59:59Z",
"catalog": {
"id": "01933a7e-7b2c-7456-8000-000000000040",
"name": "Provider catalog",
"etag": "W/\"c1\"",
"createdAtUtc": "2024-01-01T00:00:00Z",
"createdBy": "system",
"updatedAtUtc": "2024-01-01T00:00:00Z",
"updatedBy": "system"
},
"attributes": [],
"variants": [
{
"id": "01933a7e-7b2c-7456-8000-000000000031",
"variantSku": "V0-30mg",
"name": "30 day supply",
"uomId": "01933a7e-7b2c-7456-8000-000000000050",
"attributes": [],
"etag": "W/\"v1\""
}
],
"categories": [],
"etag": "W/\"p1\"",
"createdAtUtc": "2024-01-01T00:00:00Z",
"createdBy": "system",
"updatedAtUtc": "2024-01-01T00:00:00Z",
"updatedBy": "system"
}
],
"metadata": {
"limit": {},
"nextCursor": "",
"hasMore": false
}
}
Get product by id (optional)
Endpoint: GET /catalog/v1/products/{id}/find — **operationId:** findProductById` in the same OpenAPI file (path key /catalog/v1/products/{id}/find). Headers: Authorization; X-Tenant-Id (required). Returns a single ProductResponse (same inner shape as data[] from search). If this route is not routed by your API gateway, use Search products with variantId or name / sku instead.
Request: No body.
Sample request
GET /catalog/v1/products/{id}/find HTTP/1.1
Authorization: Bearer <access_token>
X-Tenant-Id: 01933a7e-6a1b-7123-8000-000000000003
Sample response (200) — one ProductResponse (not wrapped in data); same fields as a single element of data[] in Search products:
{
"id": "01933a7e-7b2c-7456-8000-000000000030",
"sku": "COMP-GLP-001",
"name": "Sample GLP-1 medication",
"description": "Example catalog product",
"brand": "CareAtlas",
"isBundle": false,
"effectiveStartUtc": "2024-01-01T00:00:00Z",
"effectiveEndUtc": "2099-12-31T23:59:59Z",
"catalog": {
"id": "01933a7e-7b2c-7456-8000-000000000040",
"name": "Provider catalog",
"etag": "W/\"c1\"",
"createdAtUtc": "2024-01-01T00:00:00Z",
"createdBy": "system",
"updatedAtUtc": "2024-01-01T00:00:00Z",
"updatedBy": "system"
},
"attributes": [],
"variants": [
{
"id": "01933a7e-7b2c-7456-8000-000000000031",
"variantSku": "V0-30mg",
"name": "30 day supply",
"uomId": "01933a7e-7b2c-7456-8000-000000000050",
"attributes": [],
"etag": "W/\"v1\""
}
],
"categories": [],
"etag": "W/\"p1\"",
"createdAtUtc": "2024-01-01T00:00:00Z",
"createdBy": "system",
"updatedAtUtc": "2024-01-01T00:00:00Z",
"updatedBy": "system"
}
2.3 Fetch Available Questionnaires
The unified API exposes GET /screening/v1/questionnaires/active (not a separate paged /questionnaires list). Use it to discover questionnaireId. For rendering or validating the flow, load the full bundle with GET /screening/v1/questionnaires/{id}/bundle.
List active questionnaires
Endpoint: GET /screening/v1/questionnaires/active
Headers: Authorization: Bearer <token>; optional If-None-Match
Query parameters
| Name | Description |
|---|---|
name | Filter by questionnaire name / slug. |
version | Questionnaire version string. |
Request: No body.
Response: 200 — JSON array of QuestionnaireResponse. Use [].id as questionnaireId for POST /screening/v1/sessions/create.
Sample response (GET /screening/v1/questionnaires/active?name=branded):
[
{
"id": "01933a7e-7b2c-7456-8000-000000000060",
"name": "branded-medication-screening",
"version": "1",
"description": "Screening questionnaire for branded medication path",
"statusId": "01933a7e-7b2c-7456-8000-000000000061",
"treatmentId": "01933a7e-7b2c-7456-8000-000000000062",
"effectiveFromUtc": "2024-01-01T00:00:00Z",
"effectiveToUtc": "2099-12-31T23:59:59Z",
"questions": [],
"etag": "W/\"q1\"",
"createdAtUtc": "2024-01-01T00:00:00Z",
"createdBy": "system",
"updatedAtUtc": "2024-01-01T00:00:00Z",
"updatedBy": "system"
}
]
Get questionnaire bundle (optional)
Endpoint: GET /screening/v1/questionnaires/{id}/bundle
Headers: Authorization: Bearer <token>; optional If-None-Match
Path parameters
| Name | Description |
|---|---|
id | questionnaireId from the active list. |
Query parameters
| Name | Description |
|---|---|
expand | Optional; use per your client (e.g. related entities) when supported. |
Response: 200 — QuestionnaireBundleResponse (questions, answer options, and related fields per spec—use this to drive the screening UI after you create a session).
Note: Carry forward productId, productVariantId (from 2.2), and questionnaireId (from 2.3) into Step 3.
Step 2: Create screening session
Endpoint: POST /screening/v1/sessions/create (operationId: createSession)
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>, Content-Type: application/json
Body: CreateSessionRequest — all fields are required per the OpenAPI schema:
| Field | Description |
|---|---|
channel | Channel identifier (e.g. web) per your integration contract. |
patientId | From §3 Patient Registration; optional lookup via Lookup by id or email. |
questionnaireId | From Step 2 above. |
productId | Catalog product UUID. |
productVariantId | Catalog product variant UUID. |
Response: 201 with SessionCreateResponse, including id (session id). Use that value as {id} in Step 3 to post answers and complete the session.
Notes:
- 409 may indicate an existing session for the same patient/questionnaire — follow your product rules (resume vs. new session).
- Confirm with CareAtlas that your client’s Bearer token includes Screening / CRUD write scopes for
/screening/v1/*routes (gateway requirements may differ from Identity or Clinical alone).
Step 3: Submit questionnaire answers and complete the session
After POST /screening/v1/sessions/create returns a session id, drive the questionnaire with the Screening API: optionally fetch batches of questions, send answers, record consent when your flow requires it, then submit the session so downstream steps (e.g. prescriptions tied to screeningSessionId when your deployment supports that) can proceed.
Headers (all calls below): Authorization: Bearer <token>, X-Tenant-Id: <tenantId>, and Content-Type: application/json on POST bodies.
3.1 Get the next question batch (optional)
Endpoint: GET /screening/v1/sessions/{id}/next
Path: {id} = session id from Step 3.
Query parameters: optional limit (string per client), optional If-None-Match.
Response: 200 — QuestionnaireNextResponse:
| Field | Notes |
|---|---|
sessionId | Same session id. |
questions | Array of questions for this batch (shape per QuestionnaireNextQuestionResponse). |
totalRemaining | Server hint for remaining work (shape per deployment). |
isLastBatch | When true, no further /next batches are expected for this pass. |
Use this in paged UIs: call /next, render questions, collect answers, then POST /answer (§4.2). Repeat until isLastBatch is true or you have collected every answer your bundle requires.
Alternative: If you already loaded GET /screening/v1/questionnaires/{id}/bundle (§2.3), you can build the full form without calling /next, then send one or more /answer requests with every
linkIdthe bundle defines.
3.2 Post answers
Endpoint: POST /screening/v1/sessions/{id}/answer
Body: AnswerBundleRequest — { "answers": [ ... ] }
Each AnswerItem typically includes:
| Field | Description |
|---|---|
linkId | Question link id from the questionnaire bundle or /next payload (stable key for the question). |
answer | Value: string, number, boolean, or structured object per question type (single choice, multi-select, text, etc.). |
evidenceRef | Optional. For file-upload questions, reference returned after uploading the file to your storage flow (per tenant/API contract). |
Sample request (POST /screening/v1/sessions/{id}/answer):
{
"answers": [
{
"linkId": "q-height",
"answer": 70
},
{
"linkId": "q-current-meds",
"answer": "None"
},
{
"linkId": "q-conditions",
"answer": ["condition-a", "condition-b"]
}
]
}
Response: 200 — AnswerBundleResponse (per spec). You may call /answer multiple times (e.g. per batch) before submitting.
3.3 Record consent (when required)
Some tenants require consent before final submit. If your bundle or product rules say consent is needed:
Endpoint: POST /screening/v1/sessions/{id}/consent
Body: CreateConsentRequest — e.g. scope, termsVersion, grantedAt / expiresAt (ISO date-times) per OpenAPI.
Skip this step if your integration does not use session-level consent.
3.4 Submit the session
When all required answers are saved, complete the session:
Endpoint: POST /screening/v1/sessions/{id}/submit
Body: SessionSubmitRequest
| Field | Description |
|---|---|
validateOnly | false to finalize; true to validate without completing (if supported). |
Sample request:
{
"validateOnly": false
}
Response: 200 — SubmitSessionResponse — may include status, completedAt, and recommendedProductVariants (used by clinical flows to suggest variants). Use the same sessionId as screeningSessionId on POST /consult/v1/consults/create (§5 Step 2) when your flow includes a consult. For prescribing, pass the consult id as consultId on POST /clinical/v1/prescriptions/create (§6 Step 2).
Operational notes
- Order of operations (typical): create session → (/next + /answer)* → optional /consent → /submit. Exact branching depends on questionnaire configuration.
- Idempotency: Re-posting overlapping answers may be rejected or merged per backend rules; prefer one bundle per batch or a single final bundle before submit.
- Errors: 400 validation (missing required answers), 404 unknown session id — confirm session id and tenant.
5. 5. Consult
Many tenant programs run a consult (provider visit or chat thread) after screening and before prescribing. Consult records tie together patient, practitioner, screening session, and catalog product / variant via POST /consult/v1/consults/create (CreateConsultRequest). Skip this section if your flow prescribes directly from screening without a separate consult step.
Prerequisites — consult
- §4 Prerequisites — screening — Completed screening; you need screeningSessionId from §4 Step 3.
- §3 Patient Registration — patientId.
- §2 Access & Authentication — Bearer token with Consult scopes (
api://haas.consult/haas.api.read,api://haas.consult/haas.api.write). - Practitioner — practitionerId for CreateConsultRequest (resolve with Identity search/onboard; see §6 Prerequisites — prescriptions under Ensure Practitioner exists).
- Product / variant — selectedProductId and selectedProductVariantId aligned with screening / catalog (§4 Step 1).
Step 1: List consults (optional)
Endpoint: GET /consult/v1/consults/search (operationId: searchConsults)
Headers: Authorization: Bearer <token>, X-Tenant-Id (required)
Query (typical): patientId, practitionerId, status, screeningSessionId, selectedProductId, selectedProductVariantId, limit, next, sortBy, sortOrder
Response: 200 — PagedResponseOfGetConsultSummaryResponse — use to find or resume consults for a patient or session context.
Sample response (200) — shape of data[0] when listing:
{
"data": [
{
"id": "01933a7e-a001-7c01-8000-000000000040",
"status": "active",
"tenantId": "01933a7e-6a1b-7123-8000-000000000003",
"patientId": "01933a7e-7b2c-7456-8000-000000000010",
"practitionerId": "01933a7e-ae5f-7789-8000-000000000021",
"screeningSessionId": "01933a7e-d182-7a23-8000-000000000030",
"selectedProductId": "01933a7e-bf60-7801-8000-000000000022",
"selectedProductVariantId": "01933a7e-c071-7912-8000-000000000023",
"channel": "web",
"startedUtc": "2025-03-04T15:00:00Z"
}
],
"metadata": {
"limit": "10",
"nextCursor": "",
"hasMore": false
}
}
(Metadata keys may match PageMetadata in your client; confirm against the spec.)
Step 2: Create a consult
Endpoint: POST /consult/v1/consults/create (createConsult)
Headers: Authorization: Bearer <token>, X-Tenant-Id, Content-Type: application/json
Body: CreateConsultRequest — required per OpenAPI: patientId, practitionerId, topic, channel, screeningSessionId, selectedProductId, selectedProductVariantId.
Sample request (POST /consult/v1/consults/create):
{
"patientId": "01933a7e-7b2c-7456-8000-000000000010",
"practitionerId": "01933a7e-ae5f-7789-8000-000000000021",
"topic": "weight-loss-intake",
"channel": "web",
"screeningSessionId": "01933a7e-d182-7a23-8000-000000000030",
"selectedProductId": "01933a7e-bf60-7801-8000-000000000022",
"selectedProductVariantId": "01933a7e-c071-7912-8000-000000000023"
}
Response: 200 — CreateConsultResponse — includes id (consult uuid). Use this id on GET/messages routes below.
Sample response (200):
{
"id": "01933a7e-a001-7c01-8000-000000000040",
"providerTenantId": "01933a7e-6a1b-7123-8000-000000000003",
"createdAtUtc": "2025-03-04T15:05:00Z",
"updatedAtUtc": "2025-03-04T15:05:00Z"
}
Step 3: Get consult detail
Endpoint: GET /consult/v1/consults/{id}/get (getConsult)
Headers: Authorization: Bearer <token>, X-Tenant-Id; optional If-None-Match
Response: 200 — GetConsultResponse — status, screeningSessionId, patientId, practitionerId, selected product ids, conversationRef, participants, chats, lifecycle timestamps, etag.
Sample response (200) — GetConsultResponse for path {id} (same consult id as create response):
{
"id": "01933a7e-a001-7c01-8000-000000000040",
"status": "active",
"tenantId": "01933a7e-6a1b-7123-8000-000000000003",
"patientId": "01933a7e-7b2c-7456-8000-000000000010",
"practitionerId": "01933a7e-ae5f-7789-8000-000000000021",
"screeningSessionId": "01933a7e-d182-7a23-8000-000000000030",
"selectedProductId": "01933a7e-bf60-7801-8000-000000000022",
"selectedProductVariantId": "01933a7e-c071-7912-8000-000000000023",
"channel": "web",
"startedUtc": "2025-03-04T15:05:00Z",
"conversationRef": "",
"participants": [],
"chats": [],
"etag": "W/\"consult-etag\""
}
Step 4: Messages and integrations
| Operation | Method | Path | Notes |
|---|---|---|---|
| Search messages | GET | /consult/v1/consults/{id}/messages/search | searchConsultMessages. Paged ConsultMessageResponse. X-Tenant-Id required. |
| Add participant | POST | /consult/v1/consults/{id}/participants/add | addConsultParticipant. Body AddParticipantRequest (practitionerId, displayName). 201 — AddParticipantResponse. Registers a practitioner on the consult chat; patientId on the row is taken from the consult. |
| Post message | POST | /consult/v1/consults/{id}/messages/post | postConsultMessage. Body PostMessageRequest (content, optional participantId, isMedia). participantId is the consult participant row id from ConsultParticipantDto, not the identity patient/practitioner uuid. |
| Update message delivery | POST | /consult/v1/consults/{id}/messages/{messageId}/update-delivery | updateConsultMessageDelivery. Body UpdateMessageDeliveryRequest (deliveredUtc, readUtc). 200 — UpdateMessageDeliveryResponse. |
| Post CS message | POST | /consult/v1/consults/{id}/cs-messages | postCsMessage. Body PostCsMessageRequest. Consult write scope. |
Beluga inbound webhooks (POST /consult/v1/consult/message, /consult/v1/consult/cs-message, /consult/v1/consult/close, /consult/v1/consult/cancel) are not in the unified partner OpenAPI; they are server-to-server integration routes on the consult service.
Step 5: Close or cancel consult (provider / tenant API)
| Operation | Method | Path | Notes |
|---|---|---|---|
| Close | POST | /consult/v1/consults/{id}/close | closeConsult. Path id = consult UUID; X-Tenant-Id required. No body. 200 — CloseConsultResponse. |
| Cancel | POST | /consult/v1/consults/{id}/cancel | cancelConsult. Path id = consult UUID; X-Tenant-Id required. No body. 200 — CancelConsultResponse. |
Close marks the consult ended (e.g. provider portal after prescription create). Cancel records cancellation with canceledAtUtc and canceledBy.
When your workflow is ready to prescribe, continue to §6 Creating Prescriptions.
6. 6. Creating Prescriptions
Prerequisites — prescriptions
Build on §4 Prerequisites — screening (patient, screening session workflow, Screening scopes). If your program runs a provider consult after screening, complete §5 Prerequisites — consult first. For Identity patient fields used on orders, follow §3 Patient Registration and Lookup by id or email (GET /identity/v1/patients/search) when needed.
This section adds clinic and practitioner prerequisites not covered above:
Ensure Clinic exists
Order import requires clinic (ClinicInfo) on every OrderImportRequest—clinic name, full address, email, and phone. Use the clinic APIs to find an existing clinic or create/link one, then map the result to ClinicInfo for the order.
Search clinics
Endpoint: GET /identity/v1/tenants/clinics/search
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>
Query parameters: name, id, npi, externalId, limit, after (optional). Use these to find a clinic by name, tenant id, NPI, or external id.
Response: Paged list of tenants (clinics). Each item is a TenantResponse-like object with id, name, partnerId, parentTenantId, role, and attributes. Use the tenant(s) to build or resolve ClinicInfo for the order (name, address, email, phone—from tenant attributes or your own data).
Sample response (GET /identity/v1/tenants/clinics/search): Returns a paged list; each element in data has id, name, partnerId, parentTenantId, role, etag, and related fields. Map to ClinicInfo using the tenant’s name and attribute/address data as required by your backend.
Onboard clinic
Endpoint: POST /identity/v1/tenants/clinics/onboard
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>, Content-Type: application/json
Body: OnboardClinicRequest — partnerId, parentTenantId, name, and attributes (e.g. name, npi, addressId, email, phone, description). See Object reference for schema details.
Response: 201 with TenantResponse. Use the returned tenant (and any address resolved from attributes.addressId) to build ClinicInfo for order import.
Sample request (POST /identity/v1/tenants/clinics/onboard):
{
"partnerId": "01933a7e-5f2a-7000-8000-000000000002",
"parentTenantId": "01933a7e-6a1b-7123-8000-000000000003",
"name": "Acme Clinic",
"attributes": {
"name": "Acme Clinic",
"npi": "1234567890",
"addressId": "01933a7e-ad00-7000-8000-000000000010",
"email": "contact@acmeclinic.example.com",
"phone": "5559876543",
"description": ""
}
}
Sample response (POST /identity/v1/tenants/clinics/onboard): Returns the created/linked tenant with id, name, partnerId, parentTenantId, role, etag, and timestamps. Use this tenant and your address data to populate OrderImportRequest.clinic (ClinicInfo).
What you need for OrderImportRequest:
- clinic – ClinicInfo:
clinicName,clinicAddressLine1,clinicAddressLine2,clinicCity,clinicState,clinicZip,clinicEmail,clinicPhoneNumber. Populate from the clinic/tenant returned by search or onboard, and from the resolved address when usingattributes.addressId.
Ensure Practitioner exists
You need practitioner.id as practitionerId in CreatePrescriptionCommandRequest when creating prescriptions (Step 2); it is optional in the OpenAPI schema but typically required for your workflow.
Fetching existing practitioner
If you already have a practitioner ID (e.g. from your system or a previous onboard response), call GET /identity/v1/practitioners/search with query id=<practitionerId> (and/or npi, email, etc. per spec).
Headers: Authorization: Bearer <token>; optional If-None-Match
Response: PagedResponseOfPractitionerResponse — use the matching PractitionerResponse from data (see PractitionerResponse).
Sample response (GET /identity/v1/practitioners/search?id=01933a7e-ae5f-7789-8000-000000000021&limit=1): Example shape for data[0]:
{
"data": [
{
"id": "01933a7e-ae5f-7789-8000-000000000021",
"tenantId": "01933a7e-6a1b-7123-8000-000000000003",
"accountId": "01933a7e-aa5e-7788-8000-000000000020",
"npi": "1234567890",
"firstName": "Maria",
"middleName": "",
"lastName": "Smith",
"suffix": "",
"displayName": "Maria Smith",
"providerTypeId": "provider-type-uuid",
"statusId": "active",
"inactivated": false,
"email": "maria.smith@clinic.example.com",
"emailVerified": true,
"phoneNumber": "5559876543",
"phoneNumberVerified": false,
"address": {
"addressLine1": "456 Clinic Way",
"addressLine2": "",
"city": "San Francisco",
"state": "CA",
"zipCode": "94103",
"phone": "5559876543"
},
"etag": "W/\"practitioner-etag\""
}
],
"metadata": {
"limit": {},
"nextCursor": "",
"hasMore": false
}
}
Creating new practitioner
If the practitioner does not exist, use POST /identity/v1/practitioners/onboard with PractitionerOnboardRequest (see Object reference for schema). The API returns an existing practitioner when one matches (e.g. by NPI/tenant), or creates one. Use the returned practitioner.id as practitionerId in prescription and order flows.
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>, Content-Type: application/json
Body: PractitionerOnboardRequest — see Object reference for schema.
Sample request (POST /identity/v1/practitioners/onboard):
{
"accountId": "01933a7e-aa5e-7788-8000-000000000020",
"npi": "1234567890",
"firstName": "Maria",
"middleName": "",
"lastName": "Smith",
"suffix": "",
"displayName": "Maria Smith",
"providerTypeId": "01933a7e-pt00-7000-8000-000000000001",
"statusId": "01933a7e-st00-7000-8000-000000000002",
"address": {
"addressLine1": "456 Clinic Way",
"addressLine2": "",
"city": "San Francisco",
"state": "CA",
"zipCode": "94103",
"phone": "5559876543"
},
"createIfMissing": true
}
Sample response (POST /identity/v1/practitioners/onboard):
{
"found": false,
"created": true,
"practitioner": {
"id": "01933a7e-ae5f-7789-8000-000000000021",
"tenantId": "01933a7e-6a1b-7123-8000-000000000003",
"accountId": "01933a7e-aa5e-7788-8000-000000000020",
"npi": "1234567890",
"firstName": "Maria",
"middleName": "",
"lastName": "Smith",
"suffix": "",
"displayName": "Maria Smith",
"providerTypeId": "01933a7e-pt00-7000-8000-000000000001",
"statusId": "01933a7e-st00-7000-8000-000000000002",
"inactivated": false,
"email": "",
"emailVerified": false,
"phoneNumber": "5559876543",
"phoneNumberVerified": false,
"address": {
"addressLine1": "456 Clinic Way",
"addressLine2": "",
"city": "San Francisco",
"state": "CA",
"zipCode": "94103",
"phone": "5559876543"
},
"etag": "W/\"practitioner-etag\""
}
}
Step 1: Resolve Product Variant (Medication)
Endpoint: GET /catalog/v1/products/search (e.g. query variantId from the prescription, or name / sku) — see §4 Step 2.2. Optional: GET /catalog/v1/products/{id}/find when your gateway exposes it (same ProductResponse shape; send X-Tenant-Id).
Headers: Authorization (catalog may use a different token in some environments); X-Tenant-Id; optional If-None-Match
Response: ProductResponse (from data[] or from /find) — use the product’s variants to get productVariantId (from prescription or e.g. variants[0].id).
The Import Order API also requires uomId (unit of measure) on each line. This app does not use UOM for any business logic; it only sends it because the API requires it. The app gets uomId from the variant when present, otherwise uses a fixed default. For headless implementations: send the variant’s uomId if available, or a known default UUID your backend accepts.
Sample response (single ProductResponse — e.g. one data row from search, or body of /find when available):
{
"id": "01933a7e-bf60-7801-8000-000000000022",
"name": "Example Medication",
"etag": "W/\"product-etag\"",
"variants": [
{
"id": "01933a7e-c071-7912-8000-000000000023",
"name": "10mg",
"variantSku": "MED-10MG",
"uomId": "e0b02579-ddbd-4d58-8ed1-8df498178e1d",
"product": {
"id": "01933a7e-bf60-7801-8000-000000000022",
"name": "Example Medication"
}
}
]
}
(Schema may vary; the important fields for order import are variants[].id (productVariantId) and variants[].uomId.)
Step 2: Get or create a prescription
Clinical routes in CareAtlas-Unified-API-oas3-v0.1.json include GET /clinical/v1/prescriptions/search, POST /clinical/v1/prescriptions/create, GET /clinical/v1/prescriptions/{id}/validate, and PATCH /clinical/v1/prescriptions/{id}/update. For provider prescribing after a consult, use create with consultId from §5 Step 2 (and pharmacyId, medsPrescribed, practitioner fields per your workflow).
Search existing prescriptions
Endpoint: GET /clinical/v1/prescriptions/search with query id=<prescriptionId> (and/or patientId, patientEmail, etc. per spec).
Headers: X-Tenant-Id, Authorization
Response: PagedResponseOfPrescriptionResponse — use the matching PrescriptionResponse from data.
Create prescriptions (provider flow)
Endpoint: POST /clinical/v1/prescriptions/create (operationId: createPrescriptionCommand)
Headers: X-Tenant-Id, Authorization, Content-Type: application/json
Body: CreatePrescriptionCommandRequest; each element of medsPrescribed is a MedPrescribedInternal. See Object reference for fields.
Response: 201 Created — JSON array of PrescriptionResponse (one entry per item in medsPrescribed).
Sample request:
{
"consultId": "01933a7e-a001-7c01-8000-000000000040",
"medsPrescribed": [
{
"productId": "01933a7e-bf60-7801-8000-000000000022",
"productVariantId": "01933a7e-c071-7912-8000-000000000023",
"strength": "10mg",
"frequency": "once daily",
"route": "oral"
}
],
"pharmacyId": "0193ace2-7048-7692-9f57-91e59b94b40a",
"pharmacyNote": "Bill to patient, ship to patient",
"practitionerId": "01933a7e-ae5f-7789-8000-000000000021"
}
Sample response (201): Same shape as each element in the search data array below (array of PrescriptionResponse).
Validate a prescription
Endpoint: GET /clinical/v1/prescriptions/{id}/validate (operationId: validatePrescription)
Headers: X-Tenant-Id, Authorization
Response: 200 — ValidatePrescriptionResponse (see ValidatePrescriptionResponse): includes isValid (boolean).
Update a prescription
Endpoint: PATCH /clinical/v1/prescriptions/{id}/update (operationId: updatePrescriptionCommand)
Headers: X-Tenant-Id, Authorization, Content-Type: application/json
Body: UpdatePrescriptionCommandRequest — all properties optional in the bundled schema; use for notes, approval metadata, or payment fields per your process.
Response: 200 — PrescriptionResponse.
Sample response (GET /clinical/v1/prescriptions/search?id=01933a7e-9d4e-7678-8000-000000000020&limit=1): Example shape for data[0]:
{
"data": [
{
"id": "01933a7e-9d4e-7678-8000-000000000020",
"tenantId": "01933a7e-6a1b-7123-8000-000000000003",
"patientId": "01933a7e-7b2c-7456-8000-000000000010",
"practitionerId": "01933a7e-ae5f-7789-8000-000000000021",
"productId": "01933a7e-bf60-7801-8000-000000000022",
"productVariantId": "01933a7e-c071-7912-8000-000000000023",
"medicationName": "Example Medication 10mg",
"quantityAuthorized": { "value": 1 },
"approvedAtUtc": "2025-03-01T14:00:00Z",
"pharmacyId": "0193ace2-7048-7692-9f57-91e59b94b40a"
}
],
"metadata": {
"limit": {},
"nextCursor": "",
"hasMore": false
}
}
Step 3: Map prescription fields for order import
From the prescription you need for the order:
- id →
lines[].matchedPrescriptionId - patientId →
customer.patientId - productId →
lines[].productId - productVariantId →
lines[].productVariantId(or resolve from product in Step 1) - quantityAuthorized →
lines[].qty(e.g.quantityAuthorized.valueor default 1)
7. 7. Creating Orders
Prerequisites — orders
Build on §6 Prerequisites — prescriptions (clinic, practitioner, product variant, and prescription). Confirm §2 Access & Authentication grants Commerce scopes for POST /commerce/v1/orders/import (same Bearer token and X-Tenant-Id as earlier steps).
Complete §6 Step 3 so you have the prescription and patient field mapping before assembling the payload. Full request shape and the import call are in Build OrderImportRequest and call Import Order below.
Build OrderImportRequest and call Import Order
Build the request body and call the import endpoint. Body schema: OrderImportRequest — see Object reference for full schema and nested types (CustomerInfo, ClinicInfo, LineInfo, Address, TotalsInfo, PaymentInfo, SourceInfo).
Mapping from prescription to order: Use the data from earlier steps to fill the payload:
- prescription.id →
lines[].matchedPrescriptionId - prescription.patientId →
customer.patientId - prescription.productId →
lines[].productId - prescription.productVariantId →
lines[].productVariantId(or from §6 Step 1) - prescription.quantityAuthorized →
lines[].qty(e.g..valueor default 1) - patient.account.id →
customer.accountId(§3 / lookup) - patient.address →
shippingAddressandbillingAddress(full Address objects) - clinic → §6 Prerequisites — prescriptions (Ensure Clinic exists); source.partnerAppId from Fetch Tenant ID and Partner App ID
Numeric fields like qty, unitPrice, lineTotal, and totals may be strings in the API; see the sample request below and OrderImportRequest.
Endpoint: POST /commerce/v1/orders/import
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>, Content-Type: application/json. Optional: Idempotency-Key: <key> to avoid duplicate orders on retries.
Sample request (POST /commerce/v1/orders/import):
{
"externalOrderId": "ORDER-1730123456789",
"customer": {
"patientId": "01933a7e-7b2c-7456-8000-000000000010",
"accountId": "01933a7e-8c3d-7567-8000-000000000011"
},
"clinic": {
"clinicName": "Acme Clinic",
"clinicAddressLine1": "456 Clinic Way",
"clinicAddressLine2": "",
"clinicCity": "San Francisco",
"clinicState": "CA",
"clinicZip": "94103",
"clinicEmail": "clinic@acme.example.com",
"clinicPhoneNumber": "5559876543"
},
"currencyISO": "USD",
"pricing": {
"priceBookVersion": "1.0",
"components": []
},
"lines": [
{
"externalLineId": "LINE-1730123456789",
"productId": "01933a7e-bf60-7801-8000-000000000022",
"productVariantId": "01933a7e-c071-7912-8000-000000000023",
"uomId": "e0b02579-ddbd-4d58-8ed1-8df498178e1d",
"qty": "1",
"unitPrice": "29.99",
"lineTotal": "29.99",
"priceComponents": [],
"requiresPrescription": true,
"matchedPrescriptionId": "01933a7e-9d4e-7678-8000-000000000020"
}
],
"promos": [],
"totals": {
"subtotal": "29.99",
"taxTotal": "0",
"shippingTotal": "0",
"grandTotal": "29.99"
},
"shippingAddress": {
"addressLine1": "123 Main St",
"addressLine2": "Apt 4",
"city": "San Francisco",
"state": "CA",
"zipCode": "94102",
"phone": "5551234567"
},
"billingAddress": {
"addressLine1": "123 Main St",
"addressLine2": "Apt 4",
"city": "San Francisco",
"state": "CA",
"zipCode": "94102",
"phone": "5551234567"
},
"payment": {},
"validatedAtUtc": "2025-03-04T12:00:00.000Z",
"source": {
"partnerAppId": "01933a7e-5f2a-7000-8000-000000000001",
"channel": "web",
"region": "US"
},
"notes": "Order for prescription 01933a7e-9d4e-7678-8000-000000000020"
}
Sample response (201 Created):
{
"id": "01933a7e-e293-7b34-8000-000000000040",
"orderNumber": "ORD-10042",
"tenantId": "01933a7e-6a1b-7123-8000-000000000003",
"patientId": "01933a7e-7b2c-7456-8000-000000000010",
"status": "Submitted",
"currencyISO": "USD",
"lines": [
{
"id": "01933a7e-f3a4-7c45-8000-000000000041",
"productId": "01933a7e-bf60-7801-8000-000000000022",
"productVariantId": "01933a7e-c071-7912-8000-000000000023",
"sku": "MED-10MG",
"uomId": "e0b02579-ddbd-4d58-8ed1-8df498178e1d",
"qty": { "value": 1 },
"unitPrice": { "value": 29.99 },
"lineTotal": { "value": 29.99 },
"prescriptionId": "01933a7e-9d4e-7678-8000-000000000020"
}
],
"subtotal": "29.99",
"taxTotal": "0",
"shippingTotal": "0",
"grandTotal": "29.99",
"shippingAddress": {
"addressLine1": "123 Main St",
"addressLine2": "Apt 4",
"city": "San Francisco",
"state": "CA",
"zipCode": "94102",
"phone": "5551234567"
},
"billingAddress": {
"addressLine1": "123 Main St",
"addressLine2": "Apt 4",
"city": "San Francisco",
"state": "CA",
"zipCode": "94102",
"phone": "5551234567"
},
"etag": "W/\"order-etag\""
}
8. 8. Working with Webhooks
The Hub service manages outbound webhooks: CareAtlas POSTs event payloads to your HTTPS URL when something happens (per event type). You do not implement CRUD against arbitrary domain tables here—you configure subscriptions, verify delivery to your endpoint, and operate retries and secrets. All Hub webhook routes use the same API base URL as the rest of the unified API; paths published in CareAtlas-Unified-API-oas3-v0.1.json appear in the §9 API Reference table.
The bundled spec includes subscription create, patch update, disable, plus search, get (…/get), deliveries, test, retry, and rotate-secret.
OpenAPI: Examples include createSubscriptionCommand, updateSubscriptionCommand, disableSubscriptionCommand, listEventTypesQuery, listSubscriptionsQuery, getSubscriptionQuery, listSubscriptionsForEventType, testWebhook, getDeliveryQuery, retryDelivery, rotateSecret — verify names in the spec.
Getting started
Available event types (reference)
Display name (left) is for readability; the name string (right) matches event-type identifiers. The authoritative list—including payloadSchema—comes from GET /hub/v1/webhooks/event-types/search (List event types).
| Display name | name |
|---|---|
| Consult Created | consult.created |
| Consult Updated | consult.updated |
| Invoice Created | invoice.created |
| Invoice Updated | invoice.updated |
| Order Created | order.created |
| Order Updated | order.updated |
| Patient Created | patient.created |
| Patient Updated | patient.updated |
| Payment Created | payment.created |
| Payment Updated | payment.updated |
| Practitioner Created | practitioner.created |
| Practitioner Updated | practitioner.updated |
| Prescription Created | prescription.created |
| Prescription Updated | prescription.updated |
| Screening Response Created | screening.response.created |
| Screening Response Updated | screening.response.updated |
| Screening Session Created | screening.session.created |
| Screening Session Updated | screening.session.updated |
Your inbound endpoint
Configure your server to accept HTTPS POST from CareAtlas, respond with 2xx quickly, and verify signatures using the signingSecret from Create subscription (201) or Rotate signing secret. Exact headers and body envelope follow your deployment’s webhook contract (payload shapes are described per EventTypeResponse.payloadSchema where provided).
Authentication and headers
- Bearer token must include Hub scopes:
api://haas.hub/haas.api.read(event types and subscription reads) andapi://haas.hub/haas.api.write(create, update, and disable subscription; test delivery; retry delivery; rotate secret). - X-Tenant-Id – Required on routes that declare it (e.g. POST /hub/v1/webhooks/subscriptions/create, GET /hub/v1/webhooks/subscriptions/search, GET /hub/v1/webhooks/subscriptions/{id}/deliveries). GET /hub/v1/webhooks/event-types/search has no tenant header in the bundled OpenAPI.
List event types
Endpoint: GET /hub/v1/webhooks/event-types/search (listEventTypesQuery)
Headers: Authorization: Bearer <token> (Hub read scope).
Response
HTTP 200 — JSON array of EventTypeResponse.
Fields per item: name, displayName, description, sourceService, payloadSchema.
Use each `name` string in the `eventTypes` array when creating or updating a subscription.
Subscriptions
Create a subscription
Endpoint: POST /hub/v1/webhooks/subscriptions/create (createSubscriptionCommand)
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>, Content-Type: application/json
Body: CreateSubscriptionRequest — required: url, eventTypes (array of event name strings from List event types), description, expiresAtUtc. Optional: notificationEmails (comma-separated or deployment-specific format for alert recipients).
Response: 201 Created — CreateSubscriptionResponse includes id, url, eventTypes, signingSecret, expiresAtUtc, notificationEmails, etag, isActive, partnerAppId, etc. Store signingSecret securely; it is used to verify inbound webhook requests (also returned only at rotate-secret thereafter).
Search subscriptions
Endpoint: GET /hub/v1/webhooks/subscriptions/search (listSubscriptionsQuery)
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>
Query (typical): limit, next, sortBy, sortOrder (per OpenAPI).
Get one subscription
Endpoint: GET /hub/v1/webhooks/subscriptions/{id}/get (getSubscriptionQuery)
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>; optional If-None-Match
Search subscriptions for an event type
Endpoint: GET /hub/v1/webhooks/event-types/{name}/subscriptions/search (listSubscriptionsForEventType)
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>
Path {name} is the event type name (e.g. order.created).
Update a subscription
Endpoint: PATCH /hub/v1/webhooks/subscriptions/{id}/update (updateSubscriptionCommand)
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>, Content-Type: application/json
Body: JSON body per the OpenAPI PATCH operation (the bundled file may reference a placeholder schema name; your generated client maps it to the subscription update type). May include notificationEmails when updating alert recipients. 200 returns UpdateSubscriptionResponse (id, url, eventTypes, notificationEmails, expiresAtUtc, etag, etc.—no signingSecret). Responses may include 412 / 409 for concurrency or conflict—follow etag / conditional request rules in the spec for your client.
Disable a subscription
Endpoint: POST /hub/v1/webhooks/subscriptions/{id}/disable (disableSubscriptionCommand)
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>
Response: 204 No Content — stops deliveries for that subscription (per API semantics; confirm behavior with your deployment).
Test delivery
Endpoint: POST /hub/v1/webhooks/subscriptions/{id}/test
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>
Triggers a test delivery to the subscription URL. Response: 202 Accepted. Use after creating (or otherwise obtaining) a subscription to confirm connectivity before production traffic.
Rotate signing secret
Endpoint: POST /hub/v1/webhooks/subscriptions/{id}/rotate-secret
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>
Returns a new secret (response shape per RotateSecretResponse in the OpenAPI spec). Update your verifier to accept both old and new secrets during rotation, then retire the old secret.
Deliveries
List deliveries for a subscription
Endpoint: GET /hub/v1/webhooks/subscriptions/{id}/deliveries
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>
Query (typical): limit, next, status, from, to, sortBy, sortOrder (per OpenAPI).
Response
HTTP 200 — paged list of DeliveryResponse (per spec). Use for observability and debugging.
Get one delivery
Endpoint: GET /hub/v1/webhooks/deliveries/{id}/get (getDeliveryQuery)
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId> (per spec)
Use this to inspect payload metadata, status, and errors for a single delivery attempt.
Retry a failed delivery
Endpoint: POST /hub/v1/webhooks/deliveries/{id}/retry
Headers: Authorization: Bearer <token>, X-Tenant-Id: <tenantId>
Response: 202 Accepted — Hub queues a retry. Combine with the deliveries list to recover from transient 5xx responses on your server.
9. API Reference
Subsections follow the service path prefix on the unified API base URL: /identity/v1, /catalog/v1, /screening/v1, /consult/v1, /clinical/v1, /commerce/v1, /hub/v1.
1. Identity (/identity/v1)
| Operation | Method | Path | Notes |
|---|---|---|---|
| Get tenant + partner app | GET | /identity/v1/tenants/me | MyTenantsResponse — partnerApp.id, tenants[], tenantProviders[], partner (MyTenantsPartnerResponse). |
| Search clinics | GET | /identity/v1/tenants/clinics/search | Query: name, id, npi, externalId, limit, after. Requires X-Tenant-Id. |
| Search practitioners | GET | /identity/v1/practitioners/search | Query: id, npi, email, limit, next, etc. |
| Onboard clinic | POST | /identity/v1/tenants/clinics/onboard | Body: OnboardClinicRequest. Creates or links a clinic tenant. |
| Search patients | GET | /identity/v1/patients/search | Query: id, email, accountId, limit, next, etc. Requires X-Tenant-Id. |
| Resolve patient by email | POST | /identity/v1/patients/resolve-by-email | Body: PatientResolveRequest; returns existing or newly created patient. |
2. Catalog (/catalog/v1)
| Operation | Method | Path | Notes |
|---|---|---|---|
| Search catalogs | GET | /catalog/v1/catalogs/search | Query: id, name, includeCategories, limit, next, sortBy, sortOrder. Header X-Tenant-Id required. PagedResponseOfCatalogSearchResponse. |
| Search products | GET | /catalog/v1/products/search | Query: variantId, sku, name, brand, catalogId, limit, next, etc. Header X-Tenant-Id required. Paged ProductResponse list. |
| Get product by id (optional) | GET | /catalog/v1/products/{id}/find | OpenAPI findProductById. Header X-Tenant-Id required. Some gateways omit this route—use Search products if 404. Single ProductResponse. |
3. Screening (/screening/v1)
| Operation | Method | Path | Notes |
|---|---|---|---|
| List active questionnaires | GET | /screening/v1/questionnaires/active | Query: name, version. Returns array of QuestionnaireResponse. |
| Get questionnaire bundle | GET | /screening/v1/questionnaires/{id}/bundle | Query: expand. Returns QuestionnaireBundleResponse (questions, options, etc.). |
| Create screening session | POST | /screening/v1/sessions/create | Body: CreateSessionRequest; headers: X-Tenant-Id. operationId: createSession. |
| Next questionnaire batch | GET | /screening/v1/sessions/{id}/next | Path: session id. Optional query limit. Returns QuestionnaireNextResponse. |
| Submit session answers | POST | /screening/v1/sessions/{id}/answer | Body: AnswerBundleRequest (answers: AnswerItem[]). Headers: X-Tenant-Id. |
| Record session consent | POST | /screening/v1/sessions/{id}/consent | Body: CreateConsentRequest. When tenant flow requires consent before submit. |
| Submit screening session | POST | /screening/v1/sessions/{id}/submit | Body: SessionSubmitRequest (validateOnly). Completes session; returns SubmitSessionResponse. |
| Update screening session | POST | /screening/v1/sessions/{id}/update | Body: UpdateSessionRequest (productVariantId, expiresAtUtc). updateSession. Change variant/expiry on in-progress session. |
| Record session payment | POST | /screening/v1/sessions/{id}/record-payment | Body: RecordPaymentRequest (paymentIntentId, amountCents). recordPayment. Sets Authorized payment on session. |
| Update payment status | POST | /screening/v1/sessions/{id}/update-payment-status | Body: UpdatePaymentStatusRequest (status: Captured, Failed, Refunded). updatePaymentStatus. |
| Cancel screening session | POST | /screening/v1/sessions/{id}/cancel | Body: CancelSessionRequest (reason). 200 — SessionResponse. cancelSession. |
4. Consult (/consult/v1)
| Operation | Method | Path | Notes |
|---|---|---|---|
| Search consults | GET | /consult/v1/consults/search | Query: patientId, practitionerId, screeningSessionId, status, selectedProductId, selectedProductVariantId, limit, next, etc. X-Tenant-Id required. searchConsults. |
| Create consult | POST | /consult/v1/consults/create | Body: CreateConsultRequest. 200 — CreateConsultResponse. createConsult. Consult write scope. |
| Get consult | GET | /consult/v1/consults/{id}/get | GetConsultResponse. getConsult. Optional If-None-Match. X-Tenant-Id required. |
| Search consult messages | GET | /consult/v1/consults/{id}/messages/search | Paged ConsultMessageResponse. searchConsultMessages. |
| Add consult participant | POST | /consult/v1/consults/{id}/participants/add | Body: AddParticipantRequest (practitionerId, displayName). 201 — AddParticipantResponse. addConsultParticipant. X-Tenant-Id required. |
| Post consult message | POST | /consult/v1/consults/{id}/messages/post | Body: PostMessageRequest (content, participantId, isMedia). postConsultMessage. participantId = consult participant row id. X-Tenant-Id required. |
| Update consult message delivery | POST | /consult/v1/consults/{id}/messages/{messageId}/update-delivery | Body: UpdateMessageDeliveryRequest (deliveredUtc, readUtc). 200 — UpdateMessageDeliveryResponse. updateConsultMessageDelivery. X-Tenant-Id required. |
| Post CS message (consult) | POST | /consult/v1/consults/{id}/cs-messages | Body: PostCsMessageRequest. postCsMessage. X-Tenant-Id required. |
| Close consult | POST | /consult/v1/consults/{id}/close | Path consult id; no body. 200 — CloseConsultResponse. closeConsult. Tenant/provider command (not Beluga webhook). |
| Cancel consult | POST | /consult/v1/consults/{id}/cancel | Path consult id; no body. 200 — CancelConsultResponse. cancelConsult. Tenant/provider command (not Beluga webhook). |
5. Clinical (/clinical/v1)
| Operation | Method | Path | Notes |
|---|---|---|---|
| Search prescriptions | GET | /clinical/v1/prescriptions/search | Query: id, patientId, patientEmail, limit, next, etc. Requires X-Tenant-Id. |
| Create prescriptions | POST | /clinical/v1/prescriptions/create | Body: CreatePrescriptionRequest (consultId, medsPrescribed, pharmacyId, …). 201 — array of PrescriptionResponse. createPrescriptionCommand. Requires clinical write scope. |
| Validate prescription | GET | /clinical/v1/prescriptions/{id}/validate | Returns ValidatePrescriptionResponse (isValid). validatePrescription. |
| Update prescription | PATCH | /clinical/v1/prescriptions/{id}/update | Body: UpdatePrescriptionCommandRequest. 200 — PrescriptionResponse. updatePrescriptionCommand. |
| Cancel prescription | POST | /clinical/v1/prescriptions/{id}/cancel | Body: CancelPrescriptionRequest. 200 — PrescriptionResponse. cancelPrescription. |
6. Commerce (/commerce/v1)
| Operation | Method | Path | Notes |
|---|---|---|---|
| Submit payment | POST | /commerce/v1/payments/submit | Body: SubmitPaymentRequest (entityName, entityId, orderId, provider, intentId, status, amount, currencyISO). 200 — SubmitPaymentResponse. submitPayment. |
| Import order | POST | /commerce/v1/orders/import | Body: OrderImportRequest; headers: X-Tenant-Id, optional Idempotency-Key. |
| Search orders | GET | /commerce/v1/orders/search | Query: patientId, orderNumber, limit, etc. |
| Get order by id | GET | /commerce/v1/orders/{id}/get | Full order details. getOrderQuery. Optional If-None-Match. X-Tenant-Id required. |
| Link orders | POST | /commerce/v1/orders/link | Body: LinkOrdersRequest (masterOrderId, childOrderIds). 200 — LinkOrdersResponse. linkOrders. |
| Update order shipping | POST | /commerce/v1/orders/{id}/update-shipping | Body: UpdateOrderShippingRequest. 200 — UpdateOrderShippingResponse. updateOrderShipping. |
| Cancel order | POST | /commerce/v1/orders/{id}/cancel | Body: CancelOrderRequest. 200 — CancelOrderResponse. cancelOrder. |
7. Hub (/hub/v1)
| Operation | Method | Path | Notes |
|---|---|---|---|
| List webhook event types | GET | /hub/v1/webhooks/event-types/search | Hub read scope. listEventTypesQuery. No X-Tenant-Id in bundled spec. |
| Create webhook subscription | POST | /hub/v1/webhooks/subscriptions/create | Body: CreateSubscriptionRequest (url, eventTypes, description, expiresAtUtc; optional notificationEmails). 201 — CreateSubscriptionResponse (includes signingSecret, notificationEmails). createSubscriptionCommand. |
| Search webhook subscriptions | GET | /hub/v1/webhooks/subscriptions/search | Query: limit, next, sortBy, sortOrder. X-Tenant-Id required. listSubscriptionsQuery. |
| Get webhook subscription | GET | /hub/v1/webhooks/subscriptions/{id}/get | Optional If-None-Match. X-Tenant-Id required. getSubscriptionQuery. |
| Update webhook subscription | PATCH | /hub/v1/webhooks/subscriptions/{id}/update | Body per OpenAPI PATCH. 200 — UpdateSubscriptionResponse. updateSubscriptionCommand. |
| Disable webhook subscription | POST | /hub/v1/webhooks/subscriptions/{id}/disable | 204. disableSubscriptionCommand. X-Tenant-Id required. |
| Search subs by event type | GET | /hub/v1/webhooks/event-types/{name}/subscriptions/search | Path name = event type id (e.g. order.created). X-Tenant-Id required. listSubscriptionsForEventType. |
| Test webhook subscription | POST | /hub/v1/webhooks/subscriptions/{id}/test | Triggers test delivery (202). X-Tenant-Id required. |
| List deliveries (subscription) | GET | /hub/v1/webhooks/subscriptions/{id}/deliveries | Paged delivery history; query status, from, to, etc. |
| Get delivery by id | GET | /hub/v1/webhooks/deliveries/{id}/get | Single delivery record. getDeliveryQuery. |
| Retry delivery | POST | /hub/v1/webhooks/deliveries/{id}/retry | Queues retry (202). X-Tenant-Id required. |
| Rotate subscription secret | POST | /hub/v1/webhooks/subscriptions/{id}/rotate-secret | Returns new signing secret. X-Tenant-Id required. |
All non-catalog endpoints require **Authorization: Bearer **** and **X-Tenant-Id unless the operation explicitly omits tenant scope (e.g. GET /hub/v1/webhooks/event-types/search per bundled spec).
10. Object reference
Types are grouped by the same service prefixes as §9 API Reference: Shared, Identity (/identity/v1), Catalog (/catalog/v1), Screening (/screening/v1), Consult (/consult/v1), Clinical (/clinical/v1), Commerce (/commerce/v1), Hub (/hub/v1). Shared types are reused across services. For fields omitted below, see CareAtlas-Unified-API-oas3-v0.1.json.
Shared types
Address
Used on commerce OrderImportRequest, practitioner onboard, and (as AddressInfo) on PatientResolveRequest. Patient update uses PatientAddressInfo (same required fields; optional label).
| Field | Type | Description |
|---|---|---|
| addressLine1 | string | Required. |
| addressLine2 | string | Required (can be empty string). |
| city | string | Required. |
| state | string | Required. |
| zipCode | string | Required. |
| phone | string | Required (e.g. 10 digits). |
| label | string | Optional on AddressInfo, PatientAddressInfo, and PractitionerAddressInfo (e.g. "Shipping Address" on resolve-by-email). |
Identity (/identity/v1)
MyTenantsResponse
200 from GET /identity/v1/tenants/me.
| Field | Type | Description |
|---|---|---|
| partnerApp | MyTenantsPartnerAppResponse | object | Partner application context; may be absent. |
MyTenantsPartnerAppResponse
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Partner application id — use as source.partnerAppId on order import. |
| appId | string | Application id (e.g. Cognito app client / portal key). |
| name | string | Display name of the partner application. |
| partner | MyTenantsPartnerResponse | Owning partner. |
| tenants | MyTenantsTenantResponse[] | Clinic/provider tenants linked to this app. |
| tenantProviders | MyTenantsTenantResponse[] | Provider/pharmacy tenants (e.g. role Pharmacy) for routing and UI. |
| attributes | MyTenantsAttributeResponse[] | Partner-app-level attributes (see OpenAPI). |
MyTenantsPartnerResponse
Nested under MyTenantsPartnerAppResponse.partner.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Partner id. |
| name | string | Partner name. |
| role | string | Partner role name. |
MyTenantsTenantResponse
Each element of tenants or tenantProviders.
| Field | Type | Description |
|---|---|---|
| tenantId | string (UUID) | Use as X-Tenant-Id when this row is selected. |
| name | string | Tenant display name. |
| role | string | Role name (e.g. provider, Pharmacy). |
| attributes | MyTenantsAttributeResponse[] | Attributes on the tenant row (see OpenAPI). |
MyTenantsAttributeResponse
Name/value pair used on MyTenantsTenantResponse and MyTenantsPartnerAppResponse.
| Field | Type | Description |
|---|---|---|
| name | string | Attribute name. |
| value | string | Attribute value. |
PatientResponse
Returned by patient search, resolve, and get-patient flows. Tenant context is X-Tenant-Id on the request, not a field on this object.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | customer.patientId. |
| account | PatientAccountResponse | Nested account; use account.id as customer.accountId. |
| firstName | string | |
| lastName | string | |
| dateOfBirth | string | |
| height | number | Height in inches (Identity column). |
| weight | number | Weight in lbs. |
| gender | string | "Male" or "Female" (per deployment). |
| string | Optional for billing. | |
| phoneNumber | string | |
| address | Address | Required for shipping/billing if not from payment. |
| etag | string | Concurrency token when supported. |
Removed from unified PatientResponse: top-level tenantId and accountId — use account.id instead of accountId.
OnboardClinicRequest
Request body for POST /identity/v1/tenants/clinics/onboard:
| Field | Type | Description |
|---|---|---|
| partnerId | string (UUID) | Partner UUID. |
| parentTenantId | string (UUID) | Parent tenant UUID. |
| name | string | Clinic/tenant name. |
| attributes | OnboardClinicAttributesRequest | Required. See below. |
attributes: name, npi, addressId (UUID), email, phone, description.
PractitionerOnboardRequest
Request body for POST /identity/v1/practitioners/onboard (create or find practitioner by NPI):
| Field | Type | Description |
|---|---|---|
| accountId | string (UUID) | Account UUID for the practitioner (from your identity/tenant context). |
| npi | string | National Provider Identifier (10 digits). |
| firstName | string | Required. |
| middleName | string | Required; use "" if none. |
| lastName | string | Required. |
| suffix | string | Required; use "" if none. |
| displayName | string | e.g. "Dr. Maria Smith". |
| providerTypeId | string (UUID) | Provider type from tenant config (e.g. physician, NP). |
| statusId | string (UUID) | Status from tenant config (e.g. active). |
| address | Address | Full address (addressLine1, addressLine2, city, state, zipCode, phone). |
| createIfMissing | boolean | Optional; default true. If true, creates the practitioner when not found. |
PractitionerResponse
Returned by GET /identity/v1/practitioners/search (each item in data) and in POST .../practitioners/onboard response:
| Field | Type | Description |
|---|---|---|
| id | string | Practitioner UUID; use as practitionerId in prescriptions. |
| tenantId | string | Tenant UUID. |
| accountId | string | Account UUID. |
| npi | string | National Provider Identifier. |
| firstName | string | |
| middleName | string | |
| lastName | string | |
| suffix | string | |
| displayName | string | |
| providerTypeId | string | |
| statusId | string | |
| inactivated | boolean | |
| string | ||
| emailVerified | boolean | |
| phoneNumber | string | |
| phoneNumberVerified | boolean | |
| address | Address | object |
| etag | string |
TenantResponse
Returned by POST /identity/v1/tenants/clinics/onboard and in clinic search results:
| Field | Type | Description |
|---|---|---|
| id | string | Tenant (clinic) UUID. |
| partnerId | string | Partner UUID. |
| parentTenantId | string | Parent tenant UUID. |
| name | string | Clinic/tenant name. |
| role | TenantRoleResponse | Tenant role. |
| etag | string | For conditional updates. |
| createdAtUtc, updatedAtUtc | string | Timestamps. |
| attributes | array | Tenant role attribute values (e.g. address, contact details). |
Use with address/attribute data to build ClinicInfo for order import.
Catalog (/catalog/v1)
Search and product payloads use GetProductResponse (informally called ProductResponse in this guide). Variants carry productVariantId and uomId for prescriptions and order lines.
GetProductResponse
Returned from GET /catalog/v1/products/search and GET /catalog/v1/products/{id}/find.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | productId. |
| name, sku, brand, description | strings | Display and catalog metadata. |
| variants | ProductVariantInfo[] | Use for variant id and uomId. |
| catalog | object (ProductCatalogInfo) | Nested catalog id/name/supplier. |
| etag | string | For conditional requests. |
ProductVariantInfo
Each variant maps to a prescribe-able SKU and order line.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | productVariantId for prescriptions and LineInfo. |
| uomId | string (UUID) | Required on commerce LineInfo when importing orders. |
| name | string | e.g. strength label. |
| variantSku | string | SKU. |
| attributes | array (AttributeResponse) | Tenant-defined attributes. |
| etag | string |
Screening (/screening/v1)
CreateSessionRequest
Body for POST /screening/v1/sessions/create.
| Field | Type | Description |
|---|---|---|
| channel | string | e.g. web. |
| patientId | string (UUID) | From Identity. |
| questionnaireId | string (UUID) | From active questionnaires list. |
| productId | string (UUID) | Catalog product. |
| productVariantId | string (UUID) | Catalog variant. |
CreateSessionResponse
201 response from session create; id is the screening session id for /next, /answer, /submit, etc.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Session id. |
| tenantId | string (UUID) | |
| patientId | string (UUID) | |
| questionnaireId | string (UUID) | |
| productId | string (UUID) | |
| productVariantId | string (UUID) | |
| startedAt | date-time | |
| expiresAtUtc | date-time | |
| etag | string |
ActiveQuestionnaireResponse
Rows from GET /screening/v1/questionnaires/active (each element describes an available questionnaire).
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Use as questionnaireId in CreateSessionRequest. |
| name, version | string | |
| treatmentId | string (UUID) | Care-path binding. |
| statusId | string (UUID) | |
| effectiveFromUtc, effectiveToUtc | date-time | Validity window. |
SessionSubmitRequest
Body for POST /screening/v1/sessions/{id}/submit.
| Field | Type | Description |
|---|---|---|
| validateOnly | boolean | Optional dry-run. |
SubmitSessionResponse
200 body after submit; use recommendedProductVariants when your clinical flow suggests variants.
| Field | Type | Description |
|---|---|---|
| status | string | Session outcome. |
| sessionId | string (UUID) | |
| questionnaireId | string (UUID) | |
| initiallySelectedProductId | string (UUID) | |
| initiallySelectedProductVariantId | string (UUID) | |
| completedAt | date-time | |
| recommendedProductVariants | array | RecommendedProductVariantInfo per spec. |
AnswerBundleRequest
Body for POST /screening/v1/sessions/{id}/answer.
| Field | Type | Description |
|---|---|---|
| answers | AnswerItemInfo[] | Each item: linkId, answer, optional evidenceRef. |
Consult (/consult/v1)
CreateConsultRequest
Body for POST /consult/v1/consults/create (createConsult).
| Field | Type | Description |
|---|---|---|
| patientId | string (UUID) | From Identity. |
| practitionerId | string (UUID) | From Identity practitioner search/onboard. |
| topic | string | Consult topic label. |
| channel | string | e.g. web. |
| screeningSessionId | string (UUID) | From screening after submit (§4). |
| selectedProductId | string (UUID) | Catalog productId. |
| selectedProductVariantId | string (UUID) | Catalog productVariantId. |
CreateConsultResponse
200 from create consult; use id as consult id on GET/messages routes.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Consult id. |
| providerTenantId | string (UUID) | |
| createdAtUtc | date-time | |
| updatedAtUtc | date-time |
GetConsultResponse
Returned by GET /consult/v1/consults/{id}/get.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | |
| status | string | Workflow status. |
| tenantId | string (UUID) | |
| patientId | string (UUID) | |
| practitionerId | string (UUID) | |
| screeningSessionId | string (UUID) | |
| selectedProductId | string (UUID) | |
| selectedProductVariantId | string (UUID) | |
| channel | string | |
| startedUtc, endedUtc | date-time | Lifecycle. |
| conversationRef | string | External conversation link when set. |
| participants | array | ConsultParticipantDto (see below). |
| chats | array | ConsultChatDto per spec. |
| etag | string |
ConsultParticipantDto
Each item in GetConsultResponse.participants (and AddParticipantResponse after POST …/participants/add).
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Consult participant row id; use as PostMessageRequest.participantId. |
| tenantId | string (UUID) | |
| patientId | string (UUID) | Identity patient on the consult (from the consult record). |
| practitionerId | string (UUID) | Identity practitioner registered on this participant row. |
| displayName | string | Display label for the participant. |
| joinedUtc | date-time | |
| leftUtc | date-time | Optional. When the participant left the thread. |
| etag | string |
The unified partner API does not expose roleId on this type. Role-based participant management (e.g. admin picklists) uses separate admin CRUD routes under /consult/v1/consults/{id}/participants, not participants/add.
AddParticipantRequest
Body for POST /consult/v1/consults/{id}/participants/add.
| Field | Type | Description |
|---|---|---|
| practitionerId | string (UUID) | Required. Practitioner to add to the consult chat. |
| displayName | string | Required. Shown name for this participant. |
The consult’s patientId is assigned on the created row by the API; it is not sent in the request.
AddParticipantResponse
201 from POST /consult/v1/consults/{id}/participants/add.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | New participant row id. |
| consultId | string (UUID) | |
| patientId | string (UUID) | Consult patient. |
| practitionerId | string (UUID) | Practitioner from the request. |
| displayName | string | |
| joinedUtc | date-time | |
| createdAtUtc | date-time | |
| updatedAtUtc | date-time |
CloseConsultResponse
200 from POST /consult/v1/consults/{id}/close.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Consult id. |
| endedUtc | date-time | When closed. |
| createdAtUtc | date-time | |
| updatedAtUtc | date-time |
CancelConsultResponse
200 from POST /consult/v1/consults/{id}/cancel.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Consult id. |
| canceledAtUtc | date-time | |
| canceledBy | string | Who canceled. |
| createdAtUtc | date-time | |
| updatedAtUtc | date-time |
PostMessageRequest
Body for POST /consult/v1/consults/{id}/messages/post.
| Field | Type | Description |
|---|---|---|
| content | string | Required. Message text. |
| participantId | string (UUID) | Optional. ConsultParticipantDto.id (participant row), not identity patient/practitioner uuid. Omit when no participant row exists; the API still accepts the message. |
| isMedia | boolean | Optional. |
UpdateMessageDeliveryRequest
Body for POST /consult/v1/consults/{id}/messages/{messageId}/update-delivery.
| Field | Type | Description |
|---|---|---|
| deliveredUtc | date-time | When the message was delivered to the recipient. |
| readUtc | date-time | When the message was read. |
Path {messageId} is the consult message id (from ConsultMessageResponse / search messages).
UpdateMessageDeliveryResponse
200 from POST /consult/v1/consults/{id}/messages/{messageId}/update-delivery.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Message id. |
| consultId | string (UUID) | |
| tenantId | string (UUID) | |
| deliveredUtc | date-time | |
| readUtc | date-time | |
| etag | string |
Clinical (/clinical/v1)
ValidatePrescriptionResponse
Returned by GET /clinical/v1/prescriptions/{id}/validate.
| Field | Type | Description |
|---|---|---|
| isValid | boolean | Whether the prescription passes validation per the API. |
CreatePrescriptionCommandRequest
Request body for POST /clinical/v1/prescriptions/create (OpenAPI: CreatePrescriptionRequest).
| Field | Type | Notes |
|---|---|---|
| consultId | string (UUID) | Consult id from §5 Step 2. Required for provider prescribing flows. |
| medsPrescribed | MedPrescribedInternal[] | Required. One row per prescribed line. |
| pharmacyId | string | Required. Pharmacy identifier (UUID in typical deployments). |
| pharmacyNote | string | Optional. |
| practitionerId | string (UUID) | Optional in schema; usually set from §6 Prerequisites — prescriptions (Ensure Practitioner exists). |
| practitionerFirstName | string | Optional. |
| practitionerLastName | string | Optional. |
| practitionerNpi | string | Optional. |
| practitionerPhone | string | Optional. |
| practitionerAddress | string | Optional. |
| practitionerCity | string | Optional. |
| practitionerState | string | Optional. |
| practitionerZip | string | Optional. |
| approvedAtUtc | string (date-time) | Optional. |
| approvedBy | string | Optional. |
| paymentAmount | object (double format per spec) | Optional. |
| paymentLink | string | Optional. |
| paymentLinkSentAt | string (date-time) | Optional. |
| comments | string | Optional. |
SubmitPaymentRequest
Body for POST /commerce/v1/payments/submit (submitPayment). Records a payment against an order entity in Commerce.
| Field | Type | Description |
|---|---|---|
| entityName | string | Entity type label (per your commerce integration). |
| entityId | string | Entity UUID. |
| orderId | string | Order UUID. |
| provider | string | Payment provider (e.g. stripe). |
| intentId | string | Provider payment intent id. |
| status | string | Payment status string. |
| amount | number | Amount (OpenAPI may export as object/double). |
| currencyISO | string | ISO currency code (e.g. USD). |
SubmitPaymentResponse
200 from submit payment — includes id, tenantId, intentId, status, amount, etag, and related fields mirroring the request.
MedPrescribedInternal
One element of medsPrescribed in CreatePrescriptionCommandRequest.
| Field | Type | Description |
|---|---|---|
| productId | string (UUID) | Required. Product UUID. |
| productVariantId | string (UUID) | Required. Variant UUID. |
| strength | string | Required. e.g. "10mg". |
| frequency | string | Required. e.g. "once daily". |
| route | string | Required. e.g. "oral". |
UpdatePrescriptionCommandRequest
Request body for PATCH /clinical/v1/prescriptions/{id}/update. All fields are optional in the bundled schema.
| Field | Type | Description |
|---|---|---|
| approvedAtUtc | string (date-time) | Optional. |
| approvedBy | string | Optional. |
| pharmacyNote | string | Optional. |
| comments | string | Optional. |
| paymentAmount | object (double format) | Optional. |
| paymentLink | string | Optional. |
| paymentLinkSentAt | string (date-time) | Optional. |
PrescriptionResponse
Relevant fields for building the order:
| Field | Type | Description |
|---|---|---|
| id | string | Used as matchedPrescriptionId. |
| patientId | string | Used as customer.patientId. |
| productId | string | Used as lines[].productId. |
| productVariantId | string | Used as lines[].productVariantId (or resolve from product). |
| quantityAuthorized | object | Used for lines[].qty (e.g. .value or default 1). |
Commerce (/commerce/v1)
ClinicInfo
Required by the API schema for OrderImportRequest.
| Field | Type | Description |
|---|---|---|
| clinicName | string | |
| clinicAddressLine1 | string | |
| clinicAddressLine2 | string | |
| clinicCity | string | |
| clinicState | string | |
| clinicZip | string | |
| clinicEmail | string | |
| clinicPhoneNumber | string |
If your backend accepts orders without clinic, you may still need to send a minimal object; confirm with the API or backend team.
CustomerInfo
| Field | Type | Description |
|---|---|---|
| patientId | string | Patient UUID. |
| accountId | string | Account UUID (from patient or profile/session). |
LineInfo
| Field | Type | Description |
|---|---|---|
| externalLineId | string | Unique line id (e.g. LINE-<ts> or same as externalOrderId for single line). |
| productId | string | Product UUID. |
| productVariantId | string | Product variant UUID. |
| uomId | string | Required by API. Unit of measure UUID. This app does not use it for logic; it sends the variant's uomId or a default. |
| qty | Record<string, any> or string | Quantity (API may expect string). |
| unitPrice | Record<string, any> or string | Unit price. |
| lineTotal | Record<string, any> or string | Line total. |
| priceComponents | array | e.g. []. |
| requiresPrescription | boolean | Typically true for prescription-based orders. |
| matchedPrescriptionId | string | Prescription UUID. |
OrderImportRequest
Full request body for POST /commerce/v1/orders/import. The API schema marks these as required: externalOrderId, customer, clinic, currencyISO, pricing, lines, promos, totals, shippingAddress, billingAddress, payment, validatedAtUtc, source, notes.
| Field | Type | Description |
|---|---|---|
| externalOrderId | string | Unique external order id (e.g. ORDER-<ts> or Stripe payment intent id). |
| customer | CustomerInfo | Patient and account identifiers. |
| clinic | ClinicInfo | Required by schema. Clinic name, full address, email, phone. |
| currencyISO | string | e.g. "USD". |
| pricing | object / PricingInfo | e.g. { priceBookVersion: "1.0", components: [] }. |
| lines | LineInfo[] | Order lines; each can reference a prescription via matchedPrescriptionId. |
| promos | string[] | Promo codes; use [] if none. |
| totals | TotalsInfo | subtotal, taxTotal, shippingTotal, grandTotal. |
| shippingAddress | Address | Full shipping address. |
| billingAddress | Address | object |
| payment | PaymentInfo | object |
| validatedAtUtc | string | ISO 8601 date-time. |
| source | SourceInfo | object |
| notes | string | Free text. |
OrderResponse
Returned by POST /commerce/v1/orders/import:
| Field | Type | Description |
|---|---|---|
| id | string | Order UUID. |
| orderNumber | string | Human-readable order number. |
| status | string | Order status. |
| patientId | string | |
| lines | OrderLineResponse[] | Order lines. |
| subtotal, taxTotal, shippingTotal, grandTotal | various | |
| payment | object | |
| shippingAddress, billingAddress | object |
PaymentInfo
| Field | Type | Description |
|---|---|---|
| paymentId | string | Internal payment UUID. |
| externalReference | string | e.g. Stripe session id. |
| status | string | e.g. "paid", "pending". |
Extended payment payload (as used after Stripe) may include intentId, provider (e.g. "Stripe").
SourceInfo
| Field | Type | Description |
|---|---|---|
| partnerAppId | string | From GET /identity/v1/tenants/me → partnerApp.id. |
| channel | string | e.g. "web". |
| region | string | e.g. "US". |
TotalsInfo
| Field | Type | Description |
|---|---|---|
| subtotal | Record<string, any> or string | Subtotal amount. |
| taxTotal | Record<string, any> or string | Tax total. |
| shippingTotal | Record<string, any> or string | Shipping total. |
| grandTotal | Record<string, any> or string | Grand total. |
In this app, totals are often sent as strings (e.g. "0", "12.99") for decimal precision.
Hub (/hub/v1)
Webhook subscription and delivery types for §8 Working with Webhooks. Requests use Hub Cognito scopes (api://haas.hub/haas.api.read / …write).
CreateSubscriptionRequest
Body for POST /hub/v1/webhooks/subscriptions/create.
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | yes | HTTPS endpoint for deliveries. |
| eventTypes | string[] | yes | e.g. order.created (per event catalog). |
| description | string | yes | Human-readable label. |
| expiresAtUtc | date-time | yes | Subscription expiry (ISO-8601 UTC). |
| notificationEmails | string | no | Optional alert recipients for subscription events. |
CreateSubscriptionResponse
201 — treat signingSecret as sensitive; persist it to verify signatures on inbound webhooks.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Subscription id. |
| partnerAppId | string (UUID) | |
| url | string | Callback URL. |
| eventTypes | string[] | |
| isActive | boolean | |
| description | string | |
| expiresAtUtc | date-time | |
| notificationEmails | string | Alert recipients when configured. |
| etag | string | |
| signingSecret | string | HMAC / verification secret. |
UpdateSubscriptionResponse
200 from PATCH /hub/v1/webhooks/subscriptions/{id}/update — same fields as create response except signingSecret (use rotate-secret to obtain a new secret).
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | |
| partnerAppId | string (UUID) | |
| url | string | |
| eventTypes | string[] | |
| isActive | boolean | |
| description | string | |
| expiresAtUtc | date-time | |
| notificationEmails | string | |
| etag | string |
GetDeliveryResponse
Returned by GET /hub/v1/webhooks/deliveries/{id}/get for observability and debugging.
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Delivery attempt id. |
| subscriptionId | string (UUID) | |
| sourceMessageId | string (UUID) | Hub message id. |
| eventType | string | |
| status | string | Delivery pipeline status. |
| statusCode | integer | HTTP status from your endpoint (if applicable). |
| error | string | Failure detail. |
| attempt | integer | Retry count. |
| nextRetryAtUtc | date-time | Scheduled retry. |
| traceId | string | Correlate with Hub logs. |
| etag | string |
11. Troubleshooting
- 401 Unauthorized – Check Cognito token and scope (
api://haas.commerce/haas.api.writefor import). - 403 Forbidden – Verify X-Tenant-Id and that the token is allowed for that tenant.
- 400 Bad Request – Validate OrderImportRequest: required fields (including
clinic), address fields (addressLine2 must be present, can be""), and numeric/string formats for qty, unitPrice, lineTotal, totals. - Missing accountId – Ensure patient has
accountIdor resolve it via your identity/profile API before calling order import. - Patient does not have address – Order import requires full shipping and billing addresses; create or update the patient’s address via Identity APIs first.
- Duplicate orders – Use Idempotency-Key (e.g. prescription id + payment id) on
POST /commerce/v1/orders/importwhen retrying or handling webhooks.
For payload examples and mapping from prescription to order, see Build OrderImportRequest and call Import Order and the sample request there.