BuildBaseBuildBase

Overview

Build event-driven automation in the console and drive it from your app by emitting events.

A workflow is a graph: one or more triggers, then actions and conditions wired together. You build it in the console's visual editor. Your application's job is to produce the events that start it.

Most triggers are system events that BuildBase already emits — a signup, a payment, a form submission. You rarely call a workflow directly; you cause the event it listens for.

// A form submission emits form.submitted, which starts any workflow
// whose trigger is triggers.form.submitted. No workflow API call needed.
await fetch(`${BASE}/api/forms/public/${orgId}/${formId}/submit`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email, message }),
});

Before you start

Build the workflow in the BuildBase console under Workflows, then publish it. A workflow in draft status never runs, no matter how many matching events arrive.

Lifecycle

A workflow moves through four states:

StatusBehavior
draftEditable. Does not run
activePublished. Matching events create instances
pausedPublished but halted. See below for what happens to in-flight runs
archivedRetired

Editing a published workflow does not change what is running. You edit a draft of it and publish that draft as a new version, which is why /publish-preview exists — it shows the diff before you commit.

Publishing validates the draft first, then hashes the flow. If the hash matches the latest published version, nothing is written.

Warning

Publish failures return HTTP 200. Both a validation failure and a no-change publish come back as 200 with success: false:

{ "success": false, "message": "Workflow has validation errors", "data": { "errors": [] } }
{ "success": false, "message": "No changes to publish", "data": { "noChanges": true } }

Check the success field. Treating res.ok as "published" silently misses both cases.

Each publish writes an immutable version record, so previous versions stay readable after the flow changes. An optional message on the publish call is kept as the change description, trimmed to 500 characters.

Instances

Each time a trigger matches, an instance is created — one run of the graph for one subject.

StatusTerminal
runningNo
pausedNo
completedYes
failedYes
canceledYes

paused is deliberately not terminal — a paused instance is holding its position and will continue from there when resumed.

Because instances are per-subject, pausing is available at several scopes: a single instance, everything for one audience member, everything for one workspace, or an organization-wide emergency stop.

Re-entry

A subject that triggers the same workflow twice does not automatically get two runs. Re-entry mode, set in the workflow's settings panel, decides:

ModeBehavior
alwaysDefault. Every matching event starts a new instance
onceThe subject enters once — unless its previous instance failed
cooldownRe-entry allowed after cooldownMinutes, defaulting to 60

once is right for onboarding sequences and wrong for anything recurring, like a monthly usage warning.

Two details that decide whether a subject is actually blocked:

A failed run does not count. The once check ignores instances in failed status, so a subject whose run failed can re-enter. That is usually what you want — it makes a fixed workflow re-runnable — but it means once is not a hard guarantee of exactly one execution.

Warning

Re-entry needs a subject to key on. The check resolves an identity from the event payload, trying audienceId, then userId, actorId, targetUserId, and finally email.

If the payload carries none of these, re-entry is not enforced at all and every matching event starts a new instance, whatever the mode says. Workflows triggered by events without a subject — most triggers.workflow.* events, for example — cannot rely on once or cooldown.

Enforcement is a Redis lock acquired atomically, so two events arriving at the same instant cannot both pass. A database check backs it up, which is what makes once outlast the lock's own expiry.

Retries

Every action node has a retry policy. Without a custom one it uses the default:

SettingDefaultAllowed range
maxAttempts31 – 10
backoffTypeexponentialfixed or exponential
backoffDelay5,000 ms1,000 – 300,000 ms
timeoutMs30,000 ms5,000 – 300,000 ms

HTTP webhook actions override this with a more forgiving policy, because outbound HTTP is the flakiest thing a workflow does.

Testing before publishing

/test runs a workflow in one of two modes:

  • dry-run — walks the graph and reports what would happen. No email is sent, no credit is granted.
  • hot-run — executes for real against a chosen subject.

Use dry-run to check branching logic and hot-run to check that an email actually renders.

Next Steps