Extension Versioning
Extension versions are immutable snapshots of a manifest. Published versions cannot be changed. Installs are always pinned to a specific version, so rolling out a change requires publishing a new version and upgrading each install explicitly.
After publishing an upgrade, follow the operations guide to monitor adoption, compatibility, and incidents.
Version lifecycle
A version starts as draft and becomes published when you publish it. Once published, the
version record is frozen — its scopes, iframeUrl, webhookUrl, and eventSubscriptions never change.
stateDiagram-v2 [*] --> draft: POST /extensions/:id/versions draft --> published: POST .../publish published --> [*]: (immutable — never changed)
# Create a new version (snapshots the current manifest)
curl -X POST https://api.usethrottle.dev/api/v1/extensions/ext_xxx/versions \
-H "Authorization: Bearer <dashboard-session>" \
-H "content-type: application/json" \
-d '{
"version": "1.1.0",
"notes": "Added customers:read scope for contact lookup feature"
}'
# Response:
# {
# "data": {
# "id": "ver_new...",
# "version": "1.1.0",
# "status": "draft",
# "scopes": ["orders:read", "customers:read"],
# "notes": "...",
# "createdAt": "..."
# }
# } # Publish a draft version (makes it installable)
curl -X POST https://api.usethrottle.dev/api/v1/extensions/ext_xxx/versions/ver_new/publish \
-H "Authorization: Bearer <dashboard-session>" \
-H "content-type: application/json" \
-d '{}' # List all versions for an extension
GET /api/v1/extensions/ext_xxx/versions
# Response: { data: { versions: [ { id, version, status, scopes, ... }, ... ] } } published versions may be referenced in an
install call. Attempting to install a draft version returns 422 draft_not_installable_in_live for production
environment callers (and similarly for non-production environments). Publish the version
first.
Installs are pinned
When an extension is installed, the install row records the exact versionId. Publishing a new version has no effect on
existing installs — they continue running on the pinned version indefinitely. This
provides stability: a breaking manifest change cannot affect installs that have not opted
in to the upgrade.
The dashboard may surface an "upgrade available" indicator when a newer published version exists for an installed extension. The installer decides when and whether to upgrade.
Upgrading
Use POST /api/v1/installations/:id/upgrade to move
an install to a different published version. The install's API key is updated to
reflect the new version's scopes; the config is preserved.
# Attempt to upgrade to a new version
curl -X POST https://api.usethrottle.dev/api/v1/installations/inst_xxx/upgrade \
-H "Authorization: Bearer <dashboard-session>" \
-H "X-Throttle-Application-Id: <applicationId>" \
-H "content-type: application/json" \
-d '{ "targetVersionId": "ver_new..." }'
# If the new version requests scopes not in the current install:
# HTTP 409
# {
# "error": {
# "code": "new_scopes_require_consent",
# "newScopes": ["customers:read"]
# }
# }
# Retry with explicit consent:
curl -X POST https://api.usethrottle.dev/api/v1/installations/inst_xxx/upgrade \
-H "Authorization: Bearer <dashboard-session>" \
-H "X-Throttle-Application-Id: <applicationId>" \
-H "content-type: application/json" \
-d '{
"targetVersionId": "ver_new...",
"acknowledgedNewScopes": true
}'
# → { data: { id: "inst_xxx", versionId: "ver_new...", ... } } - If the new version's scopes are a subset of the current version's scopes (scope reduction), no consent is required and the upgrade proceeds immediately.
-
If the new version requests additional scopes, the endpoint returns
409 new_scopes_require_consentwith the delta. Retry withacknowledgedNewScopes: trueto confirm.
409 lists exactly which scopes are being added so the
operator can make an informed decision.
Rollback
Rolling back to an older version uses the same upgrade endpoint — just target an earlier versionId. Because older versions have fewer or equal
scopes, rollback typically does not require consent. After rollback, the install runs the
older version's manifest (including its iframeUrl
and event subscriptions).
# Roll back to an older published version by upgrading to it.
# "Rollback" and "upgrade" use the same endpoint — just target an earlier versionId.
# No consent required if the older version's scopes are a subset of the current scopes.
curl -X POST https://api.usethrottle.dev/api/v1/installations/inst_xxx/upgrade \
-H "Authorization: Bearer <dashboard-session>" \
-H "X-Throttle-Application-Id: <applicationId>" \
-H "content-type: application/json" \
-d '{ "targetVersionId": "ver_old..." }' config object is not changed
by an upgrade or rollback. If the new version requires different config keys, update
config separately via PATCH /api/v1/installations/:id/config.
Versioning strategy
-
Use semantic versioning (e.g.
1.0.0) — it is stored as a string, not parsed. - Additive changes (new UI features within the same scopes, bug fixes,
updated
iframeUrl) can be shipped as a new version without requiring install consent. - New scopes always require explicit upgrader consent.
- Removing scopes does not require consent — it is a reduction.
- Keep at least the previous published version available to allow rollback without republishing.
See also: Security model for how scope ceilings are enforced at the version level, and Installing extensions for the full install lifecycle.