# Connector Lifecycle Saving a connector in the iframe **creates** it, but data doesn't move until you build a **pipeline**. This page covers that second half: the shared pipeline primitive, how ingress and egress differ, and how to check status, pause, resume, and delete an integration. All of the calls below run on your backend, behind the `MonadRequest` thunk described in the [API Reference](/sdks/embed/reference#the-monadrequest-thunk). The browser sends a connector `id`; your backend resolves the rest. ## The shared primitive Every pipeline is an input node and an output node joined by an edge. The raw call: ```ts const pipeline = await request(`/v2/${org}/pipelines/`, { method: 'POST', body: JSON.stringify({ name, enabled: true, nodes: [ { slug: 'in', component_id: inputId, component_type: 'input', enabled: true }, { slug: 'out', component_id: outputId, component_type: 'output', enabled: true }, ], edges: [ { from_node_instance_id: 'in', to_node_instance_id: 'out', conditions: { operator: 'always' }, }, ], }), }); // Poll GET /v2/{org}/pipelines/{id}/status until status is 'Running'. ``` Ingress and egress are this same call with the ends swapped. In **ingress**, the user supplies the input and you supply the output; in **egress**, you supply the source input and the user supplies the output. :::info Build is asynchronous `POST /v2/{org}/pipelines/` returns before the pipeline is `Running`. Poll `…/status` on an interval (about every two seconds, capped) and show a "connecting" state in the meantime. ::: ## Lifecycle helpers The SDK ships helpers so you don't hand-roll the common flows. Each takes a `request` thunk (see the [API Reference](/sdks/embed/reference#the-monadrequest-thunk)): - `buildDevNullPipeline({ request, org, inputId, inputName })` — stands up a throwaway sink for a freshly connected input (zero setup). - `findIntegrationPipeline({ request, org, inputId })` — resolves an input's pipeline and enabled state from just the input id. Returns `ResolvedIntegration | null`. - `setIntegrationEnabled({ request, org, pipelineId, enabled })` — pauses or resumes flow. `enableIntegration` / `disableIntegration` are conveniences over it. - `deleteIntegration({ request, org, pipelineId?, inputId?, outputId?, cleanup })` — removes an integration; `cleanup` decides what gets torn down (below). ## Ingress Your user connects a source. You decide where it flows. 1. The user configures the input; `onSave({ id })` fires with the connector id. 2. Build a pipeline from their input to a destination you control. For the zero-setup default, use `buildDevNullPipeline`. For a real destination, use the raw call above with `outputId` set to your pre-provisioned store. 3. The source shows up in the tenant's list as Active. ```ts await buildDevNullPipeline({ request, org, inputId, inputName }); ``` See [Best Practices](/sdks/embed/best-practices#default-ingress-destinations) for choosing between dev/null, a per-tenant store, and a shared store. ## Egress Your user connects a destination. You already have one source per team, provisioned once (your product's own connector, or a simple HTTP input). 1. The user configures the output; `onSave({ id })` fires. 2. Wire your team source to their new output with the same primitive. 3. One source fans out to every destination the tenant adds. The symmetry: ingress fixes the output (your store) and varies the input; egress fixes the input (your source) and varies the output. ## Status, pause, resume Resolve an integration's state from the connector id, without storing pipeline ids: ```ts const status = await findIntegrationPipeline({ request, org, inputId }); // { org, pipelineId, inputId, outputId?, enabled } | null // (null means connected but no pipeline) await setIntegrationEnabled({ // pause request, org, pipelineId: status.pipelineId, enabled: false, }); ``` Show users **Active** and **Paused**, not "pipeline." The pipeline is your implementation detail. ## Delete and cleanup Deleting is the other place ingress and egress diverge, because what's safe to remove depends on whether the other end is shared. `deleteIntegration` takes a `cleanup` policy: | Policy | Tears down | Use when | | --- | --- | --- | | `CLEANUP_FULL` | pipeline, input, and output | Ingress to a throwaway dev/null sink; nothing is shared. | | `CLEANUP_KEEP_OUTPUT` | pipeline and input (keeps the output) | Ingress to a shared store other inputs still feed. | | `CLEANUP_PIPELINE_ONLY` | pipeline only | You want to keep both ends. | For anything the presets don't cover, pass a custom `{ pipeline?, input?, output? }` — an omitted flag defaults to delete. **Egress needs a custom policy:** keep the shared source (`input: false`) and remove the pipeline and the user's output. The source feeds other tenants' destinations, so it never gets deleted. :::warning Resolve-then-act The browser sends a connector id; your backend looks up the pipeline and output ids server-side rather than trusting ids from the client — the same rule as in [Tenancy](/sdks/embed/auth-tenancy#tenancy-and-teams). ::: ## Next - [Best Practices](/sdks/embed/best-practices) — default inputs/outputs, catalog caching, and deployment. - [API Reference](/sdks/embed/reference) — the request thunk, options, and route contract. ## Support For additional support contact support@monad.com