docs/install/architecture/latency.mdx
A synchronous webhook holds the connection open until your flow returns, so the caller waits for the orchestration around your flow plus your flow's own work. These are floor numbers, slow paths included.
<Note> **Scope:** the recommended production setup (`SANDBOX_CODE_ONLY`, `AP_REUSE_SANDBOX=true`, one flow per worker), which self-hosted deployments and **dedicated Cloud workers** run. The shared Cloud freemium pool uses a different sandbox. See [Production Setup](/install/configure-operate/production-setup). </Note>| Situation | Caller waits |
|---|---|
| Warm (the normal case) | ~0.2 s |
| Cold (first request on a fresh worker) | ~2 s |
| Under heavy load (every worker busy) | ~0.5 s |
| Timeout (flow never responds) | 30 s, then the connection closes |
Cold means a worker that has never seen this flow: it installs the pieces, fetches the bundle, and forks an engine first. Warm reuses all three. You hit cold when:
AP_REUSE_SANDBOX is off (then every request is cold).Every run records where its wall-clock time went, shown as a four-phase bar. To view it, open any run and hover the info icon next to the run's duration ("Took") in the run header.
The phases run in order:
| Phase | What it covers | Warm | Cold |
|---|---|---|---|
| Queue | Waiting after the run is created until a worker picks it up and starts setup — contention (every worker busy) plus dispatch overhead. | ~0 | grows under load |
| Setup | Fetching the flow bundle and installing the pieces, engine, and code steps into the sandbox (provisioning). | ~0 (cached) | ~0.5 s |
| Warm-up | Forking the engine process, Node boot, isolated-vm init, and the socket handshake (engine boot). | ~0 (reused) | ~0.9 s |
| Run | Executing the flow's trigger and steps — the same number reported as the run's duration (Took). Your own work and third-party calls dominate here. | your flow | your flow |
Queue, Setup, and Warm-up are the orchestration around your flow; Run is the flow itself. On a warm worker the first three collapse toward zero — the reused sandbox is already booted, so Warm-up is ~0 — and the bar is almost entirely Run, which is why warm calls are dominated by your own work.
A warm call is almost entirely per-step bookkeeping, not your code: a progress callback per step, tens of ms each. A step that calls a third-party API waits on that API, often far longer. Next to a real outbound call, the Activepieces overhead is the smaller number.
Four-step flow (webhook, math, code in isolated-vm, response), single warm call, no contention:
| p50 | avg | p95 | p99 |
|---|---|---|---|
| 163 ms | 176 ms | 260 ms | 343 ms |
AP_REUSE_SANDBOX=true plus a statically sized, always-on fleet.