---
name: inferrex-build-flow
description: Create an Inferrex flow between connected systems (or into the Golden record), dry run it, show the user what it would write, then turn it on, sync it, schedule it or promote it to another environment. Use when the user asks to build, run, schedule, pause or promote a flow, pipeline or sync in Inferrex.
---

# Build and run a flow in Inferrex

A flow moves or changes data. In InferrexAPI paths and InferrexMCP tool names a flow is called a pipeline. They are the same thing. Say "flow" to the user.

A new flow is a draft. Nothing syncs until it is turned on. Always dry run before turning on a flow that writes to a system.

## When to use

- "Sync orders from the shop into finance."
- "Run that flow now." / "Run it every weekday morning."
- "Add the warehouse as another destination."
- "Promote this flow to Production."

If the flow is failing, use `inferrex-diagnose-flow`.

## Prerequisites

- **InferrexMCP** at `https://mcp.inferrex.com/mcp` (sign-in or API key in a header), or **InferrexAPI** at `https://api.inferrex.com` with `X-API-Key: $INFERREX_API_KEY`.
- Key scopes: `pipelines:read`, `pipelines:write`, `connections:read`, `mappings:read`, `jobs:read`, `workspace:read`.
- The source system is approved. For a flow between two systems, an approved mapping joins them (`inferrex-map-systems`).

## Steps: build

1. **Confirm the environment.** Call `workspace_get_active_environment`. Tell the user the `name` and `class`. A flow belongs to the environment it is created in. If the class is `production`, say so plainly and ask the user to confirm before any write.
2. **Find the parts.** Call `connection_list` for the system ids. For a flow between two systems, call `mappings_list` and pick the mapping that joins them (`source_connection_id`, `target_connection_id`). If the user has not approved it yet, stop and use `inferrex-map-systems`.
3. **Check for an existing flow.** Call `pipeline_list` with `connection_id` set to the source system. If a flow already does this, ask whether to use it.
4. **Create the draft.** Call `pipeline_create` with `name`, `sourceConnectionId`, and, for a flow into another system, `targetConnectionId` and `mappingManifestId`. Leave `targetConnectionId` out for a flow whose only destination is the Golden record. The flow starts as `draft`.
5. **Dry run.** Call `pipeline_dry_run` with `id`. Keep the `jobId`.
6. **Read the result.** Call `jobs_get_job` until `status` is `succeeded`, `failed` or `cancelled`. On success, read `result.preview`:
   - `recordsRead`,
   - for each destination: `wouldWrite`, `held` (records that would not be sent) and `samples`, each with `source` (as read) and `converted` (what would be sent).
7. **Show the user.** Give the counts and one or two samples per destination, source beside converted. If `held` is not zero, say so. If `wouldWrite` is zero, say nothing would be written and stop.
8. **Turn it on, only when the user says so.** Call `pipeline_activate`. A first full sync starts at once. Keep the `jobId` and follow it with `jobs_get_job`.
9. **Confirm the first sync.** Call `pipeline_history`. Report records read, written, skipped and failed for the latest run.

## Steps: run and schedule

- **Sync now:** `pipeline_sync` with `id` and `syncType`: `incremental` (default, only what changed), `full` or `initial`. The flow must be active. Ask before a `full` sync.
- **Schedule:** `pipelines_set_pipeline_schedule` with `scheduleType`: `realtime`, `scheduled`, `after_sync`, `manual` or `system`. For `scheduled`, send `scheduleCron` and `scheduleTimezone`. Read the cron expression back to the user in words before you save it.
- **Pause:** `pipelines_pause_pipeline`. **Turn back on:** `pipeline_activate`.
- **Stop a running sync or dry run:** `jobs_cancel_job`. A run that is already writing records finishes.
- **Move a flow to a newer mapping version:** `pipelines_apply_mapping_to_pipeline` with `mappingVersionId`, the version the user reviewed. It is refused if the mapping changed since.

## Steps: change destinations

1. Draft: `pipelines_create_flow_change` with `id`, `kind` (`add-destination` or `remove-destination`), `connectionId`, and `mappingId` when adding.
2. Show the user the answer's `riskLevel` and `preview` (`destinationsBefore`, `destinationsAfter`). Nothing is applied yet.
3. On a yes: `pipelines_confirm_flow_change`. The answer says whether it applied or now waits for approval.
4. On a no: `pipelines_discard_flow_change`.

## Steps: promote to another environment

1. Call `workspace_environments_list` and confirm the target with the user by name and class.
2. Call `pipelines_get_promotion_approval_ladder` and tell the user who must approve for that target.
3. On a yes: `pipelines_promote_pipeline` with `id` and `toEnvironmentId`.
4. Report `status`, `riskLevel`, `changeCount` and `policyDecision`. If it is refused because a system is not set up in the target environment, say which, and stop.

## Checks before any write

- Dry run before `pipeline_activate`, every time, and show the result.
- Get an explicit yes before `pipeline_activate`, a `full` sync, a confirm of a flow change, or a promotion.
- Never promote into `production` without the user naming Production themselves.

## Equivalents

| Step | InferrexMCP tool | InferrexAPI |
|---|---|---|
| Environment | `workspace_get_active_environment` | `GET /v1/workspace/environment` |
| List flows | `pipeline_list` | `GET /v1/pipelines` |
| Create | `pipeline_create` | `POST /v1/pipelines` |
| Read one | `pipeline_status` | `GET /v1/pipelines/:id` |
| Dry run | `pipeline_dry_run` | `POST /v1/pipelines/:id/dry-run` |
| Job | `jobs_get_job` | `GET /v1/jobs/:id` |
| Cancel job | `jobs_cancel_job` | `POST /v1/jobs/:id/cancel` |
| Turn on | `pipeline_activate` | `POST /v1/pipelines/:id/activate` |
| Sync now | `pipeline_sync` | `POST /v1/pipelines/:id/sync` |
| Runs | `pipeline_history` | `GET /v1/pipelines/:id/history` |
| Schedule | `pipelines_set_pipeline_schedule` | `PUT /v1/pipelines/:id/schedule` |
| Pause | `pipelines_pause_pipeline` | `POST /v1/pipelines/:id/pause` |
| Apply mapping | `pipelines_apply_mapping_to_pipeline` | `POST /v1/pipelines/:id/apply-mapping` |
| Draft a destination change | `pipelines_create_flow_change` | `POST /v1/pipelines/:id/changes` |
| Confirm it | `pipelines_confirm_flow_change` | `POST /v1/pipelines/:id/changes/:changeId/confirm` |
| Discard it | `pipelines_discard_flow_change` | `POST /v1/pipelines/:id/changes/:changeId/discard` |
| Approval ladder | `pipelines_get_promotion_approval_ladder` | `GET /v1/pipeline-promotions/approval-ladder` |
| Promote | `pipelines_promote_pipeline` | `POST /v1/pipelines/:id/promote` |

## Report back

- The flow's name, id, `status` and environment.
- The dry-run counts and whether anything was held.
- After turning on: the first sync's job `status` and record counts.
- Anything now waiting for another person's approval, and who must approve.

## Never

- Never turn on a flow without a dry run the user has seen.
- Never approve your own flow change or promotion. Where approvers are required, another member must approve.
- Never archive a flow (`pipelines_archive_pipeline`) unless the user asks for exactly that.
- Never change a flow's direction to `reverse` or `bidirectional` without the user asking. It makes the flow write to its source system.
- Never describe a flow as working because it was created or turned on. Report the result of a run.
