Event and trigger concepts
Decouple event senders from workflows so one action can start independent functions.
When a customer places an order, your app can send one shop/order.placed event. Inngest can start separate payment and notification functions from it. The event records what happened; each trigger tells Inngest which function to start.
The path from action to run
- Your app or a provider sends an event with a name and data.
- Inngest records the event in the selected environment.
- Each active function with a matching trigger starts its own run.
- Each run receives the triggering event and records its steps, waits, and result. A cron schedule starts a run without an app-sent event. A function may also start through direct invocation. See Primitives for durable waits and invocation.
Choose the right connection
- Use an event when several functions may react, the sender does not need their return values, or the action crosses apps.
- Use a webhook when an external service sends the action to an Inngest URL.
- Use cron when time starts recurring work.
- Use
step.invoke()when a run needs a specific child function and its result. An event can also resolve a waiting run or cancel a matching run. It is useful for approval, follow-up, and stop signals as well as for starting new runs.
Match an event before starting a run
A trigger can match one event name or an event family. Add a trigger if condition when only some events should start a run. Inngest evaluates the condition before it starts the function. The condition uses Common Expression Language (CEL), and it must return true or false.
import { eventType, staticSchema } from "inngest";
import { inngest } from "./client";
const orderPlaced = eventType("shop/order.placed", {
schema: staticSchema<{ orderId: string; total: number }>(),
});
export const largeOrder = inngest.createFunction(
{
id: "process-large-order",
triggers: [{ event: orderPlaced, if: "event.data.total > 100" }],
},
async ({ event }) => processOrder(event.data)
);
In the expression, event is the incoming event. Test the condition against representative payloads. A JavaScript if inside the handler chooses what a run does after it starts; a trigger if decides whether the run starts at all.
An event can match several functions. Check overlapping triggers before deploying so one event does not start unintended work.
Start one function from several triggers
Give one function several triggers to run the same logic for many events or on a schedule. For example, a function can run an integrity check every morning and also whenever an event requests one. A function supports up to 10 unique triggers. Each trigger can be an event or a cron schedule.
inngest.createFunction(
{
id: "resync-user-data",
triggers: [
{ event: "user.created" },
{ event: "user.updated" },
{ cron: "0 5 * * *" }, // Every morning at 5am
],
},
async ({ event, step }) => {
// ...
},
);
Inngest de-duplicates cron schedules that overlap. See Handle overlapping crons.
Match an event family with a wildcard
Use a wildcard to match a whole group of events with one trigger. This helps when you forward every event in a group to another system, such as a real-time ETL pipeline.
A wildcard such as app/user.* matches names such as app/user.created and app/user.updated. Wildcards follow a slash or dot and match only a trailing suffix. They cannot appear in the middle of a name. Use a wildcard for broad intake, then inspect event.name and validate each payload before acting. A wildcard event type cannot define one schema for every possible payload.
app/*matches any event with theapp/prefix, such asapp/user.createdandapp/blog.post.published.app/user.*matches any event with theapp/user.prefix, such asapp/user.createdandapp/user.updated.app/blog.post.*matches any event with theapp/blog.post.prefix, such asapp/blog.post.published.
Wildcards cannot follow characters other than / and ., and they cannot appear mid-pattern. Patterns such as app/user.update* and app/blog.*.published are not supported.
Check which event started a run
When a function has several triggers or a wildcard, read event.name in the handler to find the event that started the run. In TypeScript, checking event.name narrows the event's type.
async ({ event }) => {
// ^? type event: EventA | EventB | ScheduledTimerEventPayload | InvokedEventPayload
if (event.name === "a") {
// `event` is type narrowed to only the `a` event
} else if (event.name === "b") {
// `event` is type narrowed to only the `b` event
} else {
// `event` is type narrowed to the `inngest/scheduled.timer` (cron) or
// `inngest/function.invoked` (step.invoke) event
}
}
A batch of events can hold many different events. Check the shape of each event in a batch on its own. See Batching.
Handle overlapping crons
Several cron triggers on one function can overlap. For example, 0 * * * * runs every hour and */30 * * * * runs every half hour, so both fire at the start of each hour. Inngest runs only one cron job for a given second, so this function runs once every half hour. See Schedules and delayed starts for cron syntax, timezones, and jitter.
Identify an event
Use a descriptive, stable name such as billing/invoice.paid. Keep the relevant resource ID in data. An event ID identifies the sent event for lookup and optional deduplication. A run ID identifies one function execution. One event can produce multiple run IDs.
Inspect the result
Find the event in the environment where you sent it. Open the linked run and inspect its trace. If no run starts, compare the event name, trigger condition, app sync, and environment. See Troubleshooting.