# Build a flow

> Build or change a Signalbox routing flow from a description in plain English. Use when someone asks to route, qualify, assign or book inbound leads, or to change how a flow works.

An agent reads this skill with `get_skill` and the name `build-a-flow`. It is also the MCP prompt `build-a-flow`, and part of the [Claude Code plugin](/agents/connect-other-clients/).

What follows is the skill as the agent reads it.

A flow decides what happens to a lead after a HubSpot form is submitted. You write it as one document, Signalbox checks it, you test it, and a person says when it goes live.

## 1. Find out what is wanted

Ask only for what you cannot look up. You need:

- **Which leads go where.** The tests ("companies under 11 people", "existing customers") and what each group gets.
- **Who gets them.** The company's existing owner, a queue, or a named rep.
- **What the visitor sees.** A calendar, more questions, a message, or nothing.
- **What HubSpot gets.** A deal, a task, a property, or nothing.

If the person gave all of this, do not ask again. Carry on.

## 2. Read the workspace

Call these before you write anything. A flow holds ids, and you must not invent one.

1. `get_started`: what is connected and what is missing. If HubSpot is not connected, stop and tell the person.
2. `list_node_types`: the steps there are. Then `get_node_type` for each type you mean to use: its settings, their shapes and its exits.
3. `list_fields`: the fields a condition can test.
4. `list_queues`, `list_reps`, `list_meeting_types`: the ids your settings will hold. For a deal, `list_hubspot` with `what` = `pipelines`.

If the flow needs a queue or a meeting type that does not exist, make it first with `save_queue` or `save_meeting_type` (Build level), and tell the person you did.

## 3. Write the draft

Start from `create_flow` with a name. It returns the three steps every flow begins with: `trigger`, `clean`, `lookup`. Then send the whole document with `save_flow_draft`.

Rules for the document:

- Keep `trigger -> clean -> lookup` first, linked by `next`. Add your steps after `lookup`.
- Give each node a short `id` of its own (`fit`, `q1`, `cal`). Leave `x` and `y` out: Signalbox places the nodes.
- An edge is `{"f": from node, "e": exit, "t": to node}`. Every exit of every step needs an edge. When nothing more should happen, link the exit to an `end` node.
- An If / else has one branch with the id `yes`, and the exits `yes` and `no`. For three or more groups use a Branch.
- A calendar needs a rep first: put a queue, owner or person step on every path to it.
- Steps marked slow (AI classify, Send webhook) go after the last thing the visitor sees.
- Use the revision `create_flow` or `get_flow` gave you as `fromRevision`.

A small example: companies under 11 people get a message; the rest go to a queue and see a calendar.

```json
{
  "nodes": [
    {"id": "t", "type": "trigger"},
    {"id": "cl", "type": "clean"},
    {"id": "lk", "type": "lookup"},
    {"id": "size", "type": "ifelse", "label": "Under 11 people?",
     "branches": [{"id": "yes", "name": "Yes", "match": "all",
       "conds": [{"f": "sopro.headcount", "op": "is less than", "v": "11"}]}]},
    {"id": "msg", "type": "message", "settings": {"message": "Thanks. We will be in touch."}},
    {"id": "q", "type": "queue", "settings": {"queueId": "<a queue id from list_queues>"}},
    {"id": "cal", "type": "calendar", "settings": {"meetingTypeId": "<a meeting type id>", "slots": "rep"}},
    {"id": "done", "type": "end"}
  ],
  "edges": [
    {"f": "t", "e": "next", "t": "cl"},
    {"f": "cl", "e": "next", "t": "lk"},
    {"f": "lk", "e": "next", "t": "size"},
    {"f": "size", "e": "yes", "t": "msg"},
    {"f": "size", "e": "no", "t": "q"},
    {"f": "msg", "e": "next", "t": "done"},
    {"f": "q", "e": "next", "t": "cal"},
    {"f": "cal", "e": "booked", "t": "done"},
    {"f": "cal", "e": "notBooked", "t": "done"},
    {"f": "cal", "e": "cancelled", "t": "done"}
  ]
}
```

A blank field fails every positive test. A company Sopro Data does not know has no headcount, so it takes the `no` exit above. Say so to the person when it matters, and add an `is blank` test if they want those leads handled apart.

## 4. Check it

Every save returns `issues` and an `outline`.

- Fix every issue with severity `error`. Each has a `fix` that says what to change. Save again. Repeat until `canPublish` is true.
- Read each `warning` and tell the person about any that matter.
- Read the `outline`. It is the flow in words. If it does not say what the person asked for, the flow is wrong.

## 5. Test it

Call `dry_run_flow` once for each group of leads the person described, with sample form values. Nothing is written. Check that `path` goes down the branch you expect.

- With a real company email, the company lookup and the HubSpot read are real.
- A dry run stops at the first step that waits for the visitor (a calendar, questions). That is expected.

If a sample goes the wrong way, read the step's `evidence`, fix the draft and test again.

## 6. Show the person, and ask

Show them the outline, what each sample lead did, any warnings, and the `link` to the flow in the editor.

**Do not publish until they say yes.** Publishing changes what real visitors get. When they say yes:

1. `publish_flow` (Publish level).
2. If the flow should run for a form that has no flow, ask which form, then `set_form_flow`. Check the form's `routingOn`; turn it on with `set_form_routing` only if they agree.

If your level is too low, the refusal says so. Tell the person the draft is ready and give them the link: they can publish it themselves or connect you again at a higher level.

## Changing a flow

Call `get_flow`. Change that document: keep every node you are not changing exactly as it is, with its `x` and `y`. Leave `x` and `y` off new nodes only if you are happy for the whole flow to be laid out again; otherwise give new nodes a position near their neighbours. A setting shown as `[hidden]` is a secret: send it back as it is and the stored value is kept. Save with the revision you read. Then check, test and ask as above.
