Skip to main content

Holistic Care Workflow — Case Products API Guide

Audience: New clients integrating with the CareValidate platform and existing clients migrating from the legacy Dynamic Case API to the Case Products model.


Overview

The Case Products API is the recommended way to manage patient cases on the CareValidate platform. Unlike the legacy Dynamic Case API which ties one treatment to one case, the Case Products model lets you manage multiple treatments under a single case with independent lifecycles.

This enables you to:

  • Create a case for a patient with one or more products (treatments) in a single request.
  • Attach additional products to an existing case over time.
  • Close or reopen individual products on a case independently.
  • Receive real-time webhook notifications for every case-product lifecycle event.

Each product on a case can carry its own intake form, visit type, and subscription configuration, giving you granular control over multi-treatment workflows (e.g., a patient on both a weight-loss and a hair-loss program under one case).


Key Concepts

ConceptDescription
User (Patient)The person receiving care. Identified by a unique email address.
CaseA container representing a patient's care journey. A patient can have multiple cases. Each case has a status (OPEN → ASSIGNED → IN_PROGRESS → APPROVED / REJECTED / NO_DECISION).
ProductA treatment or service offered by your organization (e.g., "Semaglutide", "Finasteride"). Products are pre-configured in your organization settings.
Case ProductThe join between a Case and a Product. Tracks status (OPEN / CLOSE), visit type, subscription info, and an optional closure reason.
Case Product RequestA lifecycle record under a Case Product. Tracks the request through PENDING → APPROVED/REJECTED → ORDERED → SHIPPED → DELIVERED.
Form / Form ResponseAn intake questionnaire attached to a case product. Contains typed questions (TEXT, SINGLESELECT, MULTISELECT) with optional PHI flags.
SubscriptionOptional recurring billing configuration on a case product (interval + count).
WebhookAn HTTP callback your system registers to receive real-time event notifications (e.g., product added, closed, reopened).

Entity Relationships

User (Patient)
└── Case
├── CaseProduct ←→ OrganizationProduct
│ ├── CaseProductRequest
│ │ ├── FormResponse
│ │ └── CaseDecision
│ └── Subscription (interval, intervalCount)
├── Payment
└── CaseActivity (audit log → webhook events)
  • A User can have multiple Cases.
  • A Case can have multiple CaseProducts (one per unique product).
  • Each CaseProduct can have multiple CaseProductRequests tracking the treatment lifecycle.
  • FormResponses are attached at the CaseProductRequest level.

End-to-End Workflow

The Case Products lifecycle follows four core operations. Each operation triggers a corresponding webhook event.

1. Create a Case with Products

When a new patient enrolls, you create a case with one or more products in a single call to the Create Case endpoint. Each product can include its own intake form, visit type, and subscription configuration.

For example, a patient enrolling in a weight-loss program would have a case created with a Semaglutide product attached, including their intake form responses and a quarterly subscription.

Webhook triggered: ADD_CASE_PRODUCT (one per product) with status OPEN.

2. Add Products to an Existing Case

If a patient later needs additional treatments, you attach new products to their existing case using the Add Products to Case endpoint. This avoids creating a separate case for each treatment.

For example, the same weight-loss patient now wants to add a hair-loss treatment — you add a Finasteride product to their existing case with its own intake form and visit type.

  • A case product is unique per case and organization product — you cannot add the same product to a case twice.
  • Adding a product triggers an ADD_CASE_PRODUCT webhook event for each product added.

3. Close a Product

When a treatment is complete, denied, or no longer needed, you close that specific product using the Update Case Product endpoint with status CLOSE. The rest of the case and its other products remain unaffected.

A closure reason is optional but strongly recommended for the audit trail (e.g., "Treatment complete", "Patient ineligible — contraindication identified").

Webhook triggered: CLOSE_CASE_PRODUCT with the closure reason included in the payload.

4. Reopen a Previously Closed Product

If circumstances change (e.g., after further review a patient is found eligible), you can reopen a closed product by setting its status back to OPEN via the same Update Case Product endpoint. The closure reason is cleared on reopen.

Webhook triggered: OPEN_CASE_PRODUCT with status OPEN.

Workflow Summary

StepAPI EndpointWebhook Event
Create case + productCreate CaseADD_CASE_PRODUCT
Add product to caseAdd Products to CaseADD_CASE_PRODUCT
Close a productUpdate Case Product (status: CLOSE)CLOSE_CASE_PRODUCT
Reopen a productUpdate Case Product (status: OPEN)OPEN_CASE_PRODUCT

Webhook Events

When you register a webhook URL for your organization, the platform sends HTTP POST requests to your endpoint for every case-product lifecycle event. All three events (ADD_CASE_PRODUCT, CLOSE_CASE_PRODUCT, OPEN_CASE_PRODUCT) share a common payload structure containing:

  • caseProduct — The case product's ID, status (OPEN / CLOSE), closure reason, visit type, and creation timestamp.
  • product — The organization product's ID and name.
  • case — Full case details including status, submitter (patient) information, and organization ID.
  • activity — An audit record with the event type, timestamp, actor (who triggered the event), and before/after values.
