> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myme.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrations

> Published manifests that connect Marfa to external services — runtime tiers, manifest contract, install flow

An Integration is a publishable, installable description of how Marfa talks to an external service or runs a space-internal automation. It carries everything a space needs to stand up a working `integration`-kind Connection: the trigger declarations, the OAuth or webhook authentication shape (where applicable), the runtime requirements, the bidirectional handling rules, and the permissions the connector needs against the space's items.

Integrations are persisted as `system.integration` items — one item per (`name`, `version`) pair. A space installs one specific version into a Connection; later versions are sibling items, not edits.

## The manifest contract

The manifest is the canonical description. The platform validates it on registration and on every install. Required fields:

| Field                     | Meaning                                                                                                                                                                                                                                                                                                         |             |                                                   |                                                                                                   |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `name`                    | Publisher-namespaced identifier — `acme.calendar-sync`. Same shape as a publisher type identifier.                                                                                                                                                                                                              |             |                                                   |                                                                                                   |
| `version`                 | Semver. Used to dedupe (`name` + `version` is the registration key).                                                                                                                                                                                                                                            |             |                                                   |                                                                                                   |
| `publisher`               | Human-readable publisher name shown to users at install time.                                                                                                                                                                                                                                                   |             |                                                   |                                                                                                   |
| `description`             | One-line summary shown to users at install time.                                                                                                                                                                                                                                                                |             |                                                   |                                                                                                   |
| `manifest_schema_version` | Semver of the manifest contract itself. The server rejects manifests whose major exceeds the supported major.                                                                                                                                                                                                   |             |                                                   |                                                                                                   |
| `direction`               | `read`, `write`, or `both` — the data flow this connector implements.                                                                                                                                                                                                                                           |             |                                                   |                                                                                                   |
| `target_types`            | Item types the connector reads or writes.                                                                                                                                                                                                                                                                       |             |                                                   |                                                                                                   |
| `triggers`                | Array of discriminated `{ type, config? }` objects. `type` is one of `schedule`, `webhook`, `item-event`, `manual`. Schedule triggers carry `config: { cron }`; the others have no config payload today.                                                                                                        |             |                                                   |                                                                                                   |
| `runtime_compatibility`   | One or more of `hosted`, `self-hosted`, `local` — which runtime tiers can host this connector.                                                                                                                                                                                                                  |             |                                                   |                                                                                                   |
| `bidirectional_handling`  | The four positions: `echo_ttl_seconds`, `lag_window_seconds`, `tombstone_mapping` (\`state-trashed                                                                                                                                                                                                              | prompt-user | ignore`), `partial\_write\_mode` (`all-or-nothing | accept-partial`). Required even when `direction !== "both"\`; defaults absorb the read-only case. |
| `oauth_requirements`      | Per-capability map — `proxy` (route through Marfa's OAuth proxy) or `leased` (issue a short-TTL bearer for the connector's own use).                                                                                                                                                                            |             |                                                   |                                                                                                   |
| `token_requirements`      | Per-capability map for connectors authenticated by a static upstream API token (rather than OAuth). Each entry declares `required`; the install pipeline gates on a matching `kind: api_token` credential being supplied as `credential_ref`.                                                                   |             |                                                   |                                                                                                   |
| `webhook_verification`    | Discriminated on `method`: `hmac-sha256`, `slack`, `stripe`, `github`, `google-channel`, or `cloudflare-email`. The platform's adapter for each method lives in [`@withmarfa/webhooks`](https://www.npmjs.com/package/@withmarfa/webhooks) and is consumed by both the Cloudflare control plane and the server. |             |                                                   |                                                                                                   |
| `permissions` (optional)  | Per-axis grant — `extension` and `edge` maps. The install pipeline translates these onto the runtime credential.                                                                                                                                                                                                |             |                                                   |                                                                                                   |

Full schema and each field's accepted values live in the `IntegrationManifestSchema` Zod schema in `@withmarfa/shared` — published as a JSON Schema artefact alongside the SDK.

## Runtime tiers

A connector runs on one of three runtime tiers. The manifest's `runtime_compatibility` declares which tiers it supports; a space's deployment chooses among them. The enum values are `hosted | self-hosted | local`.

| Tier (`runtime_compatibility` value) | When it applies                                                                                                                                                                                             |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `hosted`                             | The default for TypeScript connectors. Each Integration deploys as a Cloudflare Worker; per-Connection state lives in a Durable Object; queues handle scheduled polls, webhook receipts, and reactive runs. |
| `self-hosted`                        | The connector runs inside a self-hosted Marfa deployment's own runtime — a Node.js process or container the operator manages directly.                                                                      |
| `local`                              | The connector runs on the user's machine, not in hosted infrastructure. Used by the sync agent and by users who prefer to keep the runtime under their own control.                                         |

Self-hosted Marfa deployments can mix `self-hosted` and `local` tiers; the manifest's compatibility list determines what's available where.

## Install flow

The platform install pipeline is uniform across runtime tiers. The user installs an Integration through the console; the server then:

<Steps>
  <Step title="Validates the manifest">
    The Zod schema check, plus type-identifier and scope-grammar validation. Rejected manifests never produce a Connection.
  </Step>

  <Step title="Renders the consent screen">
    Mirrors the `/auth/authorize` consent precedent — shows the user the scopes the connector wants, the runtime tier, the manifest's declared OAuth requirements. The user approves, modifies, or cancels.
  </Step>

  <Step title="Persists the credential">
    The credential shape follows the connector's authentication model. For OAuth-backed connectors, the user supplies the OAuth provider config (client\_id, client\_secret); the server encrypts the secret and creates a `kind: oauth_token` `system.credential`. For token-authenticated connectors (manifests declaring `token_requirements`), the user supplies an upstream API token; the server encrypts the bearer and creates a `kind: api_token` credential carrying the upstream base URL and the `auth_scheme` it expects (`Bearer`, `Token`, or `Basic`). Both shapes are referenced by the Connection via `credential_ref` and resolved by the proxy at request time.
  </Step>

  <Step title="Mints a runtime credential">
    A short-TTL `system.credential` of `kind: api_key`, scoped to the manifest's declared permissions and bound to the connection\_id. The runtime broker refreshes this transparently as the connector runs.
  </Step>

  <Step title="Creates the Connection">
    A `system.connection` item with `kind: integration`, `integration_ref` pointing at the manifest, `credential_ref` pointing at the persisted credential, and the manifest-derived `configuration`, `direction`, `triggers`, and `runtime_compatibility` fields.
  </Step>

  <Step title="Bootstraps OAuth (when applicable)">
    For connectors whose manifest declares `oauth_requirements`, the consent flow continues into an OAuth Authorization Code dance against the upstream service. Tokens land in encrypted storage; the proxy reads them at request time.
  </Step>
</Steps>

The result: a Connection ready to receive triggers and emit `system.activity` rows.

## OAuth proxy and leased tokens

Connectors don't hold OAuth tokens directly. The platform offers two patterns, declared per-capability in `oauth_requirements`:

* **`proxy`** — the connector calls a Marfa endpoint with its runtime credential; Marfa injects the upstream OAuth bearer and forwards the request. Tokens never leave the platform.
* **`leased`** — the connector requests a short-TTL bearer for direct calls (used when the proxy can't carry the request shape — multipart uploads, streaming, non-HTTP protocols).

Both routes audit every call. Refresh-on-401 with refresh-token rotation is handled centrally; replay detection trips the Connection into `runtime_status: reauth_required` and surfaces an `action_required` activity row.

## Token-authenticated connectors

Not every upstream uses OAuth. A connector targeting an API authenticated by a long-lived bearer token declares `token_requirements` in its manifest; the install pipeline gates on a `kind: api_token` credential supplied as `credential_ref`. The credential carries the encrypted bearer, the upstream base URL, and the `auth_scheme` to stamp on the request header (`Bearer`, `Token`, or `Basic` — the wire format some services demand instead of `Bearer`).

The proxy stamps the configured `auth_scheme` on the upstream call uniformly with the OAuth path. From the connector's perspective the call shape is identical — it issues a request against the runtime credential and the platform substitutes the upstream auth. The substantive difference is the absence of a refresh dance: static tokens don't expire and have no refresh primitive. A 401 from the upstream surfaces as `action_required` and trips the Connection into `runtime_status: reauth_required` — the user updates the token to recover.

A single `kind: api_token` credential can be shared across Connections that target the same upstream base. When a connector's upstream lives on a different host than the credential's default, the Connection sets `properties.configuration.upstream_base_url_override` and the proxy consults it before falling back to the credential's URL. This lets a family of related connectors share one credential row across distinct upstream hosts.

## Reactive runs and cycle detection

Connectors that declare `triggers: item-event` subscribe to changes on `target_types`. The platform routes events through a per-Connection queue with backpressure; cycle metadata (originating connection, hop count) flows on every event so a connector that writes back into Marfa cannot infinite-loop. Per-space hop budgets are configurable; the default ceiling of five steps protects against runaway chains.

Self-events are filtered upstream — a Connection never receives events its own writes produced.

## Inbound webhooks

When a connector declares `triggers: webhook`, the platform exposes a public receipt endpoint per subscription, verifies the signature using the adapter named in `webhook_verification.method`, deduplicates on the sender's delivery id, and enqueues for the per-Integration Worker. See [Inbound webhooks](/api/inbound-webhooks) for the full surface — the four verification adapters, dedup semantics, the per-Connection DLQ, and the manual replay endpoint.

## Versioning and upgrades

Manifests are versioned by semver. A patch or minor bump produces a new sibling `system.integration` item; existing Connections stay pinned to the version they installed. Major bumps are effectively new Integrations — explicit upgrade required.

The runtime credential's permissions are stamped from the manifest at install time. A consenting reader will see exactly what each version requires.

## A minimal example — the RSS Watcher

The simplest connector possible is a schedule-driven reader against a public source. `withmarfa.rss-watcher` ships in-tree and polls an Atom or RSS feed on a cron, creating a `core.bookmark` per new entry. No OAuth, no token, no webhook — just the manifest declaring its shape and a handler that runs once per tick.

```ts theme={null}
export const RSS_WATCHER_MANIFEST: IntegrationManifest = {
  name: "withmarfa.rss-watcher",
  version: "0.1.0",
  manifest_schema_version: "1.0.0",
  publisher: "withmarfa",
  description:
    "Polls an Atom or RSS feed on a schedule and creates a core.bookmark per new entry.",
  direction: "read",
  runtime_compatibility: ["hosted", "local"],
  target_types: ["core.bookmark"],
  triggers: [{ type: "schedule", config: { cron: "0 * * * *" } }],
  bidirectional_handling: {
    echo_ttl_seconds: 60,
    lag_window_seconds: 60,
    tombstone_mapping: "ignore",
    partial_write_mode: "accept-partial",
  },
  oauth_requirements: {},
  webhook_verification: { method: "hmac-sha256" },
  permissions: {
    extension: { "connection.runtime": "write" },
    edge: {},
  },
};
```

Three details worth calling out:

* **`oauth_requirements: {}`** — the connector authenticates against the upstream by virtue of the feed being public. No OAuth provider, no token credential required at install. `token_requirements` is similarly omitted.
* **`webhook_verification`** — required by the schema for shape-uniformity, but the value is informational for a connector that doesn't carry a `webhook` trigger.
* **`bidirectional_handling`** — required for every connector, even read-only ones. The defaults absorb the read-only case; tombstone mapping is `ignore` because there's nothing to retract.

The handler is a single `registerScheduleHandler` call against `@withmarfa/runtime-sdk`, fetching the feed, diffing against the connection's cursor, and emitting `CreateItemInput` per new entry. The same `dist/local.js` entry runs on both [runtime substrates](/concepts/runtime-substrates); the connector author writes the handler once.

Use this shape as a starting point — every other in-tree connector layers more onto the same skeleton (OAuth proxy for the Google family, token credentials for Todoist / Readwise / Raindrop, an `item-event` trigger for bidirectional sync, a `webhook` trigger for push-driven sources).
