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:

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.