Skip to main content
A node handler is the runtime behaviour behind one kind of node on the flow canvas. It implements Fapost\Foundation\Contracts\NodeHandlerInterface.

Identity

type() is the node’s unique identifier, such as send_message. It must be unique across every registered handler, including those from other extensions. version() is the handler’s current version; the engine resolves a handler by the pair (type, version) from an in-memory registry, with no database query on the hot path. label() and category() are what the builder shows: the node’s name and the group it appears under.

Configuration

configSchema() returns the schema the builder uses to validate the node and draw its form. This is the mechanism that lets an extension be configurable without shipping any front-end code — see Builder UI extensions.

Execution

execute() receives three things: It returns a NodeExecutionResult, built through one of its named constructors:
delayed() has two forms: Without resumeAt, the session waits exactly as after waiting(): the contact’s next message runs your handler again, and the handler decides whether it is time to move on — the built-in delay node works this way. Nothing wakes the node without an inbound message. With resumeAt, nothing the contact sends reaches your handler until that time: the engine wakes the node itself, and only then does the flow resume. Your handler tells the two situations apart through NodeExecutionContext::$resumedAfterDelay — true only on the run the engine triggered for this resumeAt, false on every other call, including the one that first asked for it:
Do not read the resume_at you asked for back out of $state. The marker the engine uses to schedule and match the wake-up is an internal detail, not part of the handler contract, and may change — rely on $resumedAfterDelay.

The handler must be graph-unaware

A handler returns a sourceHandle, never the id of the next node.
The engine resolves what comes next from the flow’s edges, using the source node id together with the handle the handler returned. A handler that decides which node runs next has taken a decision that belongs to the flow definition, and the same handler can then no longer be reused in a differently-shaped flow. sourceHandle defaults to 'default'. A node with several outcomes — a branch, a validation that can fail — returns a different handle per outcome, and the person building the flow wires each handle where they want it to go.

Writing outside the session

State changes go back through stateChanges on the result. Mutations that live outside the session JSON — contact attributes, contact language — are performed by the handler through the writer it receives on NodeExecutionContext, such as ContactWriterInterface.
Do not return a legacy effects[] array. It was removed; handlers use the writer or port from the execution context instead.