List bundle purchases
const url = 'https://qinto-api.blotout.io/teams/7b8d1a52-6c1e-4b6f-9a30-2f8a54d1c3e9/bundle-purchases?bundleId=2b7e4d10-8a3c-4f56-9e21-7c0d5b8f3a64&cursorCreatedAt=2026-11-05T12%3A00%3A00.000Z&cursorId=9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f&limit=50';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://qinto-api.blotout.io/teams/7b8d1a52-6c1e-4b6f-9a30-2f8a54d1c3e9/bundle-purchases?bundleId=2b7e4d10-8a3c-4f56-9e21-7c0d5b8f3a64&cursorCreatedAt=2026-11-05T12%3A00%3A00.000Z&cursorId=9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f&limit=50' \ --header 'Authorization: Bearer <token>'Lists the bundles the team bought, newest first, each with the terms copied when it was bought, the sites it covers, who accepted the charge and the terms, and when it started and ended. Call it to see what the team holds before you buy or install.
How it works
Ended purchases stay in the list with endedAt set; an active one has endedAt null. Narrow to one bundle with bundleId. Page with cursorCreatedAt and cursorId from the previous response’s nextCursor; a null cursor means the last page. Only a token with a user lists; a team API token is refused.
Required permissions
team:readon the team
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Unique id (UUID) of the team that owns the addressed resources, as shown in the team settings on the Blotout platform.
Example
7b8d1a52-6c1e-4b6f-9a30-2f8a54d1c3e9Unique id (UUID) of the team that owns the addressed resources, as shown in the team settings on the Blotout platform.
Query Parameters
Section titled “Query Parameters”Return only the purchases of this bundle.
Example
2b7e4d10-8a3c-4f56-9e21-7c0d5b8f3a64Return only the purchases of this bundle.
The nextCursor.createdAt of the previous page.
Example
2026-11-05T12:00:00.000ZThe nextCursor.createdAt of the previous page.
The nextCursor.id of the previous page.
Example
9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5fThe nextCursor.id of the previous page.
Page size, 1–200 (default 50).
Example
50Page size, 1–200 (default 50).
Responses
Section titled “Responses”The page of purchases.
object
One page of the team’s purchases, newest first, ended ones included.
A team’s holding of one bundle, with the terms copied when it bought it and the sites it chose.
object
Id of the purchase.
The team that holds it.
The bundle bought.
The bundle’s current name.
The developer organization’s name.
The bundle link the purchase came through; null from the app store.
The pricing model copied at purchase.
The price per interval copied at purchase, in US cents.
How often the price is charged.
The free days copied at purchase; null or 0 for none.
The price per interval of each site past the limit; null when none are sold.
The services copied at purchase.
A service the developer delivers, which the bundle price covers.
object
Name of the service. Empty only on a bundle that isn’t published yet.
What the team gets. Empty when not set.
What the service costs on its own, in US cents. Null only on a bundle that isn’t published yet.
How the price on its own is billed.
The most usage adds to the bill each month, in US cents.
The priced meters copied at purchase.
What one meter of one app costs on a bundle. A meter with no term is free.
object
Id of the app that reports the meter.
Key of the meter in that app. The same key in two apps is two terms.
Name of the meter on the bundle.
Plural unit, as the bill shows it. Empty only on a bundle that isn’t published yet.
Units included each month for the whole team, across its sites.
Price past the included amount, in US micro-dollars.
How many units that price covers. Null only on a bundle that isn’t published yet.
The app mode copied at purchase.
The bundle’s stored app list copied at purchase; empty for All apps.
The sites the purchase covers, A–Z by hostname.
object
Id of the site.
The site’s hostname.
Who accepted the apps’ terms.
When the terms were accepted.
Who approved the charge; null for a free bundle.
When the charge was approved; null for a free bundle.
The purchase instant. Charges start the next UTC day.
When the purchase ends: the first instant it no longer covers, still ahead while a cancel waits to take effect; null while active.
Pass both fields as cursorCreatedAt / cursorId for the next page; null on the last page.
object
Start instant of the last listed purchase.
Id of the last listed purchase.
Example
{ "purchases": [ { "bundlePurchaseId": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f", "teamId": "7b8d1a52-6c1e-4b6f-9a30-2f8a54d1c3e9", "bundleId": "2b7e4d10-8a3c-4f56-9e21-7c0d5b8f3a64", "bundleName": "Growth Stack", "publisher": "Acme Dev", "linkId": null, "pricingModel": "fixed", "basePriceCents": 2900, "billingInterval": "monthly", "trialDays": 14, "siteLimit": 3, "extraSitePriceCents": 500, "services": [ { "name": "Priority support", "detail": "4-hour response, shared Slack channel", "priceCents": 20000, "basis": "monthly" } ], "usageCapCents": 10000, "meterTerms": [ { "appId": "7f3a1c92-4b6d-4e8a-b1d3-5c9e2f7a6b10", "key": "events", "name": "Events", "unit": "events", "includedAmount": 100000, "priceMicros": 500000, "perUnits": 1000 } ], "appMode": "selected", "appIds": [], "sites": [ { "domainId": "3f2a9c81-5d47-4e0b-8c66-91b2a7f04d15", "domain": "q.shop.example.com" } ], "termsAcceptedByUserId": "1f2e3d4c-5b6a-4978-8f0e-9d8c7b6a5f4e", "termsAcceptedAt": "2026-11-08T12:00:00.000Z", "chargeApprovedByUserId": null, "chargeApprovedAt": "2026-11-08T12:00:00.000Z", "startedAt": "2026-11-08T12:00:00.000Z", "endedAt": "2026-11-08T12:00:00.000Z" } ], "nextCursor": { "createdAt": "2026-11-05T12:00:00.000Z", "id": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f" }}invalid_params — A path parameter failed validation (for example a malformed UUID); details pinpoints the offending parameter. invalid_body — The JSON body is unparseable or failed schema validation; details pinpoints the offending fields when the body could be parsed.
object
Machine-readable error slug; switch on this value rather than on the HTTP status alone.
Field-level validation errors: a tree keyed by input property, each node carrying an errors array of human-readable messages. Render it to help the caller correct the request.
object
Example
{ "error": "invalid_params", "details": { "errors": [], "properties": { "teamId": { "errors": [ "Invalid UUID" ] } } }}unauthenticated — The Authorization: Bearer token is missing, malformed, or no longer valid. Generate a team API token or obtain a new OAuth access token.
object
Machine-readable error slug; switch on this value rather than on the HTTP status alone.
Example
{ "error": "unauthenticated"}forbidden — The bearer credential lacks the required permission or belongs to another team, or its user lacks the organization role a developer operation needs. Update its policies, use a credential bound to the addressed team, or ask an organization owner for the role; message states the reason when one is known.
object
Machine-readable error slug; switch on this value rather than on the HTTP status alone.
Human-readable reason for this specific failure, intended for display — do not parse it programmatically.
Example
{ "error": "forbidden", "message": "The Cloudflare API token is invalid or expired."}internal — Unexpected server error. Nothing is guaranteed to have been persisted; idempotent operations are safe to retry.
object
Machine-readable error slug; switch on this value rather than on the HTTP status alone.
Example
{ "error": "internal"}