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 workingDocumentation Index
Fetch the complete documentation index at: https://docs.myme.so/llms.txt
Use this file to discover all available pages before exploring further.
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 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. |
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’sruntime_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 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:Validates the manifest
The Zod schema check, plus type-identifier and scope-grammar validation. Rejected manifests never produce a Connection.
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.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.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.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.system.activity rows.
OAuth proxy and leased tokens
Connectors don’t hold OAuth tokens directly. The platform offers two patterns, declared per-capability inoauth_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).
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 declarestoken_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 declaretriggers: 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 declarestriggers: 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 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 siblingsystem.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.
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_requirementsis similarly omitted.webhook_verification— required by the schema for shape-uniformity, but the value is informational for a connector that doesn’t carry awebhooktrigger.bidirectional_handling— required for every connector, even read-only ones. The defaults absorb the read-only case; tombstone mapping isignorebecause there’s nothing to retract.
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; 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).