Component reference

A practical guide to every building block in the component library: what it represents, where it connects, which settings you can change, and a small example.

How to read this page

Each entry starts with the component's ports — the connection points shown in the editor — and its settings. Then a use case shows the component doing its job in a small diagram. Port purposes are explained in Components & ports; the arrow styles follow the connection types.

Note

The editor is the final source for the choices available in your workspace. This page uses the same live component catalog, so its ports and settings stay aligned with the product.

Sources & triggers

Source components are where runs begin. An actor models traffic that a person or client system starts on demand; a scheduler models traffic that time starts for you.

Actor / clientTakes part in a run

Starts a bounded request and receives its explicit response.

Ports

PortDirectionRoleCarriesNotes
requestoutrequestrequestrequired
responseinresponseresponse—
eventoutenqueueevent—

Settings

SettingWhat it doesValuesDefault
Request countRoot requests emitted for this journey.1–1,0001
Interval (ms)Virtual milliseconds between root requests.0–3,600,0000

Use case — a shopper checks out

A shopper presses Buy in a web shop. The actor’s request port starts the journey and its response port receives the final answer — that round trip is the story most journeys tell. Set Request count to 5 and Interval (ms) to 200 and the same actor becomes a small burst of shoppers, with each request visible throughout the run.

Web clientActor / clientStorefront APIGateway / routerrequestresponseREQ · checkoutRES
The actor starts the journey with a request and receives the response; the dotted return line is paired automatically.

The event output is for things the client sends without waiting — an analytics beacon dropped onto a queue, or an event published to a topic — so you can model “tell and move on” traffic from the same actor that makes requests.

The actor's response input is the optional return half of a request. When this actor or a scheduler is selected as a journey start, the selected output must have exactly one executable route; use separate source components for separate public starts.

In a run you’ll see: TOKEN_EMITTED · CALL_STARTED · RESPONSE_RECEIVED · CALL_COMPLETED

Scheduler / triggerTakes part in a run

Starts bounded scheduled work by emitting tick events at a fixed virtual interval.

Ports

PortDirectionRoleCarriesNotes
tickoutenqueueeventrequired

Settings

SettingWhat it doesValuesDefault
Tick countScheduled ticks emitted for this journey.1–1001
Interval (ms)Descriptive virtual schedule duration; a journey may use a shorter run horizon.0–31,536,000,0001000
Start offset (ms)Descriptive virtual schedule duration; a journey may use a shorter run horizon.0–31,536,000,0000

Use case — a nightly report job

Nothing clicks a button at 2 a.m. — a schedule does. Here the scheduler stands in for a cron job: each tick drops a job onto a queue, and a worker service builds the report. Set Tick count to 3 and Interval (ms) to 60,000 to watch three job cycles play out — including what happens when one of them fails and retries.

Nightly batchScheduler / triggerReport jobsQueueReport workerService / functiontickASYNCdispatch
Each scheduled tick enqueues a job; the queue dispatches it to the worker with its usual retry behavior.

The tick port sends events, so it plugs into anything that consumes events: a queue’s enqueue, a service’s event input, or a topic’s publish. Ticks are bounded — the run schedules up to the count you configure, after the Start offset (ms) and spaced by Interval (ms) in virtual time. Ticks beyond the run horizon are skipped; a compiler warning identifies that clipping. Downstream failures or safety limits can still stop the run. This is simulated scheduling, not a wall-clock cron job.

In a run you’ll see: TOKEN_EMITTED · QUEUE_ENQUEUED · QUEUE_DEQUEUED

Routing & compute

These are the components that carry a request through the middle of a system: the gateway decides where traffic goes, services do the work, and transforms make data-processing steps explicit.

Gateway / routerTakes part in a run

Routes requests through explicitly authored handler actions.

Ports

PortDirectionRoleCarriesNotes
request ininrequestrequestrequired
request outoutrequestrequestrequired
response ininresponseresponse—
response outoutresponseresponse—
erroroutterminalresponse—

Settings

SettingWhat it doesValuesDefault
Route modeDescriptive routing mode; execution follows handler actions, not this setting.first-matchfirst-match

Use case — a public API edge

