# Connectors (/docs/connectors)



A **connector** is how a Ductor workflow step, routing hook, or API call reaches
a third-party system — and its **actions** are the governed units of work the
*executed* stage of the clearing lifecycle draws on. Ductor ships a catalog of
**hundreds of providers** — spanning CRM, e-commerce, payments, communications,
LMS, and dozens more categories — each action carrying machine-readable
semantics (mutation class, idempotency, retry, approval) and a parity tier that
records how completely it is implemented. The same machinery lets you add your
own without touching the core engine. Browse the live directory at
[ductor.io/connectors](https://ductor.io/connectors); provider icons are served at
`https://cdn.ductor.io/providers/<key>.svg`.

<Callout type="info" title="Where the commercial layer stands">
  Because every action already carries typed semantics and per-execution usage
  metering, the catalog is the foundation for a priced **action inventory**. Two
  of its axes now ship as opt-in surfaces: the
  [pricing engine](/docs/connectors/action-pricing) resolves an action's price
  before it executes (`connector_pricing.enabled`), and the
  [assurance axis](/docs/connectors/assurance) resolves a certified-or-community
  tier and projects it onto catalog reads (`connector_assurance.enabled`). Both
  are off by default.

  An agent worker's capped connector-action spend now settles: `max_cost_cents`
  is enforced against the resolved price before provider I/O, and a durable,
  default-off projector posts that spend to the balanced settlement journal as
  showback (the tenant's wallet is debited, the platform credited). What is
  <Status kind="roadmap" />: paying a *seller* — an action price carries no
  payee, so a settled purchase credits the platform, never a marketplace
  publisher — and settling governed-skill spend, which is capped but writes no
  durable record to settle from.
</Callout>

This section is the deep reference for that machinery: the registries that
describe providers, how a connection is authenticated and its credentials
encrypted, how triggers deliver inbound events, and how you author a provider of
your own. If you only want the one-paragraph mental model, the
[Connectors concept page](/docs/concepts/connectors) has it; this page starts the
long-form tour.

## The vocabulary [#the-vocabulary]

Four nouns show up constantly, and they are not the same thing. Getting them
straight is most of the battle.

| Term           | What it is                                                                                                                                                                                                                                                                                                                                        | Where it lives                                     |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| **Provider**   | A third-party system Ductor knows how to talk to (`hubspot`, `salesforce`, `slack`). A provider definition declares its display name, categories, supported auth types, and the keys of the actions and triggers it exposes. Providers are **Go code** — there is no runtime loader.                                                              | `domain/connector/provider.go` (`Provider` struct) |
| **Action**     | One operation in a provider's catalog (`hubspot.create_contact`). An immutable `ActionSpec` carries input and output schemas, access requirements, execution semantics, related operations, and an explicit runtime status. Executable actions bind a runtime function; catalog-only actions remain discoverable without pretending they can run. | `domain/connector/action.go` (`ActionSpec`)        |
| **Trigger**    | An event *source* a provider exposes (`hubspot.contact_created`). Delivered by webhook or by polling.                                                                                                                                                                                                                                             | `domain/connector/trigger.go` (`TriggerSpec`)      |
| **Connection** | One tenant's **authenticated instance** of a provider — the HubSpot account *this* customer linked, with its own encrypted credentials, config, and status. This is the thing a step actually dispatches against.                                                                                                                                 | `domain/connector/connection.go` (`Connection`)    |

<Callout type="info" title="Provider vs. provider config vs. connection">
  There is a fourth layer between provider and connection: a **provider config**
  (`ProviderConfigKey` + `EnvironmentID`) is *your* configured instance of a provider family — your
  OAuth client credentials, allowed scopes, and egress policy for, say, HubSpot in `prod`. A
  **connection** then belongs to a provider config and represents one end-customer's linked account
  under it. So the chain is: **provider family** (`hubspot`) → **provider config** (`hubspot-main`
  in `prod`) → **connection** (Acme's HubSpot portal).
</Callout>

## The mental model [#the-mental-model]

A provider is a **catalog entry plus code**. It says "here is HubSpot, here are
the 40 actions and 12 triggers it supports, here is how you authenticate to it."
It carries no tenant data and no secrets — a provider's `BaseURL` is an authoring
template that "must not contain tokens, headers, or tenant secrets."

A connection is where the tenant data lives. When Acme links their HubSpot
account, Ductor stores a `Connection` row: which provider, which tenant, the
encrypted credentials, non-secret config like the region, and a status. Nothing
in that row is plaintext-sensitive — credentials are AEAD ciphertext, decrypted
only in-process at dispatch time.

At dispatch, a caller names a `(providerKey, actionKey)` and a connection. Ductor
looks up the `ActionSpec`, resolves the connection into a short-lived, decrypted
`AuthContext`, runs the action's `Execute` function against the provider, and
records the outcome as a durable attempt.

<Callout type="info" title="Catalog breadth and runtime availability are separate">
  Some actions are imported as schema-rich catalog contracts before a safe provider runtime exists.
  Their `runtime_status` is `metadata_only` or `needs_runtime`, with a stable `runtime_reason`. They
  are available to search, inspect, and plan against, but execution remains blocked until a native
  runtime is registered. The catalog UI labels these actions **Catalog only**.
</Callout>

## Where connectors sit [#where-connectors-sit]

Connectors are an *egress* layer. They are invoked from three surfaces, all of
which converge on the same central action executor:

```mermaid
flowchart LR
    A[Workflow steps] --> X[Action executor]
    B[Management API] --> X
    C[Routing hooks] --> X
    X --> P[Provider]
```

* **Workflow steps** — an `action` step (or an `ai_action` / `ai_agent` step)
  names a connector action. This is the common case; the
  [Coordinator](/docs/concepts/coordinator-workers) dispatches it to a Step
  Worker, which executes it and records an `eec_workflow_step_attempt` (the
  `eec_` prefix marks Ductor's durable *Enterprise Eventing Core* tables).
* **The management API** — `POST /api/v2/connector/execute-action` runs an action
  synchronously, and `POST /api/v2/connector/action-jobs` enqueues a durable
  async job. Both are covered under [Connections](/docs/connectors/connections).
* **Routing hooks** — a routing decision can dispatch a connector action as a
  side effect of finalization.

<Callout title="A connector call is a durable attempt, not a fire-and-forget SDK call">
  Because dispatch runs through the durable attempt model, a connector action inherits retries,
  at-least-once delivery, and uniform observability for free. It also means an action can run **more
  than once** across a crash, so actions that mutate downstream state should carry an idempotency
  key — see [Architecture](/docs/connectors/architecture) and [Idempotency &
  Exactly-Once](/docs/concepts/idempotency).
</Callout>

## What's in this section [#whats-in-this-section]

<Cards>
  <Card title="Architecture" href="/docs/connectors/architecture">
    The ProviderRegistry, ActionRegistry, and ConnectionService; how an action is dispatched; the
    durable attempt and async job models; action semantics.
  </Card>

  <Card title="Authentication" href="/docs/connectors/authentication">
    Every auth type, how AuthContext is resolved at dispatch, and how credentials are AEAD-encrypted
    at rest with XChaCha20-Poly1305, BYOK, and key rotation.
  </Card>

  <Card title="Connections" href="/docs/connectors/connections">
    Creating and resolving connections, direct vs. route mode, and the management API with real
    request/response payloads.
  </Card>

  <Card title="Credential Lifecycle" href="/docs/connectors/credential-lifecycle">
    Credential health facts, the requirement scanner, hosted Connect-Link authorization requests,
    and auto-resume on reconnect.
  </Card>

  <Card title="Triggers & Polling" href="/docs/connectors/triggers-and-polling">
    Source components, provider webhook subscriptions, the provider-event envelope, the three
    trigger modes, signature verification, and coalescing.
  </Card>

  <Card title="The Sync Engine" href="/docs/connectors/sync-engine">
    Cached provider-record replication — sync variants, the scheduler, run leases, retention, and
    realtime sync channels.
  </Card>

  <Card title="Data Mapping" href="/docs/connectors/data-mapping">
    Mappers that normalize provider payloads, field catalogs and mapping profiles, and the governed
    connector function runtime.
  </Card>

  <Card title="The Catalog" href="/docs/connectors/catalog">
    The \~864-provider inventory, generated parity matrix, categories, release stages, the colocated
    provider layout, and the code generators.
  </Card>

  <Card title="Building a Provider" href="/docs/connectors/building-a-provider">
    Author a provider with ProviderSpec and RegisterFromSpec, write an action's Execute function,
    and declare input/output schemas.
  </Card>

  <Card title="Assisted Authoring" href="/docs/connectors/assisted-authoring">
    Draft, compile, dry-run, repair, and stage connector functions without bypassing governance.
  </Card>

  <Card title="Certification & Testing" href="/docs/connectors/certification-and-testing">
    The certification workbench, synthetic canaries, provider API drift intelligence, and the
    simulator/fixture dev tooling.
  </Card>

  <Card title="Marketplace & Deployments" href="/docs/connectors/marketplace-and-deployments">
    Immutable, promotable integration deployments and the extension marketplace gateway with
    destructive-change guardrails.
  </Card>

  <Card title="External MCP Providers" href="/docs/connectors/external-mcp-providers">
    Import a remote MCP server's tools as governed connector actions, with every call re-gated
    through the normal executor.
  </Card>

  <Card title="Outbound Integrations" href="/docs/connectors/outbound-integrations">
    Ductor as the event source — the iPaaS webhook adapter and the n8n community node for
    Zapier/n8n/custom consumers.
  </Card>

  <Card title="Policies, Quotas & Audit" href="/docs/connectors/policies-quotas-audit">
    Egress/placement policy, tenant provider policy, quota facts, execution admission, connection
    selection, GCRA rate limiting, and the audit hash chain.
  </Card>
</Cards>
