Skip to Content
Task guidesTechnical: Plugins & Connectors

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:

FieldDescription
namePackage name, such as orqestra-official-plugins
versionPackage version
packsNamed groups of package-local assets
assetsPackage-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:

StageWhat it means
CatalogedThe package manifest is loaded; GET /v1/packages/ lists it. No workspace state exists yet.
InstalledPOST /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).
AvailableThe 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.
BoundAn 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.
RuntimeResolvedThe 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-plugins

The 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: triage

Output: { "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-availability

CLI

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-000000000123

plugin 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 - lookup

Every 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-connectors

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

FieldValuesMeaning
certification_stateavailable, certified, production_approved, restricted, deprecatedTrust level. production_approved is the only production bar
side_effect_classnone, read_only, read_write, destructiveBlast radius of the connector’s actions
data_sensitivitypublic, internal, confidential, restrictedHighest data class the connector handles
egress_posturenone, first_party, allowlisted_external, external_networkWhere the connector sends data
secret_posturenone, optional, requiredWhether the connector needs secrets
version_compatibilityobjectSupported version range / constraints
default_policy_templateobjectSuggested policy scaffold for grants
certification_noteslist of stringsFree-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 deprecated connector can never be granted. A restricted connector, or one whose side_effect_class is destructive, requires a non-empty approval justification, 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_write means a write that is safe to repeat; authors who cannot prove idempotency must use read_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 stable code, an outcome (retryable / terminal / empty_result / partial_completion), an optional max_retry_delay_seconds hint, and side_effect_may_have_committed / partial_output_safe_to_consume flags.

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-plugins

The response includes package-local asset metadata so portal and SDK surfaces can build forms and validate inputs consistently.

Best Practices

Use task for 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.