A mobile app calls your public API. Configure explicit handler actions on the gateway's request input: test a condition, call the selected service, then respond to the caller. Author the rejection path as well. Connections provide routes; the gateway does not infer routing rules or responses from them. Actions run in their authored order, and a false condition skips that action rather than ending the handler.

Mobile appActor / clientPublic API edgeGateway / routerOrders serviceService / functionREQrouteERROR
Accepted requests are routed to the Orders service; rejected ones return to the caller along the orange error route.

The responses flow back through the gateway’s response in and response out ports. You never draw those return connections yourself — they’re paired automatically when you draw the forward requests. Pairing defines where a response can go; a downstream reply resumes the calling handler, whose respond action must return the answer upstream. An empty handler does not route or reply.

In a run you’ll see: ROUTE_EVALUATED · ROUTE_SELECTED · RESPONSE_SENT

Round-robin backend calls

Add Call next backend to a Gateway handler to call one backend, wait for its response, and continue. Connect at least one synchronous request from requestOut, each with a paired response into responseIn. Conditional backend connections are rejected. No extra routing setting is required; finish the handler with a response to the original caller.

Backends are ordered by ascending connection priority, then stable connection ID. Each run starts at the first backend and cycles A/B/C/A. Retries stay on the selected backend; the next action execution selects the next one. Explicit Call actions keep their existing behavior. Potential routes include every eligible backend, while the trace identifies the connection actually selected. Try the Round-robin gateway starter for a three-backend example.

Service / functionTakes part in a run

Performs bounded compute and explicit downstream calls.

Ports

PortDirectionRoleCarriesNotes
request ininrequestrequestrequired
calloutrequestrequest—
returninresponseresponse—
responseoutresponseresponserequired
data commandoutwritecommand—
data resultinreadrecord—
eventinconsumeevent—
ACKoutackresponse—
NACKoutnackresponse—
publish eventoutenqueueevent—
write recordoutwriterecord—
publish eventoutpublishevent—

Settings

SettingWhat it doesValuesDefault
OperationSafe, provider-neutral service behavior.pass-through | workerpass-through
Duration (ms)Illustrative virtual execution duration.0–3,600,00010

Use case — a checkout service orchestrating a purchase

The service is the workhorse of most diagrams. This checkout service receives an order on request in, then uses three different kinds of ports for three different conversations: call / return to ask the auth service a question and wait, data command / data result to read the orders database, and publish event to announce the finished order without waiting for anyone.

API gatewayGateway / routerCheckoutService / functionAuth serviceService / functionOrders DBDatabaseorder-eventsTopic / event busREQcallDATAASYNC
One service, three kinds of conversation: a synchronous call, a data access, and a fire-and-forget event.

What the service does with these connections — and in which order — is its handler: call auth, load the order, publish the event, respond. To model a worker, author its event in handler to process a queue delivery and return ACK or NACK. Selecting worker in Operation alone does not create those actions.

Note

Choose the port by the conversation you are modeling. Data command sends a data operation; data result receives a record; write record writes a record; the event input consumes an event; and publish event or a named custom event output publishes an event. The ACK and NACK outputs belong to queue delivery, not to a synchronous response.

In a run you’ll see: NODE_ENTERED · CALL_STARTED · CALLER_WAITING · RESPONSE_SENT · NODE_EXITED

TransformTakes part in a run

Passes request or record payloads through unchanged by default; authored actions can define explicit processing.

Ports

PortDirectionRoleCarriesNotes
requestinrequestrequest—
responseoutresponseresponse—
record inputinreadrecord—
record outputoutwriterecord—

Settings

SettingWhat it doesValuesDefault
MappingAllowlisted payload mapping for this transform.identityidentity

Use case — an explicit record-processing step

A legacy adapter emits a customer record through write record. A transform passes it to the profile service's data result. This makes a processing step visible, but the default identity mapping does not automatically validate or reshape the record. If the adapter first calls a database, its result must return to that caller before the handler sends the record onward.

Legacy adapterService / functionRecord stepTransformProfile serviceService / functionDATADATA
The adapter explicitly sends a record through the transform to the profile service. The identity mapping leaves its value unchanged.
Note

