Event Bus
The Event Bus lets applications in your space turn data changes and business actions into events, and lets tasks subscribe to those events so they run automatically when something happens. Instead of only running on a schedule or by hand, a task can run because an event occurred — a transaction was completed, a member was updated, content was published — and read the event's data while it runs.
This makes your space event-driven: one event can fan out to any number of tasks, across one or several installed applications.
Concepts
| Term | Meaning |
|---|---|
| Event | A named thing that happened, e.g. transaction.completed, carrying a JSON payload (e.g. member_id, amount). |
| Registered event | An event an installed application declares it can emit, together with the schema its payload must match. Registered automatically when the app is installed. |
| Subscription | A binding between a registered event and a task. When the event fires, the subscribed task is triggered. Subscriptions can carry an optional filter. |
| Envelope | The durable record of one emission — its payload, status, and which subscriptions it matched. The basis for the event log and replay. |
Access
Open the console and click Event Bus in the sidebar (/event-bus). The page has three tabs:
- Catalog — every event registered by your installed applications, grouped by app, with each event's payload schema.
- Subscriptions — the bindings between events and tasks; create, enable/disable, filter, or remove them here.
- Log — the audit trail of emitted events, with status and the option to replay a single event.
A compact Event Insights widget on the dashboard summarizes recent activity (volume, success rate, top events).
How it works
1. Emit An app emits an event (e.g. when a transaction is written).
2. Validate The payload is checked against the event's registered schema.
3. Record An envelope is stored (audit + idempotency + replay).
4. Dispatch Every active subscription for that event is matched.
5. Trigger Each subscribed task runs, with the event data attached.
Each subscribed task runs through the normal task execution path, so everything you already know about task runs (execution history, logs, concurrency limits) applies unchanged.
Where events come from
You don't create registered events by hand — they come from installed applications:
- Automatically on data writes. An application can declare that a particular data write emits an event. For example, recording a transaction emits
transaction.completedwith the new row's fields as the payload. No extra action is needed; installing the app makes the event available. - Explicitly from the application. An application can also emit an event directly (for non-data actions), via the platform's emit API.
When an application is installed, its declared events appear in the Catalog, and any default subscriptions it ships (its own tasks reacting to its own events) are created for you. Uninstalling or upgrading an app keeps the subscriptions you created.
Subscribing a task to an event
In the Subscriptions tab:
- Click New subscription.
- Choose the event (from the Catalog) and the task to trigger.
- Optionally add a filter so the task only runs when the payload matches.
- Save. The subscription is active immediately.
You can disable a subscription temporarily (toggle Active) instead of deleting it.
EVENT tasks are triggeredA task is triggered by an event only while it is active. The task owner's identity is used for the run, just like a scheduled task.
Filters
A filter is a simple match on the event payload. Every field in the filter must equal the payload's value; a list value matches when the payload value is one of the list members.
// Only run when the member is in the VIC tier
{ "tier": "VIC" }
// Run for either of two stores
{ "store_id": ["S001", "S002"] }
If you set no filter, the task runs for every occurrence of the event.
Using event data inside the task
When a task is triggered by an event, the event is attached to the run as a trigger. Inside a workflow's custom-code node you can read it from the execution context:
async execute(inputs, context) {
const trigger = context.execution?.inputs?.trigger;
const event = trigger?.event; // e.g. "transaction.completed"
const memberId = trigger?.payload?.member_id;
const amount = trigger?.payload?.amount;
const source = trigger?.source; // "datasource" | "app" | "system"
}
The trigger object contains:
| Field | Description |
|---|---|
event | The event name |
payload | The event's data |
source | Where it came from — a data write (datasource), an app (app), or the system |
id | The envelope id (also shown in the Log) |
emittedAt | When the event was emitted |
spaceId, actorUserId, causationId, depth | Origin and loop-safety metadata |
Tasks triggered manually or on a schedule simply have no trigger, so existing tasks are unaffected.
Event log and replay
The Log tab lists every emitted event (envelope) with:
- Status —
DISPATCHED(matched and triggered),PENDING(awaiting dispatch),SKIPPED(e.g. loop-depth guard), orFAILED. - Event name, payload, and how many subscriptions matched.
You can filter by status or event name, and Replay a single envelope to dispatch it again — useful when fixing a task and re-running it against a past event. Replay re-triggers the matching subscriptions; design tasks to be idempotent so replays and retries are safe.
Reliability
The Event Bus is built to not lose events and not double-fire:
- Durable & validated. Every emission is recorded before dispatch, and payloads are validated against the registered schema — malformed payloads are rejected.
- Idempotent. Repeated emissions with the same key, and redelivered messages, do not trigger a task twice.
- No loss on outage. Events emitted while messaging is unavailable are stored and re-dispatched automatically once it recovers.
- Loop-safe. If an event triggers a task that emits the same event, a depth guard stops runaway loops (such events are marked
SKIPPED). - Space-isolated. Tasks can only subscribe to events from their own space.
Emitting events from your own application
If you are building a GeniApp, you can emit events explicitly. Declare the event (name + payload schema) in your app's event manifest so it is registered on install, then emit it:
POST /api/events/emit
Authorization: Bearer <app system api key>
Content-Type: application/json
{
"appIdentifier": "retail_crm",
"event": "transaction.completed",
"payload": { "member_id": "...", "amount": 199.0, "store_id": "S001" },
"idempotencyKey": "order-12345"
}
The platform validates that the app is installed, the event is registered, and the payload matches the schema, then records the envelope and dispatches it. Provide an idempotencyKey (e.g. an order number) so retries don't emit duplicates.
The JavaScript SDK exposes emitManagedEvent(...) as a thin wrapper over this endpoint — see the developer documentation for details.
Next steps
- Review Workflow Triggers for the full set of ways a workflow can start.
- Manage the tasks you subscribe in Task Management.
- Learn how data inside a run is referenced in Workflow Context.