# The Connector Catalog (/docs/connectors/catalog)



The catalog is Ductor's **action inventory** — the governed set of typed actions
the *executed* stage draws on. Ductor combines imported catalog metadata with
hand-authored Go implementations. Rather than list every provider here — the set
grows continuously — this page explains how the catalog is organized, how to
discover providers programmatically, and how the inventory and provider sources
are laid out on disk.

The public directory's generated offline seed currently contains
**871 providers** and exposes
**11,726 action contracts**:
**11,499 executable** actions and
**227 catalog-only** actions whose runtime
implementation is still pending. These values are calculated from the same
committed snapshot used to seed the web catalog, so refreshing that snapshot also
updates this page at build time. Live deployment counts can differ because the API
always reflects the exact binary you built.

<Callout type="info" title="Browse the live catalog">
  The authoritative, always-current directory is at
  [ductor.io/connectors](https://ductor.io/connectors), backed by the JSON API at
  [ductor.io/api/connectors](https://ductor.io/api/connectors). Provider icons are
  served at `https://cdn.ductor.io/providers/<key>.svg`. The API endpoints below
  return the catalog from *your* deployment, reflecting exactly the providers your
  binary was built with.
</Callout>

## The provider inventory and parity matrix [#the-provider-inventory-and-parity-matrix]

Two documents describe the shipped catalog, and **both are generated — never
hand-edit them**:

* **`docs/connector-system/provider-inventory.md`** — one row per provider, with
  its source, parity tier, and capability flags (OAuth, refresh, shared webhook,
  dynamic props, poll triggers, delivery triggers). Generated by
  `connectorbootstrapgen`.
* **`docs/connector-system/15-parity-matrix.md`** — cross-references Ductor's
  providers against the donor catalogs. Generated by
  `go run ./internal/cmd/connectorparitymatrix`.

<Callout type="warn" title="Generated files are regenerated, not hand-edited">
  `provider-inventory.md`, the parity matrix, and the `cmd/ductor/fx_connector_generated_*.go`
  wiring files are deterministic outputs. Editing them by hand is always wrong — regenerate instead
  (see [The generators](#the-code-generators) below). Parallel branches that each add a provider
  *will* conflict in these files textually; resolve by re-running the generators after merge, never
  by hand-merging.
</Callout>

### Parity tiers [#parity-tiers]

Each inventory row carries a **parity tier** recording how completely the provider
is implemented. Imported providers land at:

| Tier          | Meaning                                                                                      |
| ------------- | -------------------------------------------------------------------------------------------- |
| `imported`    | Metadata imported from a donor catalog (auth, base URL, scopes) with no curated actions yet. |
| `partial`     | Some curated actions/triggers on top of imported metadata.                                   |
| `object_tier` | Implements the normalized object API (get/list/search/create/update records).                |

Hand-authored Go providers additionally carry richer tiers such as `full` and
`native`. The bulk of the catalog is `imported` today, with `partial` and
`object_tier` providers growing as authors curate them.

<Callout type="info" title="Parity tier vs. parity-matrix status">
  Don't confuse the inventory's *parity tier* with the parity **matrix**'s *status* column. The
  matrix compares Ductor against donors and labels each row `covered`, `gap&#x60;, or &#x2A;*`ductor-only`**
  (Ductor ships it but the compared donor catalogs don't). `ductor-only` is a comparison status, not
  an implementation tier — an `imported` provider can still be `ductor-only`.
</Callout>

<Callout type="info" title="Parity tier is not a pricing tier">
  A parity tier is an *implementation completeness* signal, and it ships today.
  It is **not** a price. A separate [pricing tier](/docs/connectors/action-pricing)
  (`free`, `included`, `standard`, `premium`, `metered`) exists in the pricing
  engine, and a certified/community *assurance* tier is a third axis that is not
  built at all — three independent signals that should never be read as one.
  Today the catalog surfaces none of them but parity: it carries no prices, and
  nothing here bills per action.
</Callout>

## Categories [#categories]

Every provider declares one or more `Category` values (`domain/connector/provider.go`).
The taxonomy has 39 categories:

|                 |                |                |                  |
| --------------- | -------------- | -------------- | ---------------- |
| `crm`           | `marketing`    | `notification` | `ai`             |
| `telephony`     | `enrichment`   | `payment`      | `data_warehouse` |
| `file_storage`  | `dev_tools`    | `internal`     | `database`       |
| `analytics`     | `productivity` | `hr`           | `accounting`     |
| `ats`           | `social`       | `design`       | `erp`            |
| `iam`           | `mcp`          | `legal`        | `knowledge_base` |
| `storage`       | `video`        | `invoicing`    | `surveys`        |
| `cms`           | `banking`      | `ticketing`    | `support`        |
| `communication` | `e_commerce`   | `gaming`       | `search`         |
| `sports`        | `other`        |                |                  |

## Release stages [#release-stages]

A provider's maturity is a `ReleaseStage`: `alpha`, `beta`, `generally_available`,
or `deprecated`. A provider may also set the boolean `Deprecated` flag with a
`DeprecationMessage`. Tenant provider policy can require a minimum release stage
before a connection may be created — see
[Policies, Quotas & Audit](/docs/connectors/policies-quotas-audit).

## Discovering providers, actions, and triggers [#discovering-providers-actions-and-triggers]

The management API exposes read-only catalog endpoints under
`/api/v2/connector/` (`service ConnectorService`):

```bash
# List providers (paginated; page_size 0-100), optionally filtered.
curl 'https://your-host/api/v2/connector/providers?categories=crm&page_size=50' \
  -H 'Authorization: Bearer <token>'
# → { "providers": [ { "key": "hubspot", "display_name": "HubSpot", ... } ], "next_page_token": "..." }

# Get one provider by key.
curl https://your-host/api/v2/connector/providers/hubspot -H 'Authorization: Bearer <token>'

# Actions for a provider, and one action's full spec (input properties, output schema, semantics).
curl 'https://your-host/api/v2/connector/actions?provider_key=hubspot' -H 'Authorization: Bearer <token>'
curl https://your-host/api/v2/connector/actions/hubspot.create_contact -H 'Authorization: Bearer <token>'

# Triggers for a provider, and one trigger's spec.
curl 'https://your-host/api/v2/connector/triggers?provider_key=hubspot' -H 'Authorization: Bearer <token>'
curl https://your-host/api/v2/connector/triggers/hubspot.contact_created -H 'Authorization: Bearer <token>'
```

Fetching a single action returns its input `Properties`, `OutputSchema`,
`Semantics`, required OAuth scopes, provider-native permissions, related action
keys, and runtime availability — enough to render a form, validate arguments,
explain setup requirements, and know whether the action is safe to retry or can
run at all. The registry keeps live counts (`ProviderCount`,
`ActionCount`, `TriggerCount`, `ModelCount`, `MapperCount`) via `RegistryStats`
(`application/connector/registrar.go`), which is what the catalog surfaces report.

## Action contracts and runtime availability [#action-contracts-and-runtime-availability]

Catalog enrichment is additive. A curated contract always wins; imported
metadata fills only missing scopes, input schemas, output schemas, provider
permissions, and action relationships. It never replaces a native request
handler or rewrites an existing execution contract.

| Field                  | What it tells you                                                                                                            |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `properties`           | The action's input fields, including nested object and array fields.                                                         |
| `output_schema`        | JSON Schema for the provider response. Builders use it for downstream output completion and type checks.                     |
| `required_scopes`      | OAuth scopes the selected connection must grant before execution.                                                            |
| `provider_permissions` | Provider roles, products, or grants configured outside the OAuth consent screen. These are setup guidance, not OAuth scopes. |
| `relations`            | Useful follow-up actions and optional start/status/cancel operations for long-running provider work.                         |
| `runtime_status`       | `executable`, `metadata_only`, `needs_runtime`, or `blocked_by_policy`.                                                      |
| `runtime_reason`       | A stable explanation for a non-executable status, such as `catalog_runtime_translation_required` or `dynamic_endpoint`.      |

<Callout type="warn" title="An action count is not an executability count">
  Catalog-only actions are intentionally visible so builders and agents can discover their schemas
  and requirements. The action executor still rejects them. Check `runtime_status == "executable"`
  before presenting an operation as runnable.
</Callout>

<Callout title="The catalog reflects your build">
  Providers are Go code compiled into the binary — there is no runtime provider loader — so the
  providers available in *your* deployment are exactly the ones bundled at build time. The live
  directory on ductor.io shows the full upstream catalog; your instance shows what you shipped.
</Callout>

## The colocated provider layout [#the-colocated-provider-layout]

New providers are authored entirely inside their own directory — **one directory =
one provider = complete truth** — so any number of providers can be authored on
parallel branches without touching a shared *authored* file
(`docs/connector-system/16-provider-authoring.md`):

<Files>
  <Folder name="infrastructure/connector/providers/<key>/">
    <File name="provider.yaml — bootstrap manifest entry (wiring, + optional provider block)" />

    <File name="doc.go — package doc" />

    <File name="provider.go — dc.Provider definition (auth types, action/trigger keys)" />

    <File name="client.go — shared connector HTTP client wiring" />

    <File name="auth.go — credential/OAuth resolution + testAuth hook" />

    <File name="register.go — wires the provider + actions/triggers into the registries" />

    <File name="upstream.yaml — provenance + per-action parity status" />

    <Folder name="actions/ — curated Go actions (helpers.go + one file per action)" />

    <Folder name="triggers/ — optional: poll/webhook trigger specs" />

    <Folder name="samples/ — fixture payloads (created empty at scaffold time)" />

    <File name="corpus.curated.json — optional additive action/schema/permission overlay" />
  </Folder>
</Files>

<Callout type="warn" title="One key, one manifest, one corpus">
  A provider key must appear in **exactly one** manifest (colocated or central)
  **and exactly one** corpus source (colocated `corpus.json` or
  `gen/connector-actions/<key>.json`). Duplicate manifest keys fail
  `connectorbootstrapgen`; duplicate corpus keys fail
  `scripts/sync-connector-corpus.sh`. This is the most common way to break the
  generators.
</Callout>

## The code generators [#the-code-generators]

Authoring flows through a small set of generators — never edit the wiring or
inventory by hand:

* **`internal/cmd/connectorscaffoldgen`** (and `tools/connectorgen`) — scaffold a
  new provider package plus its colocated `provider.yaml`.
* **`internal/cmd/connectorbootstrapgen`** — regenerate the wiring and provider
  inventory. Run it via `go generate ./cmd/ductor/...`; colocated manifests are
  picked up through the `infrastructure/connector/providers/*/provider.yaml` glob.
* **`tools/connector-icons`** — stage a provider's brand SVG; once uploaded to R2
  it serves at `https://cdn.ductor.io/providers/<key>.svg`, which the manifest's
  `icon_url` must use.

The public site keeps a committed offline seed for boot-time ingestion. Refresh
it from sibling backend and frontend checkouts after connector corpus changes:

```bash
cd ductor-frontend
pnpm --filter @ductor-frontend/db run catalog:sync-connectors:write
pnpm --filter @ductor-frontend/db run catalog:sync-connectors
```

The sync resolves provider aliases, merges base and curated corpora with the same
gap-only rules as the backend, retains native action contracts, updates action
counts, and rejects forbidden source branding. Set `DUCTOR_REPO` when the backend
is not checked out at `../ductor`.

Validate the result with `make connector-manifest-validate` and the provider
contract tests under `infrastructure/connector/providers/`.

## Where to go next [#where-to-go-next]

* [Building a Provider](/docs/connectors/building-a-provider) — write the actions and register a provider.
* [Certification & Testing](/docs/connectors/certification-and-testing) — prove a provider's behavior before promoting it.
* [Connections](/docs/connectors/connections) — link an account for a catalog provider.
