Plugins & Connectors
Technical reference. This page is for integrations, advanced configuration, or platform operations. For portal tasks, start with the user guide.
Plugins are shipped as cataloged packages that can be installed into a workspace
and then used by task steps. They let flows call external APIs, databases,
queues, cloud services, and internal tools through a governed runtime surface.
What A Plugin Provides
Every package catalog entry exposes:
| Field | Description |
|---|---|
name | Package name, such as orqestra-official-plugins |
version | Package version |
packs | Named groups of package-local assets |
assets | Package-local connector, tool, hook, MCP, and runtime profile catalog items |
Packages are listed through GET /v1/packages/. Workspace installs and runtime
availability are managed through GET/POST /v1/package-installs/.
Gateway-injected connection inputs (external-runtime mediated path)
A plugin manifest may declare connection_secret_inputs — input names that are
gateway-injected connection secrets when the plugin runs on the
external-runtime mediated path (the gateway invokes the tool on behalf of an
external runtime). For those inputs the gateway resolves the secret server-side
from the workspace’s single ready connection of the plugin’s mapped service type
and injects it; the external runtime never supplies or sees the credential — a
runtime-supplied value for a declared input is rejected. An input declared here
may also appear in the published input_schema required list (the native
flow-template path still expects the caller to supply it); on the mediated path it
is gateway-satisfied instead. Declaring an input means every mediated call needs a
ready matching connection — so declare it only for connectors that genuinely
require the credential.
For that selected connection, connector config_fields that also appear in the
tool input schema are gateway-supplied too. A conflicting caller value is rejected
before token consumption with tool_connection_config_mismatch; an absent required
config field keeps the connection in setup_required.
A manifest may also list a subset of these in optional_connection_secret_inputs.
An optional secret is injected only if the matched connection binds it (an unbound
optional slot is skipped rather than failing the call), and when every declared
secret is optional a mediated call may run with no connection at all — for public
endpoints, or connectors with mutually-exclusive auth modes (e.g. an API key or
basic-auth username/password, where only one mode is configured per connection). A
bound optional secret whose resolution fails still fails the call closed; it is
never silently downgraded to an unauthenticated request.
MCP client identities and connector setup
The official MCP package exposes two fixed direct-client identities:
mcp_client is the acting identity for delegated calls, while
mcp_client_advisory is the service-identity path for a separately governed
connector. The advisory identity is not a caller-selectable mode and it does
not accept auth_token or arbitrary headers in task inputs. Those fields are
rejected before credential resolution or network egress.
For an advisory call, install the official package in the workspace and bind
the mcp_client_advisory tool to the release. Separately create and bind the
mcp_server connection for the target URL and its auth_token secret slot.
Installation alone is only fresh package availability; it does not grant a
release authority or select a connection. The server-owned connector pin,
release binding, and target URL must all agree before the runtime can list or
call a tool. Connector credentials never appear in flow inputs, evidence, or
readback.
Package Install Lifecycle
A package asset moves through five stages before a workload can execute it:
| Stage | What it means |
|---|---|
| Cataloged | The package manifest is loaded; GET /v1/packages/ lists it. No workspace state exists yet. |
| Installed | POST /v1/package-installs/ materializes the selected assets into governed_asset/asset_version rows and writes a plugin_install row scoped to (workspace, environment, runtime_target). |
| Available | The install’s plugin_asset_availability rows report available: true for that scope. Catalog availability is not runtime availability — an installed-but-unavailable asset still denies at bind time. |
| Bound | An operator explicitly binds the installed asset’s asset_version_id to a release role (orqestractl plugin bind, orqestractl release bind, or orqestractl release bind-connection for connector_definition). An installed asset is never usable until bound. |
| RuntimeResolved | The runtime gateway resolves the bound asset against a live implementation at execution time. |
orqestractl plugin resolve <package_ref> reads the Installed/Available
stages for a given workspace and runtime target without raising — the
response’s resolved flag and reason explain exactly why an asset does or
does not resolve (uninstalled, disabled, wrong runtime target, or installed
but never materialized). See
Resolving And Binding Installed Assets
below.
Current First-Party Catalog
Official S3 reads (s3_read_write) bound source and encoded result to 4 MiB
(official_object_read). Base64 source is effectively 3,145,728 bytes.
Overflow fails closed with outbound_response_limit_exceeded and no partial
output. The limit is enforced as UTF-8 bytes at runtime, not only as JSON
Schema character maxLength.
The repository currently ships one checked-in official package,
orqestra-official-plugins. It pairs a connector_definition with a
tool_definition for each integration, all grouped under one core-connectors
pack. As of this writing that is roughly three dozen connectors spanning HTTP,
SQL/NoSQL data stores (Postgres, MySQL, Snowflake, MongoDB, Redis,
Elasticsearch, BigQuery), object storage (S3, GCS, Azure Blob), messaging
(Slack, Microsoft Teams, Discord, PagerDuty, SendGrid, Twilio), SaaS APIs
(Salesforce, HubSpot, Notion, Airtable, Jira, Zendesk, Stripe), and
general-purpose connectors (GitHub, GraphQL, Kafka, webhook verification, an
MCP client, code execution, generic IMAP email fetch).
Fetch the live, authoritative list instead of relying on a doc snapshot that drifts as connectors are added:
curl http://localhost:8000/v1/packages/orqestra-official-plugins
# or
orqestractl plugin get orqestra-official-pluginsThe package lifecycle is intentionally broader than this first checked-in catalog entry: additional trusted packages use the same install, provenance, and availability model once added.
Outbound response limits
Official HTTP connectors use the shared bounded egress client. Ordinary APIs
use the official_api profile (1 MiB encoded and 2 MiB decoded); Azure Blob and
GCS object reads use official_object_read (4 MiB for each ceiling). Redirects
are not followed automatically, and redirect bodies are discarded before a
connector parses the response.
The generic http_request action exposes an optional max_response_bytes
input. It accepts 1 through 2 MiB and defaults to 1 MiB. This value can only
lower the selected profile ceiling, never raise it. An oversized response is a
closed whole-operation failure rather than a truncated success.
IMAP email_fetch is bounded independently of HTTP connectors. The lowerable
controls are max_results (1–25, default 10), max_message_bytes (1–4 MiB,
default 1 MiB), max_attachment_bytes (1 MiB maximum, default 1 MiB), and
max_total_output_bytes (1–8 MiB, default 4 MiB). The connector validates the
declared RFC822 literal before reading, uses BODY.PEEK[] partial reads, and
rejects oversized MIME/body/attachment/base64/aggregate output as one closed
email_response_limit_exceeded failure. Messages are not marked \\Seen until
the complete bounded result is accepted.
Using A Plugin In A Flow
Plugins are called from task steps by setting action to the plugin slug:
steps:
- id: enrich_customer
type: task
action: http_request
inputs:
method: POST
url: "https://api.example.com/v1/enrich"
body:
email: "{{trigger.email}}"
next: respond
- id: respond
type: return
output:
enrichment: "{{steps.enrich_customer.output}}"Example Plugin Inputs
http_request
AKS sets ORQESTRA_PLUGIN_INLINE_CREDENTIALS=deny. Direct credential values in
plugin inputs are refused before resolution or network egress. Bind a governed
connection or use supported delegation; the runtime does not infer credential
provenance from an input name or string format. AKS http_request calls also
require a gateway-verified curated request template.
{
"method": "GET",
"url": "{{connections.customer_api.secrets.request_url}}",
"max_response_bytes": 1048576
}The request_url value must be a governed connection secret bound to an
installed curated definition; an arbitrary URL or caller-supplied header is not
credential provenance. Public request templates may use only their declared
bounded query values.
email_fetch
Generic IMAP inbox reader (Gmail / Google Workspace, Outlook / Microsoft 365,
or any IMAP server). The account name is connection configuration; the password
is declared in the manifest’s connection_secret_inputs and supplied as a
connection secret ref, never free text. Pair it with a
schedule trigger to poll a mailbox on an interval.
- id: fetch_offers
type: task
action: email_fetch
inputs:
imap_host: "{{connections.caem-mail.config.imap_host}}"
username: "{{connections.caem-mail.config.username}}"
password: "{{connections.caem-mail.secrets.password}}"
folder: INBOX
search: UNSEEN
max_results: 1
max_message_bytes: 1048576
max_attachment_bytes: 1048576
max_total_output_bytes: 4194304
mark_as_read: true
next: triageOutput: { "emails": [{ "id", "from", "subject", "date", "body", "attachments": [{ "filename", "mime_type", "size", "content_b64" }] }], "fetch_count": N }.
snowflake_query
{
"statement": "select count(*) as open_tickets from support.tickets where status = ?",
"params": ["open"]
}github_api
{
"operation": "search_issues",
"repo": "owner/name",
"query": "label:bug is:open"
}webhook_trigger
webhook_trigger is a payload-verification connector, not the main workload
trigger-binding control surface.
For workload ingress, use the Triggers guide. Use
webhook_trigger when a flow already has a payload and needs to verify or
normalize its signature and headers.
{
"body": "{\"event\":\"release.approved\"}",
"headers": {
"X-Signature-256": "abc123"
},
"secret_env_var": "WEBHOOK_HMAC",
"signature_header": "X-Signature-256",
"algorithm": "sha256",
"parse_json": true
}Plugins Page (Portal)
/operator/plugins lists the governed connector inventory: one row per
package-local tool_definition asset, joined against the workspace’s package
installs. Clicking any row opens a detail drawer showing the full plugin
description, JSON Schemas for inputs and outputs, version, category,
certification posture, and install availability state when the package is
installed. This page is read-only — it inspects catalog and install state, it
does not install packages or adopt updates.
Marketplace (Portal)
/operator/marketplace is the browse-and-install surface for whole packages
(not single connectors). It lists every package from GET /v1/packages/ as a
card — display name, package name and version, provenance (Official or
Community), a “Signed” badge when the package carries a source_digest, and
its asset/pack counts — with ?provenance= and ?category= URL filters plus
free-text search. Each card has View and Install buttons; both open
the package detail drawer.
The drawer lists the package’s packs (named installable subsets) and every asset in the package, each row showing its schema/content refs, its runtime implementation ref when executable, and an Executable/Declaration chip. An install-target form — optional environment id, optional runtime target id; leave both blank to install at workspace-default scope — drives two actions:
- Install package button —
POST /v1/package-installs/. Idempotent: installing the same(package, version, workspace, environment, runtime_target)scope twice returns the existing install (created: false) instead of erroring. - Adopt update button —
POST /v1/package-installs/adopt-update. Use this when the scope already has an older version of the package installed; it writes a new install/materialization for the newer version and never mutates the already-installed version’s rows.
The banner “Catalog availability is not runtime availability. Install to use.” states the Installed-vs-Available distinction from the lifecycle table above.
Binding an installed asset to a release is not yet exposed in the portal
— use orqestractl plugin bind, the SDK, or the API directly (see
Resolving And Binding Installed Assets
below). The portal defers a binding dialog until it can render against the
real binding contract rather than an invented shape.
Installing And Disabling Package Availability
Installing a package materializes the selected package-local assets into a
workspace scope. Disabling an install changes the plugin_install.state for that
workspace install; removing availability supersedes the install so it no longer
resolves at runtime. These operations are scoped to an install id, not a global
connector slug.
API
Install a package and then disable the resulting install:
# Install all assets in the core-connectors pack
curl -X POST http://localhost:8000/v1/package-installs/ \
-H "Content-Type: application/json" \
-d '{
"package_name": "orqestra-official-plugins",
"version": "0.1.0",
"workspace_id": "00000000-0000-0000-0000-000000000123",
"selected_pack_names": ["core-connectors"]
}'
# Disable an installed package
curl -X PATCH http://localhost:8000/v1/package-installs/<install_id>/state \
-H "Content-Type: application/json" \
-d '{"state": "disabled"}'
# Adopt a newer package version into the same scope
curl -X POST http://localhost:8000/v1/package-installs/adopt-update \
-H "Content-Type: application/json" \
-d '{
"package_name": "orqestra-official-plugins",
"version": "0.2.0",
"workspace_id": "00000000-0000-0000-0000-000000000123",
"selected_pack_names": ["core-connectors"]
}'
# Remove runtime availability entirely (supersedes the install; irreversible)
curl -X POST http://localhost:8000/v1/package-installs/<install_id>/remove-availabilityCLI
orqestractl plugin list
orqestractl plugin get orqestra-official-plugins
orqestractl plugin install orqestra-official-plugins 0.1.0 \
--workspace 00000000-0000-0000-0000-000000000123 \
--pack core-connectors
orqestractl plugin update orqestra-official-plugins 0.2.0 \
--workspace 00000000-0000-0000-0000-000000000123 \
--pack core-connectors
orqestractl plugin installs --workspace 00000000-0000-0000-0000-000000000123
orqestractl plugin disable <install_id>
orqestractl plugin enable <install_id>
orqestractl plugin remove <install_id>
orqestractl plugin provenance <install_id>SDK
from orqestra_sdk import OrqestraClient
async with OrqestraClient("http://localhost:8000") as client:
install = await client.plugins.install_package(
package_name="orqestra-official-plugins",
version="0.1.0",
workspace_id="00000000-0000-0000-0000-000000000123",
selected_pack_names=["core-connectors"],
)
await client.plugins.adopt_update(
package_name="orqestra-official-plugins",
version="0.2.0",
workspace_id="00000000-0000-0000-0000-000000000123",
selected_pack_names=["core-connectors"],
)
await client.plugins.disable_install(install["install_id"])
await client.plugins.remove_availability(install["install_id"])Resolving And Binding Installed Assets
Installed and even Available assets are not automatically usable — they must be explicitly bound to a release role (the Bound stage above). Binding addresses an asset by package ref, a stable identity distinct from any workspace-generated UUID:
pkg:<package_name>@<package_version>/<asset_kind>/<package_local_slug>@<asset_version># Read-only: explains why an asset does or does not resolve, never raises
orqestractl plugin resolve \
"pkg:orqestra-official-plugins@0.1.0/tool_definition/http-request@0.1.0" \
--workspace 00000000-0000-0000-0000-000000000123 \
--runtime-target-id orqestra_native
# Re-resolves the ref fail-closed, then binds the resolved asset_version_id
# to a release role in one step
orqestractl plugin bind <release_id> \
"pkg:orqestra-official-plugins@0.1.0/mcp_server/company-tools@0.1.0" \
mcp_server \
--workspace 00000000-0000-0000-0000-000000000123plugin bind fails closed: an uninstalled, disabled, wrong-runtime-target, or
installed-but-never-materialized ref is never bound — the command exits
non-zero with the resolution’s denial reason instead. It is equivalent to
orqestractl release bind <release_id> --asset-version <asset_version_id> --role <role> for any release-bindable asset kind, except it resolves the ref
first. connector_definition assets are the one exception: they bind through
a connection instance, not a release binding — use
orqestractl release bind-connection (see the
Connections & Secrets guide) after
resolving the ref to confirm the connector is installed and available.
Authoring A Plugin Package
An author ships a package by writing orqestra.plugin.yaml at the package
root — a kind: PluginPackage manifest. This is a distinct artifact from
kind: DeclarativePackage (the bundle orqestractl sync applies to converge
an operator’s desired workspace state, documented in the
CLI Reference): a PluginPackage
declares reusable, installable catalog content; a DeclarativePackage
declares one operator’s concrete resource instances. They use different
api_version/kind discriminators and are never interchangeable.
api_version: orqestra.ai/plugin/v1
kind: PluginPackage
metadata:
name: acme-crm-connectors # unique package distribution name
version: 0.1.0
publisher: acme
display_name: Acme CRM Connectors
license: Apache-2.0
provenance:
distribution_channel: in_repo # in_repo | registry | git (a HINT only —
# the trusted loader assigns the actual provenance class from distribution
# position, never from this field)
runtime:
entry_point_group: acme.plugins # Python entry-point group runtime
optional_dependency_group: runtime # implementations register under
packs:
- name: core
asset_refs:
- connector_definition/crm-account-lookup
- tool_definition/crm-account-lookup
assets:
- kind: connector_definition
slug: crm-account-lookup # package-local slug, unique within
version: 0.1.0 # (package_version, kind) — NOT a global slug
schema_ref: connector_definition.schema.json
content_ref: assets/connectors/crm_account_lookup.yaml
runtime:
implementation_ref: acme.plugins/crm_account_lookup
runtime_required: true
- kind: tool_definition
slug: crm-account-lookup
version: 0.1.0
schema_ref: tool_definition.schema.json
content_ref: assets/tools/crm_account_lookup.yaml
runtime:
implementation_ref: acme.plugins/crm_account_lookup
runtime_required: true
display_name: CRM Account Lookup
category: CRM & sales
icon: database
tags:
- crm
- lookupEvery asset entry’s kind is a governed AssetKind value minus
orchestration_flow (flows are not a package asset kind). The envelope above
— identity, packs, provenance hint, runtime wiring — is validated against
orqestra_plugin_manifest.schema.json; each asset body at content_ref is
validated separately against its own per-kind schema (schema_ref), e.g.
connector_definition.schema.json, tool_definition.schema.json, or
hook_definition.schema.json.
display_name, category, icon, tags, and icon_url are optional
catalog-display fields on the asset entry itself — not scraped from the asset
body. They drive the connector picker/marketplace tile: category groups
entries into taxonomy buckets (e.g. Messaging, Data & warehouses, Cloud, CRM &
sales, Productivity, Developer, AI & vectors) and defaults to integration
when omitted; icon is a Lucide icon name rendered on
the tile, falling back to a monogram when absent; icon_url is reserved for a
future brand-logo pack (not yet shipped) and renders in place of icon when
present.
Validate a manifest locally before publishing — no API calls, so it is safe to run in CI:
orqestractl plugin validate --dir path/to/acme-crm-connectorsThis checks schema conformance plus loader-equivalent semantic rules: no
duplicate (kind, slug) package-local identity, every pack asset_ref
resolves to a declared asset, content_ref never escapes the package root
(no path traversal or symlink escape), and any executable asset that declares
runtime_required: true has an implementation_ref. See
packages/orqestra-official-plugins/README.md
for the full package directory layout used by the checked-in first-party
package.
Connector Certification
Alongside per-install availability state, every connector carries a certification posture that records how trusted it is and how dangerous it is to run. The posture is platform-owned metadata supplied by trusted package/catalog declarations and materialized records. Runtime and tool-permission grant paths read it — connectors and callers cannot self-assert their own trust.
| Field | Values | Meaning |
|---|---|---|
certification_state | available, certified, production_approved, restricted, deprecated | Trust level. production_approved is the only production bar |
side_effect_class | none, read_only, read_write, destructive | Blast radius of the connector’s actions |
data_sensitivity | public, internal, confidential, restricted | Highest data class the connector handles |
egress_posture | none, first_party, allowlisted_external, external_network | Where the connector sends data |
secret_posture | none, optional, required | Whether the connector needs secrets |
version_compatibility | object | Supported version range / constraints |
default_policy_template | object | Suggested policy scaffold for grants |
certification_notes | list of strings | Free-form certification rationale |
These fields surface read-only in the portal plugins page, the plugin detail drawer, and on marketplace cards.
How posture is enforced
Certification metadata is not a second policy engine — it feeds the existing grant and runtime paths:
- Tool-permission grants fail closed at creation. A
deprecatedconnector can never be granted. Arestrictedconnector, or one whoseside_effect_classisdestructive, requires a non-empty approvaljustification, which is recorded in the governance audit chain. - Posture is platform-derived on the grant. The grant snapshots the connector’s current platform certification; callers cannot supply posture fields on a grant request.
- Unknown/uncertified toolsets are non-trusted. A connector the catalog does not know about (for example an external MCP or Hermes toolset that has not been certified) cannot be granted a permission — there is no implicit trust default.
Author-declared side-effect class and error-response contract
A tool’s author can declare intent directly in the contract
(tool.schema.json) and the plugin SDK manifest, separately from the
platform-owned certification posture above:
side_effect_class(author, five values):none,read_only,idempotent_write,read_write,destructive.idempotent_writemeans a write that is safe to repeat; authors who cannot prove idempotency must useread_write. The declaration is optional during migration — when absent, the legacy boolean is inferred and the catalog flags a posture gap (tool_contract_complete: false).error_response_contract(author): a bounded list of error rules, each with a stablecode, anoutcome(retryable/terminal/empty_result/partial_completion), an optionalmax_retry_delay_secondshint, andside_effect_may_have_committed/partial_output_safe_to_consumeflags.
The catalog resolves the declaration into three visible fields:
declared_side_effect_class (the author class), side_effect_class (the
effective four-value certification class the gate enforces — idempotent_write
maps to the conservative read_write), and retry_side_effect_class (the class
the runtime retry logic uses). An idempotent_write declaration only relaxes
retries once accepted — first-party manifests are accepted directly; external
manifests require a certified / production_approved state and no operator
side-effect-class override. Until then it stays clamped at read_write and shows
side_effect_declaration_source: declared_unaccepted.
At runtime, the executor classifies a tool failure against the contract: a
retryable outcome is retried (subject to the non-idempotent clamp and the
committed-side-effect guard), terminal / empty_result / partial_completion
are never retried, transport faults stay retryable infrastructure, egress/policy
denials are terminal, and a contract-declaring tool that emits an unstructured
failure is a terminal contract mismatch. Plugin authors raise the SDK’s
ToolError to drive this classification.
The ORQESTRA_TOOL_CONTRACT_COMPLETENESS_MODE setting governs rollout:
report_only (the default) surfaces undeclared tools as posture gaps without
blocking them; enforce omits undeclared tools from the minted runtime posture
so they fail closed.
These fields are the authoring vocabulary carried on the tool contract and
the plugin SDK manifest. Runtime certification posture and retry classification
are minted from the platform plugin catalog (first-party manifests plus
discovered plugins), not from individual tool assets — a side_effect_class set
on a standalone tool asset is schema-validated but does not by itself change
runtime retry or certification behavior.
Certification posture
Certification posture is platform-owned metadata attached to cataloged and materialized assets. The package lifecycle CLI installs, disables, removes, and inspects package availability; it does not edit certification posture.
Granting tool permissions
When granting a workload access to a connector, supply the slug and grant scope; the platform resolves the certification posture and enforces approval:
orqestractl tool-permission set <workload_id> \
--plugin-slug http_request \
--grant-type permanent \
--justification "approved by security review"The --justification is recorded in the audit chain and is required when the
connector’s posture is restricted or its side-effect class is destructive.
API Reference
List all plugin packages:
curl http://localhost:8000/v1/packages/Get one package by name:
curl http://localhost:8000/v1/packages/orqestra-official-pluginsThe response includes package-local asset metadata so portal and SDK surfaces can build forms and validate inputs consistently.
Best Practices
Use
taskfor connector work. Do not invent custom step types when the platform already models external integration as plugin-backed tasks.
Prefer release-bound governance for the surrounding workflow. Plugins are part of a governed release context, not standalone automation nodes.
Use the trigger-binding APIs for workload ingress. Treat plugin-based webhook verification as a task-level utility, not as a replacement for schedule/webhook/event trigger bindings.
Keep examples schema-shaped. Plugin input payloads should match the catalog
input_schema, not old ad hoc config examples.