Product configs
Product configs define the packages and memberships you sell. List them to build checkout pickers; create or update them from your own provisioning tools instead of Admin → Product Configs.
List configs
Section titled “List configs” GET
/api/v2/product-configs Requires the summary scope. Pass location_id as a query parameter. Optional ?active=true returns only active configs.
curl "https://app.<your-domain>/api/v2/product-configs?location_id=loc_1" \ -H "Authorization: Bearer ktly_<your-token>"Response (200)
Section titled “Response (200)”{ "ok": true, "reason_code": "product_configs_loaded", "correlation_id": "a1b2c3d4-...", "product_configs": [ { "id": "pc_package_1", "name": "10-Class Pack", "type": "package", "credits_total": 10, "expiry_days": 90, "monthly_credits": null, "active": true, "customer_purchasable": true, "ghl_product_id": null, "calendar_id": null, "service_id": null, "stripe_price_id": null, "price_amount": null, "currency": null } ]}Create a config
Section titled “Create a config” POST
/api/v2/product-configs Requires the manage scope. Idempotent via request_id. Validation mirrors Admin → Product Configs — reject nonsense combinations (membership with credits_total, package without credits, etc.).
Request body
Section titled “Request body”| Field | Type | Required | Notes |
|---|---|---|---|
location_id | string | yes | Must match the API client’s location |
request_id | string | yes | Idempotency key |
type | string | yes | package or membership |
name | string | yes | Display name |
credits_total | integer | package | Positive integer — required for package |
expiry_days | integer | no | Optional positive integer — package expiry |
monthly_credits | integer | membership | Positive integer — required for membership |
active | boolean | no | Default true |
customer_purchasable | boolean | no | Default false |
ghl_product_id | string | no | Optional GHL product id when connected |
calendar_id | string | no | Booking family matching |
service_id | string | no | Booking family matching |
stripe_price_id | string | no | Optional Stripe price id |
price_amount | integer | no | Display price in cents |
currency | string | no | e.g. "USD" |
curl -X POST "https://app.<your-domain>/api/v2/product-configs" \ -H "Authorization: Bearer ktly_<your-token>" \ -H "Content-Type: application/json" \ -d '{ "location_id": "loc_1", "request_id": "pc-create-10pack", "type": "package", "name": "10-Class Pack", "credits_total": 10, "expiry_days": 90, "customer_purchasable": true }'Response (200)
Section titled “Response (200)”{ "ok": true, "reason_code": "product_config_created", "correlation_id": "a1b2c3d4-...", "product_config": { "id": "pc_new_uuid", "name": "10-Class Pack", "type": "package", "credits_total": 10, "expiry_days": 90, "active": true, "customer_purchasable": true }}Update a config
Section titled “Update a config” PATCH
/api/v2/product-configs/{id} Requires the manage scope. Pass location_id as a query parameter. Only active, name, and customer_purchasable can be changed — type and credit rules are fixed at create time.
Request body
Section titled “Request body”| Field | Type | Required | Notes |
|---|---|---|---|
active | boolean | no | Enable or disable the config |
name | string | no | Non-empty display name |
customer_purchasable | boolean | no | Show in customer-facing pickers |
curl -X PATCH "https://app.<your-domain>/api/v2/product-configs/pc_package_1?location_id=loc_1" \ -H "Authorization: Bearer ktly_<your-token>" \ -H "Content-Type: application/json" \ -d '{ "active": false, "customer_purchasable": false }'Response (200)
Section titled “Response (200)”{ "ok": true, "reason_code": "product_config_updated", "correlation_id": "a1b2c3d4-...", "product_config": { "id": "pc_package_1", "name": "10-Class Pack", "type": "package", "active": false, "customer_purchasable": false }}Errors
Section titled “Errors”| Status | When |
|---|---|
400 | Validation error (missing required field, invalid type combination) |
403 | BILLING_SUSPENDED — workspace billing is suspended |
404 | Config not found for this location (PATCH only) |
See the Overview for the full error model.