The current mapping choice is identity: the transform passes the payload through unchanged. The current catalog exposes both a request/response path and a record input/output path; use the path whose message kind matches the connection. If you author actions for the arrival, those actions control the route instead of the untouched identity fallback.

In a run you’ll see: TRANSFORM_APPLIED

Data & state

State components remember things between steps of a run. What they hold at the start comes from your journey’s fixtures; what they hold at the end is part of the story the run tells.

Cache / key-value storeTakes part in a run

Models explicit get, set and delete operations with lazy TTL.

Ports

PortDirectionRoleCarriesNotes
lookupinreadcommandrequired
hitoutresponserecord—
missoutresponserecord—
writeinwriterecord—
writtenoutackresponse—

Settings

SettingWhat it doesValuesDefault
TTL (ms)Virtual lifetime of a cached value.0–31,536,000,00060000
Key pathBounded payload path used as the cache key.text/key

Use case — cache-aside session lookup

A session service checks the cache before touching the database. The cache’s defining feature is that hit and miss are separate output ports, so the fast path and the slow path are two different routes you can see and follow. On a miss the service loads from the database. Once the result returns, the service resumes its handler and writes the record to the cache. The database result is a matched return to the caller, not a second write-back route.

Session serviceService / functionSession cacheCache / key-valueSessions DBDatabasewritelookuphitmissquery on missresultwrite back
Cache-aside: lookup first; on a miss, load from the database and write the result back into the cache.

The TTL setting gives every entry a virtual lifetime, and Key path says which part of the payload identifies the entry (like /sessionId). Preload entries through journey fixtures to start a run with a warm cache — or leave it cold and watch the misses.

Note

Key paths are JSON Pointers. A lookup uses the configured pointer when it resolves; scalar values are stringified and composite values are canonicalized. If the pointer is missing or cannot resolve, the runtime falls back to the correlation or token identity. An entry expires lazily when a lookup reaches its expiry time, so a lookup at or after the TTL is a miss. A zero TTL therefore expires an entry at the same virtual time as the write.

In a run you’ll see: CACHE_LOOKUP · CACHE_HIT · CACHE_MISS · CACHE_WRITE · CACHE_EXPIRED

DatabaseTakes part in a run

Reads and deterministically mutates bounded journey fixture records. Get preserves the input payload and adds /record; query adds /records.

Ports

PortDirectionRoleCarriesNotes
queryinreadcommandrequired
resultoutresponserecordrequired
writeinwritecommand—
writtenoutackresponse—
record readinreadrecord—

Settings

SettingWhat it doesValuesDefault
CollectionJourney fixture collection used by the database.textrecords
Record pathRFC 6901 JSON Pointer evaluated from the write payload root to select the record.text(empty)
Key pathRFC 6901 JSON Pointer selecting the stable key: reads evaluate it from the payload root, while writes evaluate it from the record selected by recordPath.text/id
Read operationOperation used for arrivals at queryIn. Get merges its result at /record; query merges results at /records.get | queryget
Write operationOperation used for arrivals at writeIn.insert | update | upsert | delete | atomic-batchupsert
Atomic batch1–32 ordered rows: { id, operation: insert | update | upsert | delete, recordPath: JSON Pointer into the incoming payload }. Every row uses this database's collection and key path.1–32 ordered write operations—

Use case — the orders system of record

The database holds the records your journey cares about. An order service reads through query / result and mutates through write / written — two explicit conversations, so reads and writes are separate arrows you can trace in the run. In the current catalog, Read operation chooses get or query and Write operation chooses insert, update, upsert, delete, or Atomic batch against the configured collection. The defaults are get and upsert.

Order serviceService / functionOrders DBDatabaseDATA · queryresult
A read conversation: the service sends a query command and receives the matching record.

Records come from your journey’s fixtures — you decide exactly which rows exist before the run starts, which is what makes runs repeatable. Writes during the run show up as state patches you can inspect afterwards.

For a write, Record path is a JSON Pointer to the object that should be stored, and Key path is evaluated inside that object to find its stable key. With {"job":{"jobId":"job-1001"}}, use /job and /jobId. An empty record path selects the whole incoming value. Connect get and query to the query port; connect insert, update, upsert, and delete to the write port. A get result is added at /record; a query result is added at /records.

