Add Products to Case
/api/v1/cases/:caseId/productsAttaches one or more products to an existing case. Supports an optional top-level payment that covers all products in the request. Each product can also include an optional intake form and subscription configuration.
https://api.care360-next.carevalidate.com/api/v1/cases/:caseId/productshttps://api-staging.care360-next.carevalidate.com/api/v1/cases/:caseId/productsParameters
cv-api-keystringrequiredContent-Typestringrequiredapplication/jsoncaseIdstring (UUID)requiredpaymentobjectoptionalShow 4 child properties
amountnumberrequiredcurrencystringrequiredUSDdescriptionstringoptionalproviderReferenceobjectoptionalShow 2 child properties
typestringrequiredidstringrequiredproductsarrayrequiredShow 4 child properties
idstring (UUID)requiredvisitTypestringoptionalformobjectoptionalShow 3 child properties
titlestringoptionaldescriptionstringoptionalquestionsarrayoptionalsubscriptionobjectoptionalShow 2 child properties
intervalstringoptionalintervalCountintegeroptionalForm Question Fields
questionstringrequiredanswerstringrequiredtypestringrequiredrequiredbooleanoptionalphibooleanoptionaloptionsarrayoptionalPayment Status Lifecycle
When a PAYMENT_INTENT is still pending at attach time (the BNPL pre-redirect flow), the product and its payment move through the following states as Stripe webhook events arrive. No polling is required — the case product's status/reason and the linked payment's status update automatically.
| Event | Product status | Product reason | Payment status |
|---|---|---|---|
Pending PAYMENT_INTENT attached | CLOSE | Pending payment | UNPAID |
payment_intent.amount_capturable_updated (BNPL hold acquired) | OPEN | null | UNPAID (capture happens later) |
payment_intent.succeeded (charge captured) | OPEN | null | PAID |
payment_intent.payment_failed | CLOSE | Payment failed | ERROR |
If a PaymentIntent fails, only the product(s) and payment tied to it are closed/errored — the case itself remains OPEN. To retry, re-add the same product with a new PAYMENT_INTENT; it starts a fresh pending-payment cycle independent of the failed one.
Examples
- cURL (no payment)
- cURL (single payment, multi-product)
- cURL (BNPL pre-redirect)
- JavaScript
- Python
curl -X POST "https://api.care360-next.carevalidate.com/api/v1/cases/550e8400-e29b-41d4-a716-446655440000/products" \
-H "cv-api-key: <redacted>" \
-H "Content-Type: application/json" \
-d '{
"products": [
{
"id": "8dff2c47-b2ab-42d8-9af0-4c4dbca8b4b6",
"visitType": "SYNC_VIDEO",
"subscription": {
"interval": "month",
"intervalCount": 3
}
},
{
"id": "621527f5-3877-48b3-b27f-df395a5308bb",
"form": {
"title": "Healthcare Intake Form",
"description": "Patient intake form for healthcare services",
"questions": [
{
"question": "How much do you weigh?",
"answer": "138 lbs",
"phi": true,
"type": "TEXT"
},
{
"question": "What are your weight loss goals?",
"answer": "Lose 1-20lbs for good",
"type": "SINGLESELECT",
"required": true,
"options": [
"Lose 1-20lbs for good",
"Lose 21-50lbs for good",
"Lose over 50 for good",
"Maintain my healthy weight"
]
}
]
}
}
]
}'
curl -X POST "https://api.care360-next.carevalidate.com/api/v1/cases/550e8400-e29b-41d4-a716-446655440000/products" \
-H "cv-api-key: <redacted>" \
-H "Content-Type: application/json" \
-d '{
"payment": {
"amount": 79.98,
"currency": "USD",
"description": "Bundle upsell",
"providerReference": {
"type": "PAYMENT_INTENT",
"id": "pi_3Qx1AbGkkQS2eXzh1Kj9mN2c"
}
},
"products": [
{ "id": "8dff2c47-b2ab-42d8-9af0-4c4dbca8b4b6" },
{ "id": "621527f5-3877-48b3-b27f-df395a5308bb" }
]
}'
# Call this BEFORE redirecting the patient to the BNPL provider.
# Pass the pending PaymentIntent — products attach as CLOSE (inactive)
# and are automatically reopened when payment_intent.succeeded fires.
curl -X POST "https://api.care360-next.carevalidate.com/api/v1/cases/550e8400-e29b-41d4-a716-446655440000/products" \
-H "cv-api-key: <redacted>" \
-H "Content-Type: application/json" \
-d '{
"payment": {
"amount": 79.98,
"currency": "USD",
"description": "BNPL upsell",
"providerReference": {
"type": "PAYMENT_INTENT",
"id": "pi_3Qx1AbGkkQS2eXzh1Kj9mN2c"
}
},
"products": [
{ "id": "8dff2c47-b2ab-42d8-9af0-4c4dbca8b4b6" },
{ "id": "621527f5-3877-48b3-b27f-df395a5308bb" }
]
}'
const caseId = "550e8400-e29b-41d4-a716-446655440000";
const response = await fetch(
`https://api.care360-next.carevalidate.com/api/v1/cases/${caseId}/products`,
{
method: "POST",
headers: {
"cv-api-key": "YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
products: [
{
id: "8dff2c47-b2ab-42d8-9af0-4c4dbca8b4b6",
visitType: "SYNC_VIDEO",
subscription: {
interval: "month",
intervalCount: 3,
},
},
{
id: "621527f5-3877-48b3-b27f-df395a5308bb",
form: {
title: "Healthcare Intake Form",
description: "Patient intake form for healthcare services",
questions: [
{
question: "How much do you weigh?",
answer: "138 lbs",
phi: true,
type: "TEXT",
},
{
question: "What are your weight loss goals?",
answer: "Lose 1-20lbs for good",
type: "SINGLESELECT",
required: true,
options: [
"Lose 1-20lbs for good",
"Lose 21-50lbs for good",
"Lose over 50 for good",
"Maintain my healthy weight",
],
},
],
},
},
],
}),
}
);
const data = await response.json();
console.log(data);
import requests
case_id = "550e8400-e29b-41d4-a716-446655440000"
response = requests.post(
f"https://api.care360-next.carevalidate.com/api/v1/cases/{case_id}/products",
headers={
"cv-api-key": "YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
json={
"products": [
{
"id": "8dff2c47-b2ab-42d8-9af0-4c4dbca8b4b6",
"visitType": "SYNC_VIDEO",
"subscription": {
"interval": "month",
"intervalCount": 3,
},
},
{
"id": "621527f5-3877-48b3-b27f-df395a5308bb",
"form": {
"title": "Healthcare Intake Form",
"description": "Patient intake form for healthcare services",
"questions": [
{
"question": "How much do you weigh?",
"answer": "138 lbs",
"phi": True,
"type": "TEXT",
},
{
"question": "What are your weight loss goals?",
"answer": "Lose 1-20lbs for good",
"type": "SINGLESELECT",
"required": True,
"options": [
"Lose 1-20lbs for good",
"Lose 21-50lbs for good",
"Lose over 50 for good",
"Maintain my healthy weight",
],
},
],
},
},
],
},
)
print(response.json())
Responses
▶200SuccessProducts successfully attached to the case.
{
"success": true,
"data": {
"caseId": "550e8400-e29b-41d4-a716-446655440000"
}
}
▶400Bad RequestInvalid request body or parameters. Also returned when a pending or already-charged `PAYMENT_INTENT` is provided without an `amount`.
{
"success": false,
"message": "Invalid request"
}
▶401UnauthorizedMissing or invalid authentication token.
{
"success": false,
"message": "Unauthorized"
}
▶409ConflictCase conflict (e.g., duplicate product attachment).
{
"success": false,
"message": "Case conflict"
}
▶422Validation FailedRequest body failed validation rules.
{
"success": false,
"message": "Validation failed"
}
▶500Internal Server ErrorUnexpected server error.
{
"success": false,
"message": "Internal server error"
}
Try It Out
API Version: v2.2