Documentation/Managing Your App/Plugin Registry
Plugin Registry
Register your own external HTTP endpoint as a domain-scoped plugin, then call it through Supero with centralized credential storage, automatic retries, and a circuit breaker.
What the Plugin Registry Is
For most third-party services, the managed services catalog is the right place to start: browse it with GET /api/v1/services/catalog, import an entry with POST /api/v1/services/import, and configure it with PUT /api/v1/services/{id}/config. The Plugin Registry is the secondary, do-it-yourself path — use it when the vendor you need is not in the catalog, or when you want to wire up your own internal HTTP endpoint (an internal microservice, a partner webhook receiver, a custom automation) as something your app can call consistently.
A plugin is a record of one HTTP endpoint plus the settings needed to call it safely: method, timeout, an optional auth header, retry behavior, and circuit-breaker thresholds. Plugins are scoped to the domain, not to a project. POST /api/v1/plugins/register takes a project_uuid, but that value is not part of the plugin's address: the record is stored as a child of the domain under the two-element name [domain, plugin_name].
⚠️ Domain-scoped, not project-scoped
Every project in the domain sees every plugin. plugin_name must be unique across the whole domain, and GET /api/v1/plugins?project_uuid=... returns the domain's full plugin list regardless of the project_uuid you pass — the parameter is accepted for caller compatibility and does not filter. If you run several apps in one domain, namespace your plugin names yourself (storefront-sms, billing-sms) and assume every project can read every plugin record.
Register a Plugin
Register a plugin with a POST to /api/v1/plugins/register. project_uuid, plugin_name, and endpoint_url are required; everything else has a default.
bash
curl -X POST https://api.supero.dev/api/v1/plugins/register \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"project_uuid": "8f14e45f-2d3a-4b1c-9e6a-1234567890ab",
"plugin_name": "internal-sms-gateway",
"endpoint_url": "https://sms.example.com/api/send",
"http_method": "POST",
"auth_type": "header",
"auth_header_name": "X-Api-Key",
"auth_header_value": "your-vendor-api-key",
"timeout_seconds": 15,
"max_retries": 3
}'A successful call returns 201 with the new plugin_uuid:
json
{
"success": true,
"message": "Plugin registered successfully",
"plugin": {
"plugin_uuid": "c4a1...e92f",
"plugin_name": "internal-sms-gateway",
"endpoint_url": "https://sms.example.com/api/send",
"enabled": true,
"health_status": "healthy"
}
}⚠️ Unique per domain
plugin_name must be unique within the domain — registering the same name twice returns a 400, even when the two calls pass different project_uuid values. Two apps in one domain cannot both register internal-sms-gateway. endpoint_url must start with http:// or https://.
Plugin Fields
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| endpoint_url | string | Yes | — | Full URL of the target endpoint (http:// or https://). |
| http_method | string | Yes | POST | One of POST, PUT, PATCH. |
| timeout_seconds | integer | Yes | 30 | Request timeout, up to 300 seconds. |
| auth_type | string | Yes | none | none or header. |
| auth_header_name | string | No | — | Header name to send when auth_type is header, e.g. X-Api-Key or Authorization. |
| auth_header_value | string | No | — | Header value (API key, bearer token, etc). Redacted in the plugin API's own list and read responses; not encrypted at rest — see the warning below. |
| custom_headers | json | No | — | Additional static headers, as a JSON object, sent with every call. |
| max_retries | integer | Yes | 3 | Retry attempts on a failed call. |
| retry_backoff_seconds | integer | Yes | 2 | Initial backoff delay; increases exponentially with each retry. |
| circuit_breaker_threshold | integer | Yes | 5 | Consecutive failures before the circuit opens. |
| circuit_breaker_timeout_seconds | integer | Yes | 60 | How long the circuit stays open before a call is allowed through again. |
| enabled | boolean | Yes | true | Disabled plugins reject invocations with 503. |
🚨 auth_header_value is not encrypted at rest
The credential you register here is stored as a plain string attribute on the plugin record. The platform's at-rest key store seals only integration service_config and platform_secret objects — plugin_registry is not in that set, so no sealing is applied to this field on any deployment. What the platform does provide is redaction: the plugin service substitutes ***REDACTED*** for the value in its own list and get responses. That is response shaping in one service, not a storage guarantee, and it is not a reason to treat the value as protected. Treat a registered plugin credential as a secret you are handing to the platform's database: use a short-lived, narrowly scoped, rotatable vendor credential, never a long-lived master key, and rotate it on the vendor's side if your domain's data is ever exposed. If the vendor is in the managed services catalog, configure it there instead — those credentials go through the service_config path, which the key store can seal.
The plugin API has no endpoint that changes auth_header_value — the only write routes on a registered plugin are PUT /status and DELETE. Rotating the credential means deleting the plugin and registering it again under the same name.
The registry also tracks read-only fields you'll see on a GET: plugin_uuid, health_status, total_invocations, success_count, failure_count, last_invocation_at, and average_latency_ms.
Managing Plugins
| Method & Path | Purpose |
|---|---|
| POST /api/v1/plugins/register | Register a new plugin in the domain. project_uuid is required in the body but does not scope the record. |
| GET /api/v1/plugins?project_uuid={uuid} | List the domain's plugins. project_uuid is accepted but does not filter — every project in the domain gets the same list. Add &enabled_only=true to return only enabled plugins. |
| GET /api/v1/plugins/{plugin_uuid} | Get full details for one plugin. |
| PUT /api/v1/plugins/{plugin_uuid}/status | Enable or disable a plugin. Body: { "enabled": true }. |
| DELETE /api/v1/plugins/{plugin_uuid} | Delete a plugin. |
| GET /api/v1/plugins/{plugin_uuid}/health | Get health status, invocation statistics, and circuit-breaker configuration. |
bash
curl "https://api.supero.dev/api/v1/plugins?project_uuid=8f14e45f-2d3a-4b1c-9e6a-1234567890ab" \
-H "Authorization: Bearer $ACCESS_TOKEN"
curl -X PUT https://api.supero.dev/api/v1/plugins/c4a1...e92f/status \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'Invoking a Plugin
To call a registered plugin, POST to /api/v1/plugins/invoke/{object_type}. object_type is just a label used for logging — use whatever name describes the call you're making. Identify the plugin with plugin_name or plugin_uuid in the body; every other field you send is forwarded as the request payload to the plugin's endpoint_url.
bash
curl -X POST https://api.supero.dev/api/v1/plugins/invoke/sms_notification \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"plugin_name": "internal-sms-gateway",
"to": "+15551234567",
"body": "Your order has shipped"
}'- •The stored auth header (if auth_type is header) is attached automatically — the credential never has to travel through your application code.
- •If the plugin is disabled, the call returns 503.
- •If plugin_name or plugin_uuid does not resolve to a plugin in your domain, the call returns 404.
- •plugin_name and plugin_uuid are stripped before the payload is forwarded — they are never sent to your endpoint.
ℹ️ A separate permission
Invoking a plugin requires the operational:create permission, which is distinct from the plugin:* permissions used to manage the registry — see Permissions below.
Reliability and Health
Every plugin call goes through the same reliability layer: on failure, the platform retries up to max_retries times with exponential backoff starting at retry_backoff_seconds. If failures reach circuit_breaker_threshold in a row, the circuit opens and further calls fail immediately — without a network round trip — until circuit_breaker_timeout_seconds has elapsed.
| health_status | Meaning |
|---|---|
| healthy | The value a plugin is registered with — it is the schema default, so a brand-new plugin reports healthy before it has ever been called. Also the value while recent calls are succeeding. |
| unhealthy | Recent calls are failing, but the circuit is not open. |
| circuit_open | The failure threshold was reached; calls are being short-circuited. |
| unknown | Declared in the health_status enum but not written on registration. Do not read it as "not yet checked" — as the registration response above shows, a newly registered plugin comes back healthy. |
GET /api/v1/plugins/{plugin_uuid}/health returns the current health_status plus a statistics block (total_invocations, success_count, failure_count, success_rate_percent, average_latency_ms, last_invocation_at) and the plugin's circuit_breaker configuration (threshold, timeout_seconds).
Permissions
Managing the registry (register, update status, delete) and viewing it (list, read, health) are separate permissions, and invoking a plugin uses a third permission entirely. Across the roles you can assign to your own users:
| Role | View plugins | Register / update / delete | Invoke plugins |
|---|---|---|---|
| domain_admin | Yes | Yes | Yes |
| project_admin | Yes | Yes | Yes |
| domain_user | Yes | No | Yes |
| project_user | Yes | No | Yes |
| developer | Yes | No | Yes |
| tenant_admin | Yes | No | Yes |
| tenant_user | Yes | No | Yes |
| viewer | Yes | No | No |
In practice: only domain_admin and project_admin can add, change, or remove plugins. Every other role can see what is registered; viewer can see the list but cannot invoke. So a developer or tenant_admin building against a plugin can call it but cannot change how it is configured, and a viewer can see that it exists without being able to run it.
Next Steps
- •API Integrations — the managed services catalog, the primary path for connecting supported third-party services
- •Webhooks & Events — receive events from Supero instead of calling out to one
- •Roles & Permissions Reference — the full permission set behind the table above
- •Projects & Tenants — how project scoping works across the platform
On this page