Atomic batch — all writes succeed together

Choose Atomic batch under Write operation for 1–32 ordered insert, update, upsert, or delete rows. Each row selects a record from the incoming payload with a JSON Pointer. All rows use this database's collection and key path; a delete row selects an object containing its key. Later rows see earlier staged writes, so insert-then-update works. Updates replace the record, so supply the complete intended object.

Connect the caller to writeIn and return through writtenOut. The database commits one state update only when every row succeeds and preserves the incoming payload. On the first failure, all staged writes are discarded, the trace identifies the failed row, and the existing database failure code reaches the caller's catch. Rolled-back rows emit no successful mutation evidence.

Batch writes use the intrinsic database handler: custom writeIn actions and catches are rejected. Prepare the payload, call the database, and respond in the service; handle recovery there too. A batch executes in one scheduler operation, with no intermediate calls, waits, or visible partial state. General transactions across calls or collections, isolation levels, and Begin/Commit/Rollback actions are not modeled. The Atomic database batch starter includes successful and rolled-back journeys.

Note

The current v1.3 read path is query in. The catalog keepsread in for older graph versions, but a current get/query interaction connected to that legacy port fails at runtime with an operation-port mismatch. For writes, an insert/update/delete can fail when its key or record is missing or conflicts with the selected operation. Keep the record and key pointers valid RFC 6901 JSON Pointers.

Note

Database fixtures are grouped by the configured collection name. A database configured for jobs, for example, starts from a fixture shaped like {"jobs":{"job-1001":{...}}}. Each run receives its own copy; mutations never alter the saved journey fixture.

In a run you’ll see: DB_READ · DB_RESULT · DB_MUTATION · DB_TRANSACTION_COMMITTED · DB_TRANSACTION_ROLLED_BACK · STATE_PATCH_APPLIED

Object storageTakes part in a run

Stores opaque objects behind explicit put, get and delete operations; tokens carry object references, never blob bytes.

Ports

PortDirectionRoleCarriesNotes
putinwritecommandrequired
storedoutackresponse—
getinreadcommandrequired
objectoutresponserecord—
missingoutresponserecord—
deleteinwritecommand—
deletedoutackresponse—

Settings

SettingWhat it doesValuesDefault
BucketJourney fixture namespace holding this store's objects.textobjects
Key pathRFC 6901 payload pointer resolving the string, number, or boolean object key.text/key

Use case — an image upload pipeline

An upload API stores a photo, and a thumbnailer fetches it later. Object storage models the S3-style half of that story: put stores an object and acknowledges through stored; get looks one up and branches — object when it exists, missing when it doesn’t — so the not-found path is a real route, just like a cache miss.

Upload APIService / functionMedia bucketObject storageThumbnailerService / functionputstoredgetobjectmissing
The upload service puts an object; the thumbnailer gets it back — or takes the missing route if it isn't there.
Note

Tokens never carry the object’s bytes — only a reference (its key and metadata), the way real systems pass presigned URLs instead of file contents. The Bucket setting names the fixture namespace the store reads and writes. A get is one synchronous interaction: both the object and missing returns lead to the caller's record arrival. Put and delete acknowledgements are responses instead: stored and deleted lead to the caller's response arrival. The Key path must resolve to a string, number, or boolean; an absent or non-scalar key fails the operation. The compiler also requires the get interaction to include both the object and missing return routes, and authored actions on get in are not supported.

In a run you’ll see: OBJECT_PUT · OBJECT_GET · OBJECT_MISSING · OBJECT_DELETED

Messaging

Messaging components decouple the sender from the receiver. The queue is point-to-point — one message, one consumer, with retries. The topic is broadcast — one event, every subscriber. Together they cover most of the asynchronous patterns in modern systems.

QueueTakes part in a run

Models bounded competing consumers, retries and explicit dead-letter routing.

Ports

PortDirectionRoleCarriesNotes
enqueueinenqueueeventrequired
dispatchoutrequesteventrequired · max 1 connection
ACKinackresponse—
NACKinnackresponse—
DLQoutterminaleventmax 1 connection

Settings

