Skip to content

Install an app

PUT
/teams/{teamId}/domains/{domainId}/apps/{appSlug}
curl --request PUT \
--url https://qinto-api.blotout.io/teams/7b8d1a52-6c1e-4b6f-9a30-2f8a54d1c3e9/domains/3f2a9c81-5d47-4e0b-8c66-91b2a7f04d15/apps/facebook-capi \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "appVersionId": "7d8e9f0a-1b2c-4d3e-9f4a-5b6c7d8e9f0a", "version": "1.4.1", "label": "EU pixel", "replaceRemoved": false, "installDependencies": true, "acknowledgeNecessaryDependencies": [ "consent-manager" ], "acceptCapabilities": false, "acknowledgeNecessary": false, "approvedCharges": { "pixel-sync": { "upToCents": 4900, "trialDays": 14 } } }'

Installs a ready app version on the site (creating its first install), or moves an already-installed app to the version. An app in the app store installs the same way, by its slug. Replaying with the current version is a no-op, so retries are safe.

How it works

A first install needs the caller to belong to the app’s organization, unless the app is in the app store; a later version move needs neither. With no version named, the newest version the site may run is installed. approval_required, acknowledgement_required and dependencies_required each name a flag to repeat the call with; charge_approval_required gives the charge for approvedCharges. Approving a charge needs purchase_apps on the credential and on its user. With installDependencies the missing apps install first, and if one fails nothing changes. A new install starts unconfigured; the site updates in the background.

Required permissions

  • domain:manage_domain_apps:write on all domains, or on the addressed domain when its id is in the route
teamId
required

Unique id (UUID) of the team that owns the addressed resources, as shown in the team settings on the Blotout platform.

string format: uuid

Example

7b8d1a52-6c1e-4b6f-9a30-2f8a54d1c3e9

Unique id (UUID) of the team that owns the addressed resources, as shown in the team settings on the Blotout platform.

domainId
required

Unique id (UUID) of the site, as returned by the site operations.

string format: uuid

Example

3f2a9c81-5d47-4e0b-8c66-91b2a7f04d15

Unique id (UUID) of the site, as returned by the site operations.

appSlug
required

Immutable slug of the app, as shown by the list operation.

string
/^[a-z][a-z0-9-]{2,29}$/

Example

facebook-capi

Immutable slug of the app, as shown by the list operation.

The version to install and the consent flags the operation may require.

Media typeapplication/json

The version to install, with the name for the installation and the consent flags the operation may require.

object
appVersionId

The ready version to install or move to. Omit it, and version, to take the newest version the site may run: the newest approved version for a site that got the app from the app store, the newest published version otherwise.

string format: uuid

Example

7d8e9f0a-1b2c-4d3e-9f4a-5b6c7d8e9f0a
version

The published version to install, named instead of appVersionId.

string
/^\d+\.\d+\.\d+$/

Example

1.4.1
label

Names the installation this call creates; left out or blank, it takes the app’s name. Ignored by a version change.

string
<= 100 characters

Example

EU pixel
replaceRemoved

Discards a recent removal that could still be undone, so this call installs a new one instead; required to answer restore_available.

boolean

Example

false
installDependencies

Installs the apps the version needs that the site does not have yet, in the same step as the install or version move, and approves the plan’s private-function calls; required to answer dependencies_required.

boolean

Example

true
acknowledgeNecessaryDependencies

The slugs of the apps this call installs as dependencies that run without visitor consent, each acknowledged; required when acknowledgement_required names one.

Array<string>
<= 50 items

Example

[
"consent-manager"
]
acceptCapabilities

Accepts the additional access a newer version requests, including any query params or cookies it newly captures; required when the change answers approval_required.

boolean

Example

false
acknowledgeNecessary

Records that this app runs without visitor consent; required when the call answers acknowledgement_required.

boolean

Example

false
approvedCharges

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
key
additional properties
object
upToCents
required

The most the install can charge one site in a month, in US cents: the base price plus the usage cap.

integer

Example

4900
trialDays
required

The free days before the first charge; 0 when the app has no trial.

integer

Example

14

Example

{
"pixel-sync": {
"upToCents": 4900,
"trialDays": 14
}
}

