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.
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 iframeUrland 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.