SettingWhat it doesValuesDefault
Maximum attemptsTotal delivery attempts before dead-letter routing.1–1003
Backoff (ms)Virtual delay before a retry becomes available.0–31,536,000,0001000
Consumer slotsMaximum deterministic deliveries active at the same virtual time.1–1001

Use case — a fulfilment worker with retries and a DLQ

Checkout finishes and hands fulfilment to a queue so the customer never waits on it. The queue dispatches each message to its single consumer — the worker — which reports back through ACK or NACK. A NACK sends the message back for another attempt after the configured backoff; when Maximum attempts runs out, the message takes the DLQ route instead of retrying forever.

CheckoutService / functionFulfilment queueQueueFulfilment workerService / functionOps auditExternal / terminalenqueuedispatchACK / NACKDLQASYNCERROR
Enqueue, dispatch, verdict: NACKed messages retry with backoff until attempts run out, then dead-letter to the audit sink.

The dispatch port accepts one connection, so the queue sends work to one consumer. Consumer slots controls how many deliveries that consumer may have active at once; it does not add more dispatch connections. Maximum attempts includes the first delivery, so a value of 3 permits the initial attempt plus two retries. Watching a message bounce through MESSAGE_REQUEUED into MESSAGE_DEAD_LETTERED is one of the most instructive stories Graphrun can tell.

Note

A worker sends ACK or NACK back to the queue. ACK completes that delivery. NACK schedules another delivery after the virtual Backoff (ms) until the attempt limit is reached; then the queue emits its dead-letter event and uses DLQ out when it is connected. Without a DLQ route, the exhausted delivery still ends rather than retrying forever.

In a run you’ll see: QUEUE_ENQUEUED · QUEUE_AVAILABLE · QUEUE_DEQUEUED · MESSAGE_ACKED · MESSAGE_REQUEUED · MESSAGE_DEAD_LETTERED

Topic / event busTakes part in a run

Broadcasts one published event to every subscription, forking a correlated child token per subscriber with recorded lineage.

Ports

PortDirectionRoleCarriesNotes
publishinpublisheventrequired
deliveroutsubscribeeventrequired

Settings

SettingWhat it doesValuesDefault
Delivery modeHow published events fan out to subscriptions.all-subscribersall-subscribers

Use case — fanning out an order-placed event

When an order is placed, three unrelated parts of the system care: email wants to confirm it, analytics wants to count it, loyalty wants to award points. Instead of the checkout calling all three, it publishes one event to a topic. Every connection drawn from the topic’s deliver port is a subscription, and each subscriber receives its own copy.

CheckoutService / functionorder-placedTopic / event busEmail serviceService / functionAnalytics intakeQueueLoyalty serviceService / functionpublishdeliverASYNCPUBSUB
One published event, three subscriptions: each fine-dotted connection receives its own copy.

Each subscription receives its own copy of the event. Want a subscriber to get retries and a dead-letter path? Send that subscription to a queue first.

Note

A topic has no queue state or retry policy of its own. Conditions are supported on the topic's deliver subscription edges; a false condition skips that subscriber. A published event creates a correlated child token for each selected subscriber, so a join after the topic can gather one fan-out wave.

In a run you’ll see: TOPIC_PUBLISHED · TOKEN_FORKED · TOKEN_COMPLETED

Tip

Queue or topic? Ask who should handle the message. Exactly one consumer, with retries — queue. Everyone who subscribed, each with their own copy — topic.

Control & orchestration

Control components shape when and how work happens: pausing it, gathering parallel branches back together, and driving multi-step processes to completion.

Timer / delayTakes part in a run

Advances virtual time and resumes through an explicit output.

Ports

PortDirectionRoleCarriesNotes
requestinrequestrequest—
responseoutresponseresponse—

Settings

SettingWhat it doesValuesDefault
Duration (ms)Virtual time advanced before the output resumes.0–31,536,000,0001000

Use case — an explicit pause in the flow

Real systems are full of deliberate waits: a cool-down before a retry, a grace period before an escalation. The timer makes that wait a visible box in the diagram. A request arriving at request advances the run’s virtual clock by the configured duration, then continues through response — nothing hidden, no implicit delays.

Cool-down 5sTimer / delayrequestresponseREQvirtual time advances by Duration (ms) between start and elapsed
The timer's request/response path advances virtual time before its response continues the interaction.

