Installing Extensions
An install pins a specific published version of an extension to an application, mints a dedicated API key (and optionally a webhook signing secret), and begins event delivery if a webhookUrl was declared.
Install an extension
Pass the versionId of the published version you want to install. Optionally pass config — a free-form JSON object that your extension reads at runtime. The install's environment is derived from the caller's API key or dashboard-selected X-Throttle-Environment-Id and cannot be overridden by the request body.
# Install a published version of an extension into an application.
# Both the dashboard session token (Clerk JWT) and the Application ID header
# are required — installs are application-scoped.
curl -X POST https://api.usethrottle.dev/api/v1/extensions/ext_xxx/install \
-H "Authorization: Bearer <dashboard-session>" \
-H "X-Throttle-Application-Id: <applicationId>" \
-H "X-Throttle-Environment-Id: <workspace-environment-uuid>" \
-H "content-type: application/json" \
-d '{
"versionId": "ver_xxx",
"config": {
"syncInterval": 60,
"notifyOnNew": true
}
}'
# ─── Response (show once — store both values immediately) ────────────────────
{
"data": {
"id": "inst_01HZX...",
"extensionId": "ext_xxx",
"versionId": "ver_xxx",
"status": "active",
"environmentId": "8a5f2d1e-3d2a-4d24-93a1-fd3f9e1f37f0",
"environmentSlug": "uat",
"environmentKind": "non_production",
"providerEnvironment": "sandbox",
"config": { "syncInterval": 60, "notifyOnNew": true },
"createdAt": "2026-05-31T10:00:00.000Z",
"apiKey": "sk_uat_...", // ← never shown again
"webhookSigningSecret": "whsec_..." // ← never shown again (only if webhookUrl declared)
}
}apiKey and webhookSigningSecret are shown exactly once in the install response. If you lose either value, the only recovery is to uninstall and reinstall — a new key and signing secret will be generated.Scope and event consent
When you install an extension, you implicitly consent to its declared scopes and event subscriptions. The installer must hold the extensions:install scope (API key) or the application:extensions:install RBAC permission (dashboard user). The issued API key is scoped exactly to the version's declared scopes.
If the extension later publishes a new version with additional scopes, upgrading to that version requires re-consent. The upgrade endpoint returns 409 new_scopes_require_consent listing the new scopes, and the caller must retry with acknowledgedNewScopes: true. See the Versioning guide.
Configuration
The config field is a free-form JSON object passed at install time and updateable any time thereafter. Use it for extension-specific settings such as API keys for third-party services, feature flags, or integration identifiers. Your extension reads it from the installation record; it is not embedded in the iframe URL or automatically included in the bridge session.
# Update the installation configuration after install.
curl -X PATCH https://api.usethrottle.dev/api/v1/installations/inst_xxx/config \
-H "Authorization: Bearer <dashboard-session>" \
-H "X-Throttle-Application-Id: <applicationId>" \
-H "content-type: application/json" \
-d '{ "config": { "syncInterval": 120, "notifyOnNew": false } }'
# → { data: { id: "inst_xxx", config: { ... }, ... } }import { createBridge } from '@usethrottle/extension-bridge';
const bridge = createBridge({ targetOrigin: 'https://app.usethrottle.dev' });
const ctx = await bridge.ready;
// The bridge context gives you the installation id, not the config object.
// If your iframe needs runtime config, request extensions:read in the manifest
// and read the installation record through the scoped API client.
const installs = await bridge.api.get('/api/v1/installations') as {
data: Array<{ id: string; config: Record<string, unknown> }>;
};
const install = installs.data.find((item) => item.id === ctx.installationId);
const config = install?.config ?? {};bridge.ready resolves with user, workspace, application, environment, installation id, version, role, and scopes. It intentionally does not include config or configSchema. If a browser iframe needs config, give the extension an explicit read scope and fetch the installation record. Keep secrets that should never reach browser JavaScript on your extension backend instead.webhookSigningSecret in config — the config object may be readable by anyone with extensions:read scope. Store the signing secret in your own secrets manager.Configuration schema
Extension authors can publish a configSchema to describe the settings an installer should provide. The dashboard authoring flow includes a field builder for simple string, number, and boolean settings plus an advanced JSON editor for custom JSON Schema. During install and on the management page, Throttle uses this schema to render the configuration form. Empty schemas are treated as “no configurable options.”
{
"type": "object",
"properties": {
"apiKey": {
"type": "string",
"title": "Provider API key",
"description": "Issued by the upstream provider"
},
"syncInterval": {
"type": "number",
"title": "Sync interval"
},
"notifyOnNew": {
"type": "boolean",
"title": "Notify on new records"
}
},
"required": ["apiKey"]
}type: "object", properties, and optional required. Use the advanced JSON mode for nested objects, arrays, enums, or provider-specific validation.configSchema lives on the workspace catalog extension while you are editing it. Creating a version snapshots that schema into the immutable version record. Installs use the pinned version's schema to collect per-install config values. Publishing a new schema does not rewrite existing installs until they upgrade to a version that carries it.Environment-scoped installs
Installs are per environment. A UAT install receives only UAT events and gets an API key for that environment; a production install receives only production events and gets a production-environment API key. The installs are completely independent — each has its own config, its own API key, and its own event delivery endpoint.
List and inspect installs
# List all installations for the current application + environment.
GET /api/v1/installations
-H "Authorization: Bearer <dashboard-session>"
-H "X-Throttle-Application-Id: <applicationId>"
-H "X-Throttle-Environment-Id: <workspace-environment-uuid>"
# Response shape: paginated list
# {
# "data": [ { "id": "inst_...", "extensionId": "ext_...", "status": "active", ... } ],
# "meta": { "requestId": "...", "pagination": { "cursor": null, "hasMore": false } }
# }Install status values: active, suspended. Suspended installs stop receiving events and their launch-token endpoint returns 409 installation_not_active. Delivery logs are accessible at GET /api/v1/installations/:id/deliveries.
Uninstall
Uninstalling removes the install row for the current application and environment. The extension catalog entry and other applications' installs are not affected. The API key and signing secret issued at install time are immediately revoked.
# Uninstall removes the active install for this application + environment.
# The extension catalog entry and other applications' installs are unaffected.
curl -X POST https://api.usethrottle.dev/api/v1/extensions/ext_xxx/uninstall \
-H "Authorization: Bearer <dashboard-session>" \
-H "X-Throttle-Application-Id: <applicationId>"
# → 204 No Content