What the swimlane looks like once ExternalSystem, Database and Notify calls become nodes. The flow below is the rig demo the plan seeds (FlowOutboundDemo → DelmiaSim01.OutboundDemo): one synchronous REST call, one cached database write, one notification.
| Time | Row | Status | Node | Error |
|---|---|---|---|---|
| 05:31:02.301 | CachedSubmit | Submitted | site-a / node-a | — |
| +0.9 s | DbWriteCached | Forwarded | node-a | — |
| +4.1 s | CachedResolve | Delivered | node-a | — |
CachedCall / CachedWrite / Notify.Send returned at once; the run carried on without it.
Inferred device echo (a write that satisfied a wait).
New. Trigger spawn — this write fired a value-change script.
OkSync call answered, or cached operation delivered.
PendingSubmitted / forwarded, not yet resolved. The page keeps refreshing for up to two minutes after a Live run.
ParkedRetries exhausted or permanent failure downstream; retry or discard from Site Calls / Notifications.
FailedA sync call failed and the script received the error.
External is a lane, not a caller-side node. Like Tags, it holds actors outside ScadaBridge — a REST endpoint, a database, the mail relay — so a hop that leaves the system reads as one. Its gutter lists the targets touched.
One node per operation. A cached call's four audit rows (Submitted → Forwarded → Attempted → Delivered) fold into one node; the pill is the latest status and the Lifecycle tab is the history. A notification folds the site's NotifySend with central's NotifyDeliver rows.
Sync calls sit in the chain; async ones branch. The script blocks on ExternalSystem.Call, so the chain runs through it (write → API call → write). It does not block on the cached write or the notification, so those hang off the script run on a dashed edge and the chain skips them.
The Response now starts from the route call, not from whatever step happened last — a cascaded write on another instance is not what answered the HTTP request.
| Script call | Audit rows already written | Node | Live inline | Arrives later |
|---|---|---|---|---|
ExternalSystem.Call | ApiOutbound / ApiCall Delivered|Failed, request + response, duration | API CALL · in chain | whole node | — |
ExternalSystem.CachedCall | CachedSubmit → ApiCallCached Forwarded / Attempted ×n → CachedResolve | API CALL · async branch | Submitted | Forwarded, attempts, Delivered / Parked |
Database.Query / Execute | DbOutbound / DbWrite, Extra {op, rows…} | DATABASE · in chain | whole node | — |
Database.CachedWrite | CachedSubmit → DbWriteCached → CachedResolve | DATABASE · async branch | Submitted | the rest |
Notify.Send | site NotifySend Submitted → central NotifyDeliver Attempted / Delivered / Parked | NOTIFY · async branch | Submitted | central outbox rows |
central method script Database.* | none today → gets DbOutbound / DbWrite (closes an audit gap) | DATABASE · in chain, Central-side caller | whole node | — |
ApiCall / DbCall / Notify, lane External, outcomes Pending / Parked, FlowLifecycleRow.Database.* and feeds the trace sink.Retry / Discard on a parked node (Site Calls and the Notification report own those actions), deep links by operation id into those pages, and per-attempt request diffs. Each is a register row, not a blocker.
Repo plan: docs/plans/2026-09-06-flow-debugger-outbound-calls.md.