The current ports carry a request and its response, so the timer keeps the caller’s synchronous interaction paired while advancing virtual time. In the run you can watch the virtual clock jump: the wait costs virtual time, never real time. Older timer graph versions may expose the equivalent start / elapsed control ports.

In a run you’ll see: WAIT_STARTED · WAIT_COMPLETED

Join / aggregatorTakes part in a run

Waits for correlated branch arrivals and lets one satisfying token continue once its policy is met, completing earlier arrivals at the barrier.

Ports

PortDirectionRoleCarriesNotes
branchinjoineventrequired
joinedoutjoineventrequired · max 1 connection
timeoutoutterminaleventmax 1 connection

Settings

SettingWhat it doesValuesDefault
Join policyHow many correlated branches must arrive before continuing.all | any | countall
Required countBranches to await when the policy is Exact count.1–642
Timeout (ms)Virtual milliseconds before an unsatisfied group continues through timeout (0 preserves unresolved-barrier behavior).0–3,600,0000

Use case — scatter-gather price quotes

A travel site asks two suppliers for a price at the same time and can only answer once both quotes are in. The fan-out half is a topic; the fan-in half is the join. Each supplier’s answer arrives at branch, and the join waits at the barrier until its policy is met — all branches, any branch, or an exact count (a quorum). Then the story continues through joined.

quote-requestTopic / event busSupplier AService / functionSupplier BService / functionBoth quotes inJoin / aggregatordeliverbranchjoined
Two branches fan out through a topic and meet again at the join; with policy “all”, the joined output fires once both have arrived.

Graphrun keeps branches from the same request together, so two shoppers' quotes never mix. The branch that satisfies the policy is the one that continues.

Note

Join policies count distinct inbound branch edges in one fan-out wave; repeated arrivals from one branch do not satisfy all or an exact count. A count cannot exceed the statically reachable branches (or a bounded for each iteration). One join wave must share fork history — either all descending from the same fan-out, or none forked at all; mixing a forked branch with a never-forked one won’t meet. Set a positive Timeout (ms) to send an unsatisfied group through timeout. An arrival exactly at the deadline wins before the timeout fires; with timeout 0, an incomplete barrier remains unresolved.

In a run you’ll see: JOIN_ARRIVED · JOIN_SATISFIED · JOIN_TIMED_OUT · TOKEN_JOINED

Workflow / sagaTakes part in a run

Runs graph-derived steps sequentially as a durable saga, answering its caller once every step completes.

Ports

PortDirectionRoleCarriesNotes
startinrequestrequestrequired
stepoutrequestrequestrequired
step resultinresponseresponserequired
compensateoutcontrolevent—
completedoutresponseresponserequired · max 1 connection
failedoutterminalresponsemax 1 connection

Settings

SettingWhat it doesValuesDefault
On step failureHow the workflow reacts when a step fails; after at least one earlier step completes, compensation dispatches every connected route without waiting for acknowledgement.compensate | fail-fastcompensate

Use case — an order saga, step by step

Placing an order really means several things in a row: reserve the stock, charge the payment. The workflow receives the original request on start, then drives each outgoing step connection as one sequential step — the connection priorities set the order. Each step is an ordinary synchronous call whose answer returns on step result; when the last step completes, the workflow answers its caller through completed.

StorefrontActor / clientOrder sagaWorkflow / sagaReserve stockService / functionCharge paymentService / functionstartstepstep resultREQstep 1step 2completed
One workflow, two steps: each step connection is called in order, and the caller gets its answer when the last one finishes.
Note

Workflow steps run by the unique priority on each step request: lower values run first. MCP and editor authoring append omitted priorities, and WORKFLOW_STARTED reports the resolved order. A failed step returns through failed so the synchronous caller can close its continuation; that return edge uses the error kind. With Compensate completed steps, every compensate connections are dispatched as detached fire-and-forget work once for each completed step, in reverse step order. The compensation event records dispatch, not acknowledgement. A fail action ends that handler activation immediately: earlier effects remain, but later actions do not run.

