# Fan-out and fan-in

> How an APIANT automation launches child automations in parallel with a latch group and waits for all of them to finish, including latch scopes, timeouts and restrictions.

A parent automation can launch several child automations to run at the same time, then wait until every one of them has finished before continuing. The children share a named **latch group**: each launch adds one to the group's count, each finished child subtracts one, and the parent's wait step continues when the count reaches zero.

Ask for it in plain English: "start the contact, order and inventory syncs at the same time, and when all three are done, send me a Slack message".

## How it is built

A fan-out and fan-in uses two kinds of step in the parent:

1. **Launch steps.** One call automation step per child, each with `launch_async: true` and the same `latch_group`. Each launch returns immediately.
2. **One wait step.** A `wait_for_latch` step with that `latch_group`. Steps after it run only once every child has finished.

```json
{
  "name": "Nightly sync hub",
  "steps": [
    {
      "step_id": "launch_contacts",
      "type": "call_automation",
      "automation_uuid": "<contact sync id>",
      "launch_async": true,
      "latch_group": "nightly_sync",
      "inputs": { "batch_id": "{{ trigger.fired_at }}" }
    },
    {
      "step_id": "launch_orders",
      "type": "call_automation",
      "automation_uuid": "<order sync id>",
      "launch_async": true,
      "latch_group": "nightly_sync",
      "inputs": { "batch_id": "{{ trigger.fired_at }}" }
    },
    {
      "step_id": "wait_for_syncs",
      "type": "wait_for_latch",
      "name": "Wait for both syncs",
      "latch_group": "nightly_sync"
    },
    {
      "step_id": "notify_done",
      "type": "assembly",
      "assembly_uuid": "<send message action id>",
      "connection_uuid": "<chat app connection id>",
      "settings": { "Message": "Nightly sync finished for batch {{ trigger.fired_at }}" }
    }
  ]
}
```

The same wait is available as the Flow Control action **Wait for latched automations**, and the Automations app's **Execute automation by mapped name** actions can launch children into a latch group by name. See [Subroutines and calling automations](/docs/automations/subroutines#calling-by-name).

## What the parent gets back

- A launch step returns `launched: true` and the `latch_group` it joined. It does not return the child's result.
- The wait step declares `latch_group`, `latch_released` and `skipped`. When it continues without waiting, `latch_released` is `true` and `skipped` is `false`. `skipped` is `true` only when the step has no group name. After a real wait, `latch_released` holds a value and the other two are empty; after a timeout, the output has `timed_out: true`.

The parent receives no output from the children. If the parent needs their results, have each child write them somewhere the parent can read after the wait, such as a lookup table or a Collector bucket. See [Storing data between runs](/docs/automations/data-storage).

## How the wait behaves

- The parent's run is parked while it waits. It does not poll, and it holds no worker.
- A child counts as finished when its run ends, whether it succeeded, failed or was halted. A child that is itself paused, for example snoozed or waiting on a form, keeps the parent waiting.
- If every child already finished before the parent reaches the wait step, the parent continues without parking.
- If the parent automation is turned off while it waits, it is not woken until it is turned back on.

## Latch scopes

The scope decides which launches share one count. On call automation launch steps and the wait step, set the same scope:

| Scope | The count covers |
|---|---|
| This execution (default) | Only the children this run launched. Two runs of the same automation at the same time each wait for their own children. |
| My account | Every launch into that group name in your account, including launches from other runs |
| Linked accounts | Every launch into that group name across the linked parent account and its children. With no linked parent, it behaves like My account. Linked accounts are an Enterprise feature; see [Linked and associated accounts](/docs/account/linked-accounts). |

A launch and a wait with different scopes count different things, so the wait can continue early with no error. Keep the default unless runs deliberately need to share one count.

The **Execute automation by mapped name** actions do not take these three scopes. Their count belongs to the launching run, like **This execution**, unless their shared latch group option is on, which keys the count to the linked parent account, like **Linked accounts**. Match the wait step's scope to that choice.

A child called synchronously, whether a subroutine or an ordinary automation, shares the caller's latch count. So a synchronous child can launch grandchildren and the caller can wait for them with the default scope. An asynchronous child has its own count: for the parent to wait on children that an asynchronous child launched, both sides need **My account**.

## Timeouts

The `wait_for_latch` step takes an optional `timeout_seconds`. Leaving it out, or setting `0`, waits indefinitely. When a timeout passes, the wait step does not fail: the run continues and the step's output shows it timed out.

The Flow Control action **Wait for latched automations** has settings for the group and scope only, so it always waits indefinitely.

## Reusing a group name in a loop

The same group name can be reused safely inside a for-each loop:

- **Launch and wait inside the loop.** Each pass waits for the children launched in that pass only.
- **Launch inside the loop, wait once after it.** Every child from every pass joins one count, and the single wait covers all of them.

## Restrictions

- A wait step cannot run inside a parallel branch, or inside an ordinary automation called synchronously. It can run inside a subroutine, a condition or a loop.
- A launch step with `launch_async` but no latch group is refused.
- Match the group name exactly on every step. Letters, digits, hyphens, underscores and spaces are a safe choice.

## Troubleshooting

| Symptom | Likely cause | What to do |
|---|---|---|
| The parent continues before the children finish | The launch and wait steps use different group names or scopes, or a launch was synchronous | Ask Claude to check that every launch has `launch_async`, the same group, and the same scope as the wait |
| The parent never continues | A child is still running or is paused, and the wait has no timeout | Check the children's runs in Execution History, or add a timeout |
| The wait step fails immediately | It is inside a parallel branch or a synchronously called automation | Move the wait to the top level or into a subroutine |

## Next steps

- [Subroutines and calling automations](/docs/automations/subroutines)
- [Delays, snooze and waits](/docs/automations/delays)
- [Conditions, loops and parallel branches](/docs/automations/control-flow)
- [Linked and associated accounts](/docs/account/linked-accounts)
