# Move Deal to Stage

Open this contact's deal in a pipeline stage, or move it if one is already open — one fact from the funnel's point of view, with an optional amount and a guard against re-opening a deal someone has since moved.

## Getting here

**Automate → Workflows**, open a workflow, click **+** on the canvas, then pick **Move Deal to Stage** under **Add Steps**.

## Before you start

- An **owner**, **admin** or **agent** can add this step and save the workflow. Publishing that workflow — the thing that makes it run on live conversations — is an **owner** or **admin** action.
- Building and running workflows depends on your plan; see [pricing](/pricing).
- This workspace needs at least one pipeline with stages, created under Pipelines. The drawer says so when there is none yet.

## Set up

1. Open the workflow and click **+** where the move belongs.
2. Pick **Move Deal to Stage**.
3. Choose the stage from the **Select a stage** dropdown — it lists every pipeline's stages, shown as pipeline · stage name.
4. Optional: fill in the field placeholder “Deal name when it is first opened” — used only the first time this contact's deal in that pipeline is created.
5. Optional: fill in the field placeholder “Amount, e.g. 150 or `{{payment.amount}}` (optional)”.
6. Optional: fill in the field placeholder “Only if the deal is still in… (stage name, optional)”, so the step does nothing unless the deal is still sitting where this flow left it.
7. Save the step, then publish the workflow. A saved but unpublished workflow opens or moves no deals.

## Fields

| Field | What it means | Default | Limits |
| --- | --- | --- | --- |
| Select a stage | The pipeline stage to land the deal in. This is an unlabelled dropdown; “Select a stage” is its own placeholder option, not a field label. | — | — |
| Deal name when it is first opened | The deal's name. This is placeholder text inside an unlabelled box, not a printed field label. | “Opportunity”, if left blank | Ignored when an already-open deal is being moved instead of created — there is no separate rename action. |
| Amount, e.g. 150 or {{payment.amount}} (optional) | The deal's value — a number, or a placeholder that resolves to one. Also placeholder text, not a printed label. | — | Applied whether the deal is being opened or moved. A placeholder that does not resolve to a number is skipped — logged on the server, but the run's own Activity tab shows nothing about it — never failing the step and never clearing a value the deal already has. |
| Only if the deal is still in… (stage name, optional) | Moves the deal only when it is currently in the named stage of the same pipeline, compared trimmed and without regard to case; otherwise the step does nothing and opens no new deal. | — | Meant for a step placed after a Wait, where a person may have moved the deal elsewhere by then. |

## How it works

- The step resolves the contact behind this conversation, then looks for that contact's OPEN deal in the target stage's pipeline. If there is one, it moves it; if there is none, it creates one — a single fact from the funnel's side, so an author never has to check first which case they are in.
- A contact can have at most one open deal per pipeline. The step never opens a second one for someone who already has one open there — it moves the existing deal instead, even if a fresh deal was intended.
- Moving to a stage marked **won** or **lost** also closes the deal, so it stops counting in every “still open” total on the board.
- Moving a deal to the stage it is already in does not reset **stage_changed_at** — a re-run flow does not make a deal look freshly stuck — though a supplied Amount is still written.
- With **Only if the deal is still in…** set and the deal not in that stage (or no open deal at all), the step still records a reason — it shows as a ⊘ mark in the run's step list, with the reason as its tooltip (e.g. “the deal is in ‘X’, no longer in ‘Y’ — nothing to move”), rather than being a fully silent success. It never opens a new deal in this case.
- When the run started from a payment slip read on the customer's message and the target stage is a **won** stage, the deal records that slip (amount, reference, bank) as evidence — only in that case; a deal won by a keyword or a person's own judgement records no evidence.
- Moving a deal from a workflow step dispatches this workspace's **Deal Stage Changed** trigger for other workflows, checked about once a minute by the same drain the board's own edits and the AI agent's moves go through — a pipeline can chain several flows together this way.

## When it fails

- This conversation has no linked contact: the step fails with **this conversation has no contact to open a deal for**.
- No stage matches — nothing was named, the named stage no longer exists, or the same stage name exists in more than one pipeline (an ambiguous name fails rather than guessing): the step fails with the exact reason, e.g. **more than one pipeline has a stage named “<name>” — pick the stage instead of naming it**.
- **Only if the deal is still in…** cannot be checked because reading the contact's deal fails: the step fails with **could not read this contact's deal**.
- A database error opening a brand-new deal fails the step with **could not create the deal**; a database error reading which deal is open fails with **could not read this contact's deals**; a database error moving an existing deal to a different stage fails with **could not move the deal**; and a database error updating only the Amount on a deal already in the target stage fails with **could not update the deal's value**.
- A failed move does not stop the run: the step is recorded failed and the flow moves straight to the next one, with the deal left exactly as it was.

## Limits

- It cannot open a second deal for a contact who already has one open in the same pipeline — see How it works.
- The deal name only applies the first time a deal opens; this step cannot rename an already-open deal.
- The amount cap is the same the board's own editor enforces (no negative amounts, and a ceiling of one billion) — a mis-resolved placeholder past that skips the write rather than saving a number the board would refuse, and the operator is not told: it is logged on the server only, with nothing shown in the run's Activity tab.
- **Only if the deal is still in…** never opens a new deal when the condition is not met — it only ever guards a move.
- A deal moved by this step is throttled against re-triggering the flows listening for the move: at most 6 dispatches of **Deal Stage Changed** per deal in a trailing hour when the mover is a workflow or a system rule (a person dragging the card, or the AI agent, is not counted), plus a workspace-wide hourly ceiling — past either, the extra moves simply do not dispatch that trigger. A drain outage older than an hour also drops the dispatch rather than firing it late.
- It does not notify anyone of the move on its own — pair it with **Assign To (human)** or **Add Comment** if a person should be told.

## Use cases

- Move the deal to **Pending Payment** and record the amount once a payment slip is read on the customer's message.
- A flow that quotes a deal, waits a day, then parks it as **Dormant** with **Only if the deal is still in “Quoted”** — so a deal a person already moved to **Won** or **Lost** in that day is left alone.

## Related

- [Update Lifecycle](https://inchat.inseller.my/help/workflows/update-lifecycle)
- [Create a Task](https://inchat.inseller.my/help/workflows/create-a-task)
- [Assign To (human)](https://inchat.inseller.my/help/workflows/assign-to-human)
- [Workflows: how a flow runs](https://inchat.inseller.my/help/workflows/workflows-overview)

---

Source files: `src/lib/workflows/ai-builder/spec.ts`, `src/lib/workflows/canvas-model.ts`, `src/components/workflows/WorkflowStepConfigDrawer.tsx`, `src/components/workflows/WorkflowHistory.tsx`, `src/lib/workflows/engine-record-actions.ts`, `src/lib/workflows/types.ts`, `src/lib/workflows/workflow-input-schema.ts`, `src/lib/pipelines/opportunities.ts`, `src/lib/pipelines/stage-events.ts`, `src/lib/workflows/engine-opportunity-guard.test.ts`, `src/components/workflows/WorkflowTriggerConfigDrawer.tsx`
