Bulk Replace Alternate Purchase Paths
Replace alternate purchase paths across content items. Partial success; default cap 500.
Replaces the full set of alternate purchase paths on each listed content item. Each
item runs in its own transaction, so one invalid course is reported in failed
without rolling back the rest.
An item whose entries array is empty clears every live path on that course.
Omitting entries, sending null, or sending a non-array is a per-item failure —
it does not silently clear.
A content item may appear at most once per request. Duplicate contentIds reject the
whole payload with 400 before any item is written.
https://example.thoughtindustries.com/v3/admin/content/alternate-purchase-paths/bulkThis endpoint is served by the v3 API. Its full path is
https://{instance}.thoughtindustries.com/v3/admin/content/alternate-purchase-paths/bulk, not the/incoming/v2base URL used by the rest of this reference. Use the full URL shown in the examples below.
Example request
curl -X PUT "https://{instance}.thoughtindustries.com/v3/admin/content/alternate-purchase-paths/bulk" \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"items": [
{
"contentId": "3a131ac3-1a74-420d-b4da-ae10b18b2c68",
"entries": [
{
"label": "Subscribe for unlimited access",
"targetType": "ti_item",
"targetId": "7a6b5c4d-3e2f-1a0b-9c8d-7e6f5a4b3c2d"
}
]
},
{
"contentId": "9c8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f",
"entries": []
}
]
}'Parameters
Requires the settings.alternatePurchasePaths permission. A company API key satisfies this.
| Name | Type | Required | Location | Description |
|---|---|---|---|---|
items | object[] | Yes | body | Per-course replacement sets. Default cap 500 items (ALTERNATE_PURCHASE_PATH_BULK_MAX). |
items[].contentId | uuid | Yes | body | Source course UUID. Must be unique within the request. |
items[].entries | object[] | Yes | body | Full replacement set. [] clears. Each object uses the same fields as Create. |
The global ValidationPipe does not deeply validate each entry. Per-item errors
land in failed[].error as a semicolon-joined string (the same static and
purchasability checks as create). Up to 20 items are processed concurrently
(ALTERNATE_PURCHASE_PATH_BULK_CONCURRENCY).
Example response
Note the data wrapper — v3 endpoints nest their payload under data.
{
"data": {
"updated": [
{
"contentId": "3a131ac3-1a74-420d-b4da-ae10b18b2c68",
"entries": [
{
"id": "443d8182-d461-4568-a777-918ca729fd85",
"courseId": "3a131ac3-1a74-420d-b4da-ae10b18b2c68",
"displayOrder": 0,
"label": "Subscribe for unlimited access",
"targetType": "ti_item",
"targetId": "7a6b5c4d-3e2f-1a0b-9c8d-7e6f5a4b3c2d",
"targetUrl": null,
"targetUrlTitle": null,
"hidePrimaryCta": false,
"createdAt": "2026-09-15T15:10:00.000Z",
"updatedAt": "2026-09-15T15:10:00.000Z"
}
]
},
{
"contentId": "9c8d7e6f-5a4b-3c2d-1e0f-9a8b7c6d5e4f",
"entries": []
}
],
"failed": []
}
}A typical per-item failure (the rest of the batch still commits):
{
"data": {
"updated": [],
"failed": [
{
"contentId": "3a131ac3-1a74-420d-b4da-ae10b18b2c68",
"error": "contentId does not name a content item in this company"
}
]
}
}Other failed[].error strings you will see:
contentId must be a valid UUIDentries is required and must be an array (use [] to clear)label is requiredtargetType must be one of: ti_item, external_urltargetId is required when targetType is ti_itemtargetUrl is required when targetType is external_urltargetUrlTitle is required when targetType is external_urltargetId {uuid} does not resolve to a purchasable item (an active, priced course, learning path, subscription, or collection)
A missing contentId is reported as contentId: "(missing)".
Errors
More than 500 items (or the configured cap) rejects the whole request with 413:
{
"code": "BULK_PAYLOAD_TOO_LARGE",
"message": "Bulk update accepts at most 500 content items per request (received 501)",
"category": "VALIDATION",
"timestamp": "2026-09-15T15:10:00.000Z",
"statusCode": 413,
"metadata": {
"limit": 500,
"received": 501
}
}Duplicate contentIds reject the whole request with 400:
{
"code": "VALIDATION_ERROR",
"message": "Duplicate contentId in bulk payload",
"category": "VALIDATION",
"timestamp": "2026-09-15T15:10:00.000Z",
"statusCode": 400,
"metadata": {
"errors": [
"contentId 3a131ac3-1a74-420d-b4da-ae10b18b2c68 appears more than once; each may appear at most once"
]
}
}A non-array items value or an unrecognized top-level property is rejected by
ValidationPipe as 400 with code: "BAD_REQUEST" and category: "CLIENT_ERROR".
message names the field. timestamp, statusCode, and metadata.errors are
present on the same body; see
Create alternate purchase path
for the shape.
A missing or invalid API key returns 401. A manager session without
settings.alternatePurchasePaths returns 403.
Rate limit
20 requests per 60 seconds, counted per instance. This limit is specific to this endpoint
and is independent of the per-family rate limits that apply to /incoming/v2 endpoints.