What the call did.

Media typeapplication/json
object
domainAppId
required

Id of the installation the call affected.

string
action
required

What the call did: a first install, a version move, or nothing (the version was already selected).

string
Allowed values: installed version_changed unchanged

Example

{
"domainAppId": "6c5b4a39-2817-4d6e-9f0a-1b2c3d4e5f6a",
"action": "installed"
}

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.

Media typeapplication/json
object
error
required

Machine-readable error slug; switch on this value rather than on the HTTP status alone.

string
Allowed values: invalid_params invalid_body
details

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
key
additional properties

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.

Media typeapplication/json
object
error
required

Machine-readable error slug; switch on this value rather than on the HTTP status alone.

string
Allowed values: unauthenticated

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.

Media typeapplication/json
object
error
required

Machine-readable error slug; switch on this value rather than on the HTTP status alone.

string
Allowed values: no_payment_method team_unpaid
message

Human-readable reason for this specific failure, intended for display — do not parse it programmatically.

string

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.

Media typeapplication/json
object
error
required

Machine-readable error slug; switch on this value rather than on the HTTP status alone.

string
Allowed values: forbidden
message

Human-readable reason for this specific failure, intended for display — do not parse it programmatically.

string

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.

Media typeapplication/json
object
error
required

Machine-readable error slug; switch on this value rather than on the HTTP status alone.

string
Allowed values: not_found

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.

Media typeapplication/json
object
error
required

Machine-readable error slug; switch on this value rather than on the HTTP status alone.

string
Allowed values: version_not_ready install_changed version_not_approved version_below_minimum version_not_offered cap_reached subscription_item_limit quota_reached multiple_not_allowed browser_budget restore_available binding_conflict schema_incompatible variable_retype linked approval_required acknowledgement_required dependency_unavailable registry_budget app_suspended app_taken_down purchase_ended removal_pending sites_paused deploys_blocked charge_approval_required dependencies_required
message

Human-readable reason for this specific failure, intended for display — do not parse it programmatically.

string
plan

With dependencies_required: what a confirmed call installs and the grants it approves.

object
dependencies
required

Every app the app needs, at any depth, in install order (each before the apps that need it).

Array<object>

One app the installed app needs, directly or through another app it needs.

object
slug
required

Slug of the app that is needed.

string
name
required

Display name of the app that is needed.

string
reason
required

Why it is needed, from the app that needs it, or else the description of the needed app; null when neither gives one.

string | null
neededBy
required

Slug of the app that needs it: the app being installed or another app in the plan.

string
state
required

Whether the site has the app: missing (installed by a confirmed call), installed, or installed-disabled (on the site but switched off).

string
Allowed values: missing installed installed-disabled
version

The version a confirmed call installs; only for a missing app.

string
necessary

Whether the app runs without visitor consent, so its slug must be in acknowledgeNecessaryDependencies; only for a missing app.

boolean
legal

The developer’s legal documents, accepted by approving its charge; only for a missing app.

object
termsUrl
required

The developer’s terms for this app.

string
privacyUrl
required

The developer’s privacy policy for this app.

string
charge

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
upToCents
required

The most the install can charge one site in a month, in US cents: the base price plus the usage cap.

integer
trialDays
required

The free days before the first charge; 0 when the app has no trial.

integer
rpcCalls
required

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.

Array<object>

Calls from one app to the private functions of another, which need a grant on the site.

object
callerAppSlug
required

Slug of the calling app: the app being installed or a missing app.

string
providerAppSlug
required

Slug of the app whose functions it calls.

string
functions
required

The private functions it calls, sorted by name.

Array<string>
charge

With charge_approval_required: the current charge to send back under appSlug in approvedCharges.

object
upToCents
required

The most the install can charge one site in a month, in US cents: the base price plus the usage cap.

integer
trialDays
required

The free days before the first charge; 0 when the app has no trial.

integer
appSlug

With charge_approval_required: the app the charge belongs to.

string

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.

Media typeapplication/json
object
error
required

Machine-readable error slug; switch on this value rather than on the HTTP status alone.

string
Allowed values: internal

Example

{
"error": "internal"
}