# Randomizer (A/B split)

Send each contact down exactly one of several weighted lanes at random, using a fixed per-contact draw so the same contact keeps landing in the same lane as long as the step and its weights stay put.

## Getting here

**Automate → Workflows**, open a workflow, click **+** on the canvas, then pick **Randomizer (A/B split)** under **Add Steps**.

## Before you start

- An **owner**, **admin** or **agent** can add this step and save the workflow. Publishing it is an **owner** or **admin** action.
- Building and running workflows depends on your plan; see [pricing](/pricing).
- Nothing outside Inchat has to be connected — the split needs no field of its own to work from.

## Set up

1. Open the workflow and click **+** where the split belongs.
2. Pick **Randomizer (A/B split)**.
3. Set a **Weight** on each lane — the share of traffic shown next to it updates as you type. Two lanes are the minimum; **+ Add lane** adds more for an A/B/C test.
4. Optionally name each lane; an unnamed lane is labelled A, B, C… in order.
5. Save the step, then publish the workflow.

## Fields

| Field | What it means | Default | Limits |
| --- | --- | --- | --- |
| Weight | A lane's share of traffic relative to the other lanes' weights — not a fixed percentage of the whole. Two lanes weighted 20 and 80 send roughly a fifth of contacts to the first. | 50 on each of the two lanes a fresh Randomizer step starts with | 1–100 per lane in the drawer; a weight that is missing, zero or negative is treated by the engine as 1, never as zero traffic — this only comes up for a lane built some other way than through the drawer's own "+ Add lane", since the drawer itself always writes a real number. |

## How it works

- Each run draws one number and picks the lane it falls into, in proportion to the lanes' weights — every contact goes down exactly one lane, never more than one and never none.
- The draw itself is **sticky**: it is a hash of the workflow, the run's computed position at this step, and the contact, not a fresh dice roll each time — so the same contact reaching this step again the SAME way (a reopened conversation, a second trigger fire) produces the same fixed number every time. Reaching it by a different route in the same workflow — directly one time, by way of a **Jump To** another time — computes a different position, so that guarantee does not carry across the two routes.
- That fixed number is then matched against the lanes' CURRENT weights to pick a lane, every time the step runs — so the number stays the same for a given contact, but which lane it lands in can still change if the weights are edited later, or if a step is added or removed earlier on the canvas (which shifts this step's position, and so the hash itself).
- The percentage shown next to each lane's weight is that lane's share worked out from every lane's weight together, rounded to whole numbers that always add up to 100.
- The lane taken is written to the run as **split.lane** (the lane's own label, or its letter if it has none) — put a condition on that field, or a tag inside each lane, to read back later which variant a contact saw.
- The **None** lane every Condition step also has in its stored shape is never reached in random mode — random mode always resolves to one of its own weighted lanes.

## When it fails

- A Randomizer step does not fail at runtime — every contact who reaches it lands in some lane, so there is no error state to read for this step in the **Activity** tab.

## Limits

- The split is not re-rolled per message or per visit within an unchanged workflow — it is fixed to the contact for this exact step, as long as neither its weights, its position on the canvas, nor the route the run takes to reach it (direct vs. via a Jump To) change; mid-test switching for an individual contact is not something the step offers on its own.
- It only chooses a lane. It sends nothing to the customer and changes nothing about the conversation by itself — whatever a variant does comes from the steps placed inside its lane.
- There is no built-in reporting for a split's results beyond what you build yourself: tag each lane (or read **split.lane** in a Condition) and compare with your own report or segment.
- Changing a lane's weight, or adding or removing a step earlier on the canvas (which moves this step), can move a contact who already ran this step into a different lane the next time they reach it — the draw's fixed number does not guarantee a fixed lane once either of those changes.

## Best practices

- Tag the contact (or the conversation) inside each lane so the split shows up somewhere you can filter or report on later — **split.lane** alone is only visible to a later Condition in the same run.
- Keep the number of lanes small. A three- or four-way split takes proportionally longer to reach a result you can trust than a clean A/B.
- Once a test is live, avoid editing its weights or moving other steps earlier on the same canvas before it — either can reshuffle contacts who already ran through it into a different lane on their next pass, muddying a comparison that depends on each contact staying in one lane.

## Use cases

- An opening message is split 50/50 between two openers, each tagging the contact with which one they saw, to compare reply rates later.
- A pricing nudge is tested at three weights — a control lane at 60% and two variants at 20% each — before committing to one for everybody.

## FAQ and troubleshooting

### Will the same customer ever see two different variants?

Not from the same step reached the same way — the draw is sticky per contact for a given position in the workflow. But editing the lane weights, moving the step by editing what comes before it, or reaching it a different way (directly one time, via a Jump To another time) can each land that same contact in a different lane.

### Do the weights have to add up to 100?

No. They are shares relative to each other; the drawer shows the resulting percentage next to each one, always rounded to add up to 100.

### Can I add a lane for people who match none of the others, like a Condition's None lane?

No — every contact who reaches a Randomizer lands in a weighted lane. There is no equivalent of None in random mode.

## Related

- [Condition](https://inchat.inseller.my/help/workflows/condition)
- [Conditions and variables](https://inchat.inseller.my/help/workflows/conditions-and-variables)
- [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/lib/workflows/random-split.ts`, `src/lib/workflows/engine.ts`, `src/lib/workflows/engine.test.ts`