In a run you’ll see: WORKFLOW_STARTED · WORKFLOW_STEP_STARTED · WORKFLOW_STEP_COMPLETED · WORKFLOW_STEP_FAILED · WORKFLOW_COMPLETED · WORKFLOW_FAILED · WORKFLOW_COMPENSATED

Boundaries & canvas helpers

The external dependency marks the edge of the world you’re modeling; groups and notes keep the picture readable without affecting behavior at all.

External dependency / terminalTakes part in a run

Models a fixed-latency dependency that answers synchronous calls with a configured success or failure outcome.

Ports

PortDirectionRoleCarriesNotes
requestinrequestrequest—
responseoutresponseresponse—
eventinterminalevent—
callback requestoutrequestrequest—
callback responseinresponseresponse—
publish eventoutpublishevent—
data commandoutwritecommand—

Settings

SettingWhat it doesValuesDefault
ModeFixed provider-neutral dependency outcome.success | failuresuccess
OutcomeNamed terminal outcome shown in the story.textcompleted
Latency (ms)Virtual milliseconds before the configured boundary outcome.0–3,600,00010

Use case — a payment provider you don’t control

Your checkout calls a payment provider, but the provider’s internals aren’t your story — only its answers are. The external dependency stands in for it: after the latency you choose, success mode replies through the matching interaction, while failure mode fails with the named outcome. Flip one setting and the same diagram tells the sad-path story — what your system does when payments are down.

CheckoutService / functionPayment providerExternal / terminalREQ · chargeRES · mode decides
The external dependency answers with exactly what you configured — a fixed success or a named failure.

Its event input also makes it a terminal sink: point a queue’s DLQ or any fire-and-forget event at it to give a story an explicit, named ending — “lands in ops audit” — instead of trailing off the edge of the diagram.

The current boundary also exposes callback and publish ports:request out can start a callback request, response in receives its return, event out publishes an event, and command out sends a write command. The intrinsic fixed-outcome behavior applies to an untouched request in: success needs its matching response out route; failure raises the configured outcome and does not use an error output. Other ports need authored handlers.

In a run you’ll see: RESPONSE_SENT · ERROR_RAISED

References another diagram in this workspace as a typed boundary; answers callers with a configured outcome and navigates to the linked diagram in the editor.

Ports

PortDirectionRoleCarriesNotes
requestinrequestrequestrequired
responseoutresponseresponse—
erroroutterminalcontrol—
event outoutpublishevent—
event ininsubscribeevent—

Settings

SettingWhat it doesValuesDefault
Linked diagramOpaque UUID of the target diagram in this workspace; empty means unlinked. Format is checked in pure validation, existence never is.text(empty)
ModeFixed provider-neutral boundary outcome for synchronous calls.success | failuresuccess
OutcomeNamed outcome attached to the response or terminal exit.textok
Latency (ms)Deterministic virtual-time service latency before answering.0–3,600,00010

Use case — decomposing a big estate into diagrams

A real system is many diagrams, not one enormous canvas. A diagram link stands in for another diagram in the workspace: your order flow can point to Accounts without redrawing it. Select the node, choose the target diagram, then use Open to visit it. Graphrun leaves a trail back to the original diagram.

During a run, the link acts as a boundary with the success, failure, outcome, and delay you set on the link itself. It does not run the contents of the other diagram. This keeps the current story focused and predictable.

On an untouched request, success follows the matching response out route, or the first response route when no interaction match is available; if there is no response route, the token completes. Failure takes the first error out route when one is connected and otherwise ends the token with the configured outcome. An empty or malformed target ID is shown as an advisory unlinked/broken state; it does not make the host graph's pure configuration validation fail.

If the target is removed or you lose access to it, the node shows a broken-link state. The rest of the current diagram can still be opened, saved, and run.

In Present mode, a diagram link is a labeled boundary: the viewer cannot open the linked diagram from presentation. The public share page currently shows only access and publication metadata, not the diagram or linked content.

Order serviceService / functionAccountsDiagram link / boundaryREQ · open accountRES · mode decides
A diagram link points to the Accounts diagram; Open visits it and offers a trail back.

In a run you’ll see: RESPONSE_SENT · ERROR_RAISED

Group / boundaryVisual helper

Organizes trust zones, regions, systems or teams without executing.

Settings

