FluxBilling
Plugins

Building Flows

How to build a plugin flow on the visual canvas: wiring nodes and branches, the flow types and when the platform runs each one, execution timeout and retry behavior, and testing a flow with sample input before you ship it.

Updated · 2026-06-29

Flows are the visual workflows that power a plugin. Instead of writing code, you build a flow by dragging nodes onto a canvas and wiring them together — each node does one job (make an HTTP request, branch on a value, map a status, format a message), and the connections between them define the order things run in. A plugin can contain many flows, one for each thing it needs to do: provisioning a service, suspending it, handling an incoming webhook, fetching live status, sending a notification, and so on.

This article covers how the canvas works, the flow types and when the platform runs each one, the execution behavior the platform applies (timeout and retry), and how to test a flow before you ship it. For the full catalog of nodes, see the Node Reference. For the {{ }} syntax used throughout flows, see Variables and Expressions.

The canvas

The flow builder is a drag-and-drop canvas. You add nodes from the palette and connect them by dragging from one node's output handle to the next node's input handle. A connection (an "edge") means "when this node finishes, continue to that one."

Every flow begins at a single Start node. Execution follows the wired connections outward from Start until it reaches an End node. If a flow has no Start node, it cannot run, so always begin there.

Most nodes have a single output and simply pass control to whatever you wire next. Branching nodes are the exception — they have more than one output handle, and at runtime they choose exactly one based on what happened:

  • The HTTP Request node has a success output and an error output.
  • The Condition node has a true output and a false output.
  • The Switch node has one output per case you define, plus a default.
  • The Loop node has a body output (run once per item) and a complete output (run when the loop finishes).

When a branching node emits one of its outputs, execution continues down whatever you wired to that specific handle. If a branch handle is left unwired, that path simply stops — it does not fall through to a different branch. So if you care about the error path of an HTTP Request, make sure you wire its error output to something.

Flow types and when they run

Every flow has a type that tells the platform when to run it. Choose the type that matches what the flow is for:

Flow type When the platform runs it
Provision When a new service using this plugin is set up for a customer.
Suspend When a service is suspended (for example, on non-payment).
Unsuspend When a suspended service is reactivated.
Terminate When a service is cancelled or removed.
Custom Action On demand — triggered by an action button you define (e.g. "Restart", "Reinstall").
Webhook Handler When your external system sends an inbound webhook to the plugin.
Data Fetch When the platform needs live data from the provider (e.g. current status, usage, a list of options).

These seven types are what the create-flow picker offers. A couple of flow kinds are set up differently rather than chosen here: scheduled flows (which run on a time-based schedule) are scaffolded for you when you start a plugin from the "Scheduled Task" starter in the plugin creation wizard, and notification flows are created through a notification plugin (see Notification Plugins).

The lifecycle types (Provision, Suspend, Unsuspend, Terminate) are the backbone of an infrastructure plugin — the platform calls them automatically as a service moves through its life. Custom Action flows power the buttons you expose to staff or customers. Data Fetch flows run whenever the panel needs fresh information from your API. Notification flows are described in detail in Notification Plugins.

Execution behavior

Beyond the nodes you wire, the platform applies a couple of execution rules to every flow run: a timeout and, where enabled, retries on failure.

Execution timeout

Each flow run has an execution timeout — 30 seconds by default, with a hard ceiling of 5 minutes. If a flow is still running when its timeout elapses, it is stopped and reported as a failure. The timeout bounds the whole run, not any single node, so a flow that makes a slow external call can hit it even if no individual node misbehaves.

Retry on failure

A flow can retry on failure — up to 3 attempts in total by default (one initial run plus retries), with an increasing delay plus a little random jitter between attempts, capped around 30 seconds. Each retry restarts the flow cleanly from its original input.

Each retry starts clean: any variables set during the previous attempt are discarded and the flow begins again from its original input. Retry is best for operations that are safe to repeat. If repeating an operation could cause a duplicate side effect (for example, charging a card or creating a resource twice), be cautious — or design the flow so repeats are harmless.

Testing a flow

You don't have to deploy a plugin to see whether a flow works. The builder lets you test a flow by supplying sample input — a small object that stands in for the data the platform would normally pass in. The flow runs immediately against that input, end to end, and you see the result and any error.

When you test, the flow uses your real plugin configuration and connections, so an HTTP Request node really calls your API. Use sample values that are safe to send.

Execution History

Every time a flow runs — whether from a test, a lifecycle event, a webhook, or a schedule — it's recorded in the History view for that flow. Each entry shows the input, the output, whether it succeeded or failed, how long it took, and a step-by-step trace of which nodes ran in what order. When a run fails, the History entry points at the node where it failed and includes the error message, which makes it the first place to look when something isn't behaving.

History entries are kept for a rolling window and then automatically cleaned up, so the view stays focused on recent activity.

Worked example: a status-fetch flow

Here's a small Data Fetch flow that asks a provider for a service's current state and maps the provider's wording onto a clean status. The shape is:

Start → HTTP Request → Status Map → End

  1. Start — the entry point. It passes the incoming input (which identifies the service) straight through to the next node.
  2. HTTP Request — calls your provider's "get status" endpoint using your configured connection (method GET). On a successful response it emits its success output; on a failure it emits error.
  3. Status Map — wired to the HTTP Request's success output. It reads the provider's status field out of the response and maps each provider value to your internal status (for example, runningactive, stoppedsuspended), with a default of unknown for anything unmapped.
  4. End — receives the mapped result and returns it as the flow's output.

To handle failures, wire the HTTP Request's error output to a second End node that returns a clear error result, so the panel knows the fetch didn't succeed instead of silently stopping.

That's the whole pattern: a Start to receive input, one or more working nodes in the middle, branches wired for both the happy path and the error path, and an End to return the result. Once it's wired, test it with sample input, confirm the run in History, and the flow is ready.


Related: Variables and Expressions · Node Reference · Capabilities and Flow Contracts · Connections and Authentication · Configuration Settings