Install a purchase’s apps
const url = 'https://qinto-api.blotout.io/teams/7b8d1a52-6c1e-4b6f-9a30-2f8a54d1c3e9/bundle-purchases/9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f/apps';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"appSlugs":["facebook-capi"],"installDependencies":false,"acknowledgeNecessary":[],"approvedCharges":{"pixel-sync":{"upToCents":4900,"trialDays":14}}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://qinto-api.blotout.io/teams/7b8d1a52-6c1e-4b6f-9a30-2f8a54d1c3e9/bundle-purchases/9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f/apps \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "appSlugs": [ "facebook-capi" ], "installDependencies": false, "acknowledgeNecessary": [], "approvedCharges": { "pixel-sync": { "upToCents": 4900, "trialDays": 14 } } }'Installs the chosen apps of a bundle purchase on every site the purchase covers, at no charge beyond the bundle: step 2 of buying a bundle, which the team can run at any time. A site that already runs an app keeps it as it is.
How it works
The installs commit together, and the sites update in the background. acknowledgement_required names an app that runs without visitor consent; repeat the call with its slug in acknowledgeNecessary. dependencies_required carries the plan of the apps outside your choice the chosen apps need; repeat with installDependencies. Such an app outside the bundle installs at its own price, so charge_approval_required gives the charge for approvedCharges. cap_reached names the site that would go over 50 apps, and nothing is installed. The caller needs manage_domain_apps on every covered site; a team API token is refused.
Required permissions
domain:manage_domain_apps:writeon all domains, or on the addressed domain when its id is in the route
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.
Unique id (UUID) of the purchase, as the list operation returns it.
Example
9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5fUnique id (UUID) of the purchase, as the list operation returns it.
Request Bodyrequired
Section titled “Request Bodyrequired”The covered apps to install and the confirmations the operation may require.
The covered apps to install, with the confirmations the operation may require.
object
The covered apps to install on every site the purchase covers; duplicates are collapsed.
Confirms the apps outside your choice that the chosen apps need, which install with them; required when the call answers dependencies_required.
Example
falseSlugs of the apps that run without visitor consent, among the chosen apps and the apps they need, that a person acknowledged.
Example
[]The charge a person was shown for each app that charges the team, by app slug: this app and every app the call installs with it, as charge_approval_required or the dependencies_required plan gave them. Approving it also accepts the developer’s terms. The call is refused with the current charge when an entry is missing, the amount went up or the free days went down since it was shown.
object
object
The most the install can charge one site in a month, in US cents: the base price plus the usage cap.
Example
4900The free days before the first charge; 0 when the app has no trial.
Example
14Example
{ "pixel-sync": { "upToCents": 4900, "trialDays": 14 }}Responses
Section titled “Responses”The installs made.
object
One entry per install made, site by site; empty when every chosen app already ran on every covered site.
One install step 2 of a bundle purchase made.
object
Id of the site the app was installed on.
The site’s hostname.
Slug of the installed app.
The version installed.
Example
{ "installed": [ { "domainId": "3f2a9c81-5d47-4e0b-8c66-91b2a7f04d15", "domain": "q.shop.example.com", "appSlug": "facebook-capi", "version": "1.4.1" } ]}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"}no_payment_method — The team pays by card and has no payment method on file, so nothing was deployed or installed. Add a card on the team’s Billing page, then retry. team_unpaid — The team has an unpaid invoice, so it cannot install or enable an app that charges, buy a bundle, or add a site that charges. Nothing changed. Pay what is owed on the team’s Billing page, then retry.
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": "no_payment_method", "message": "The Cloudflare API token is invalid or expired."}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."}not_found — The addressed resource does not exist within this team; check the id and retry.
object
Machine-readable error slug; switch on this value rather than on the HTTP status alone.
Example
{ "error": "not_found"}version_not_ready — The addressed version is still a draft; finalize the push, then retry. install_changed — The app installation was removed or replaced while this request was being prepared. Retry to check the current installation and its charges. version_not_approved — The site got the app from the app store, so it can run only approved versions. Name another version, or omit the version to get the newest approved one. version_below_minimum — Blotout set a minimum version for this app, and the named version is below it. Name the minimum version or a newer one, or omit the version to get the newest one the site may run. version_not_offered — Blotout has suspended this app, and the named version was published during the suspension. Name a version published before the suspension, or retry once the suspension is lifted. cap_reached — The operation would exceed a site-app capacity limit — either the installed-app count or the worker access-token environment. message names the exhausted capacity. subscription_item_limit — The team’s Stripe subscription holds the most lines it can, so no further app or bundle that charges can be added to it; message names the app or bundle. Nothing was installed or bought. Contact support. quota_reached — An object the version declares (a KV namespace, Durable Object class, Pipeline, Hyperdrive config, …) would push the site’s host Cloudflare account past its per-account ceiling. message names the object type and the account’s count; free objects or have the ceiling raised, then retry. multiple_not_allowed — The app allows only one install per site, or a version change would leave more installs than the target version allows. message states the conflict. browser_budget — A version’s browser code, or a large value its browser code reads, would push the site’s q.js past its size budget; shrink the value, or remove or shrink another app first. restore_available — This app was removed from the site recently and can still be brought back with its configuration and data. Call the restore operation to undo the removal, or repeat this call with replaceRemoved set to discard it and install a new one. binding_conflict — The target version binds a resource name that a version still running on this site binds to something else; message names both. Retrying cannot resolve it — the app must use a different binding name, or remove the binding in one version and re-add it in the next. schema_incompatible — The site has completed a contract D1 migration that the target app version does not include unchanged; select a schema-compatible version. variable_retype — The target version changes a variable’s kind or type while a version still running on this site declares the old shape; message names both. Retrying cannot resolve it — the app must use a different variable name, or remove the variable in one version and re-add it in the next. linked — The app’s storage is shared through a storage link: a child site binds this site’s objects, or this site binds a parent’s. message names the sites; change the link first, or make the change on the parent site. When a child site lacks an app the version needs, message names it; install it on that child first. approval_required — The target version requests more access than the installed one — API scopes, data buckets, or newly captured query params and cookies; message names the additions. Repeat the call with acceptCapabilities once a site manager accepts them. acknowledgement_required — The app, or an app it needs, declares necessary consent, so it runs without visitor consent; message names it. Repeat the call with acknowledgeNecessary for the app, or with the slug of the app it needs in acknowledgeNecessaryDependencies. dependency_unavailable — The app needs an app that cannot be installed on this site: it is not in the app store and you are not in its organization, it is suspended or deleted, or it has no version the site may run, or it was removed from this site recently and must be restored or installed on its own first. message names it. Nothing was installed. registry_budget — The change would grow the site’s app configuration past the size the site worker can deploy. Remove an app or an install, or shorten install labels and geo filters, then retry. message states the refusal. app_suspended — Blotout has suspended this app, so nothing ran and nothing changed. The call works again once the suspension is lifted. app_taken_down — Blotout has taken this app down for good, so nothing changed. message states what the call cannot do. Do not retry. purchase_ended — The bundle purchase that covered the app ended while the install ran, so nothing was installed. Retry: the app then installs at its own price. removal_pending — A previous uninstall of this app is torn down as part of this call, and that teardown could not finish — because a deployment for the site is already running, or Cloudflare refused a delete. Nothing was left half-removed and the site’s scheduled cleanup carries on, so retry. sites_paused — Blotout has paused this team’s sites, so nothing was deployed or installed. Every site of the team answers 410 until the pause ends, and uninstalls, switches and settings changes are still recorded. A payment does not end the pause; contact Blotout. deploys_blocked — Blotout has blocked this team’s deploys, so nothing was deployed or installed. The team’s live sites keep running, and uninstalls, switches and settings changes still roll out. A payment does not lift the block; contact Blotout. charge_approval_required — The call charges the team and did not approve that charge, or the charge went up, or its free days went down, after it was shown. charge holds the current charge and message states it. For an app, appSlug names the app the charge belongs to: once a person allowed to purchase apps accepts the charge and the developer’s terms, repeat the call with { "<appSlug>": <charge> } in approvedCharges, keeping the other entries. For a bundle, repeat the call with charge as approvedCharge. Nothing was installed or bought. dependencies_required — The app needs other apps the site does not have yet; plan lists every app it needs, at any depth, and whether the site has it. Nothing was installed. Repeat the call with installDependencies to install the missing apps first, then the app, and approve the plan’s private-function calls.
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.
With dependencies_required: what a confirmed call installs and the grants it approves.
object
Every app the app needs, at any depth, in install order (each before the apps that need it).
One app the installed app needs, directly or through another app it needs.
object
Slug of the app that is needed.
Display name of the app that is needed.
Why it is needed, from the app that needs it, or else the description of the needed app; null when neither gives one.
Slug of the app that needs it: the app being installed or another app in the plan.
Whether the site has the app: missing (installed by a confirmed call), installed, or installed-disabled (on the site but switched off).
The version a confirmed call installs; only for a missing app.
Whether the app runs without visitor consent, so its slug must be in acknowledgeNecessaryDependencies; only for a missing app.
The developer’s legal documents, accepted by approving its charge; only for a missing app.
object
The developer’s terms for this app.
The developer’s privacy policy for this app.
What installing it charges, sent back in approvedCharges under this app’s slug to approve it; null when it charges nothing; only for a missing app.
object
The most the install can charge one site in a month, in US cents: the base price plus the usage cap.
The free days before the first charge; 0 when the app has no trial.
The calls the app and the missing apps make to private functions of apps the site would run. A confirmed call approves their grants. Calls to public functions need no grant and are not listed.
Calls from one app to the private functions of another, which need a grant on the site.
object
Slug of the calling app: the app being installed or a missing app.
Slug of the app whose functions it calls.
The private functions it calls, sorted by name.
With charge_approval_required: the current charge to send back under appSlug in approvedCharges.
object
The most the install can charge one site in a month, in US cents: the base price plus the usage cap.
The free days before the first charge; 0 when the app has no trial.
With charge_approval_required: the app the charge belongs to.
Example
{ "error": "version_not_ready", "message": "The Cloudflare API token is invalid or expired.", "plan": { "dependencies": [ { "slug": "consent-manager", "name": "Consent Manager", "reason": "Reads the visitor’s consent state", "neededBy": "facebook-capi", "state": "missing", "version": "1.2.0", "necessary": false, "legal": { "termsUrl": "https://example.com/legal/terms", "privacyUrl": "https://example.com/legal/privacy" }, "charge": { "upToCents": 4900, "trialDays": 14 } } ], "rpcCalls": [ { "callerAppSlug": "facebook-capi", "providerAppSlug": "consent-manager", "functions": [ "getConsent" ] } ] }, "charge": { "upToCents": 4900, "trialDays": 14 }, "appSlug": "pixel-sync"}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"}