SettingWhat it doesValuesDefault
CollapsedHide child components while preserving the boundary.on | offfalse

Use case — showing who owns what

A diagram that mixes two teams’ services needs boundaries. Draw a group around the payments components and label it — reviewers instantly see ownership, trust zones, or regions. Collapse a group to hide its children while you focus elsewhere; expand it when the detail matters again. Groups never execute and never change a run.

Payments teamCharge serviceService / functionLedger DBDatabaseNote / legendCharges are idempotent;retries reuse the originalcharge id.
A group boundary around the payments components, with a note explaining the diagram — neither affects the run.

Note / legendVisual helper

Adds plain-text documentation without affecting behavior.

Settings

SettingWhat it doesValuesDefault
Note textPlain-text documentation; markup is never executed.text(empty)
Fit canvas note to contentGrow the canvas note vertically so its complete plain-text content remains visible.on | offtrue

Use case — explaining the diagram to the next reader

Notes hold the things a diagram alone can’t say: assumptions (“charges are idempotent”), pointers (“see the retry journey”), or a legend for your labels — as in the figure above. Note text is always plain text: it’s never executed, never rendered as markup, and never affects a run.

The note text is limited to 2,000 characters and preserves line breaks. Fit canvas note to content grows the note vertically so the plain text remains visible; when it is off, you can set the note's width and height directly. The optional inspector description is context for editors and is not shown on the canvas.

Authoring & runtime rules

The live catalog supplies the port and setting tables above. These rules explain the parts that are easy to miss when you only look at a component in isolation.

Note

A connection must run from an output to an input with the same message kind and payload contract. The editor derives the connection kind from the port roles. A response connection is the reverse half of a synchronous request; keep the request and its response paired with the same interaction.

Handlers and default behavior

Every input port has one ordered handler. An empty catalog_default handler has no actions, so components that use the generic executor do not invent a route from their connections. Add an explicit handler action — for example call, emit, respond, assign, or fail — when the input should continue the story. The handler reference describes the action sequence and catch rules.

Components with an intrinsic executor — including cache, database, queue, timer, external, transform, object storage, topic, join, and workflow — have documented default paths. Authored actions can control the supported handler paths, while some intrinsic paths remain fixed: object storage get, for example, must use its object/missing interaction rather than an authored handler. The compiler reports unsupported combinations before a run.

Starts, fan-in, and resilience

A journey start is one selected output from an actor or scheduler, or an external component's request/event output. That output must have exactly one executable route. A topic may fan out after the start, but the selected start itself cannot be multiply routed.

Join arrivals are counted by distinct inbound branch edges within one fan-out wave. Repeated arrivals from the same branch do not satisfy a multi-branch policy. A positive join timeout creates a virtual-time deadline; with the default zero timeout, an incomplete barrier remains unresolved and a drained run reports it.

Interaction resilience belongs to a synchronous request/response interaction and is owned by the component that sends the request. Its settings are timeout, maximum attempts, fixed or exponential backoff, and retryable outcomes: timeout, network_error, http_429, or http_5xx. One-way event interactions do not have a caller return path, so the graph validator rejects a resilience policy on them.

Outputs, custom events, and simulation limits

An output port can have an optional JSON Schema or an inferred schema from an example payload. That describes the output for inspection and validation; it does not add a connection or change simulation behavior. Protocol-bound outputs keep their intrinsic result contract.

Services can add named event outputs for separate publish routes. A label is trimmed and normalized, must be non-empty, and is limited to 80 characters. A service can have at most 64 graph ports, and a custom event output must be disconnected before it can be removed. Use the canonical event out or publish out port when a named custom route is not needed.

The Behavior tab also supports simulation-only traffic limits. Requests over time is a sliding window counter: it records all attempts for a global, payload-keyed, or variable-keyed bucket and rejects attempts above its limit. Requests at once is a capacity limit: it admits up to Maximum concurrent, queues up to Maximum queued, and rejects work beyond both. These limits affect runs only, not a real deployment. New limits default to 60,000 ms and 120 attempts with rate_limited, or 8 concurrent and 0 queued with capacity_exhausted. Keyed limits use RFC 6901 JSON pointers and expose only a derived key hash in runtime events.