EventWhen it firesKey payload values
ADD_CASE_PRODUCTProduct added to a case (during creation or via Add Products)status: OPEN, valueAfter: product name
CLOSE_CASE_PRODUCTProduct closed on a casestatus: CLOSE, reason: closure reason, valueAfter: "<ProductName> - <Reason>"
OPEN_CASE_PRODUCTPreviously closed product reopenedstatus: OPEN, reason: null, valueAfter: product name

Migration Guide (from Dynamic Case API)

If you are currently using the legacy Dynamic Case creation flow (single case per request without explicit product management), this section outlines the key differences and recommended migration steps.

Key Differences

AspectLegacy Dynamic Case APICase Products API
ProductsImplicit — one treatment per caseExplicit products array — multiple treatments per case
FormsAttached at the case levelAttached per product via products[].form
SubscriptionsManaged separatelyInline per product via products[].subscription
Closing treatmentsArchive the entire caseClose individual products while keeping the case open
WebhooksCREATE_CASE, ARCHIVE_CASEADD_CASE_PRODUCT, CLOSE_CASE_PRODUCT, OPEN_CASE_PRODUCT
GranularityCase-level lifecyclePer-product lifecycle within a case

What Changes in the Request

The legacy /dynamic-case endpoint accepts patient info, payment, and form questions at the top level:

{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"paymentAmount": 159,
"stripeSetupId": "seti_1Example123",
"productBundleId": "<your-product-bundle-id>",
"questions": [...]
}

The Case Products API moves product-specific data (forms, subscriptions, visit type) into a products array, and patient info into a user object:

{
"user": {
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phoneNumber": "+1987654321",
"dob": "1990-05-15"
},
"payment": {
"amount": 159,
"currency": "USD",
"providerReference": {
"type": "SETUP_INTENT",
"id": "seti_1Example123"
}
},
"products": [
{
"id": "<organization-product-uuid>",
"visitType": "ASYNC_TEXT_EMAIL",
"subscription": { "interval": "month", "intervalCount": 3 },
"form": {
"title": "Intake Form",
"questions": [...]
}
}
]
}

Key structural changes:

  • Patient info moves from root-level fields into a user object, with shippingAddress nested inside.
  • Payment moves from flat fields (paymentAmount, stripeSetupId) into a structured payment object with providerReference.
  • Product bundle is replaced by an explicit products array where each product has its own id, form, subscription, and visitType.
  • Questions move from a case-level questions array into products[].form.questions.

What Changes in Lifecycle Management

With the legacy API, closing a treatment means archiving the entire case. With the Case Products API, you close individual products while keeping the case open for other active treatments.

Legacy ActionCase Products Equivalent
Archive case to end treatmentClose a specific product via Update Case Product with status CLOSE
Reopen archived caseReopen a specific product with status OPEN
Create new case for additional treatmentAdd product to existing case via Add Products to Case

What Changes in Webhooks

Legacy WebhookCase Products WebhookNotes
CREATE_CASEADD_CASE_PRODUCTFires per product, not per case
ARCHIVE_CASECLOSE_CASE_PRODUCTFires per product closure, not whole-case archive
(no equivalent)OPEN_CASE_PRODUCTNew event for reopening individual products

The webhook payload structure also changes — case-product webhooks include caseProduct and product objects alongside the existing case and activity data.

  1. Inventory your current integrations. Identify every call to the legacy case creation endpoint and note which product/treatment each call represents.

  2. Map products. Ensure each treatment you offer is configured as an Organization Product in your CareValidate dashboard. Note the product UUIDs.

  3. Update case creation calls. Replace legacy case creation with the Create Case endpoint including the products array. Move form data from the case level into products[].form.

  4. Update webhook handlers. Register for the new case-product webhook events (ADD_CASE_PRODUCT, CLOSE_CASE_PRODUCT, OPEN_CASE_PRODUCT). Update your event processing logic to handle per-product payloads.

  5. Adopt per-product lifecycle management. Replace case-level archive/reopen flows with product-level close/reopen via Update Case Product.

  6. Test in staging. Use isTest: true and the staging environment to validate the full workflow before switching production traffic.

  7. Cutover. Once staging validation is complete, update your production integration to use the Case Products endpoints.


Appendix: Enums Quick Reference

Case Status

OPENASSIGNEDIN_PROGRESSAPPROVED / REJECTED / NO_DECISION

Case Product Status

OPEN | CLOSE

Case Product Request Status

PENDINGAPPROVED / REJECTED / NO_DECISIONORDEREDSHIPPEDDELIVERED

Other values: RE_ORDERED, SUPERSEDED, REFILL_PENDING

Visit Type

ASYNC_TEXT_EMAIL | SYNC_IN_PERSON | SYNC_PHONE | SYNC_VIDEO | ORDER_FORM | NO_SHOW

Payment Provider Reference Type

SETUP_INTENT | PAYMENT_INTENT | PAYMENT_TOKEN

Per-product payments only accept PAYMENT_INTENT. Global (case-level) payments accept all three types.

Subscription Interval

day | week | month | year (used with intervalCount — e.g., month + 3 = quarterly)