step.waitForEvent
step.waitForEvent() pauses a run until a matching event arrives or the wait times out. Inngest resumes the run with the event without keeping your function process running while it waits.
They are useful when:
- You need a response from another service before continuing.
- A person must approve or reject work.
- One event should be able to resume multiple matching runs.
Why choose an event?
- Resume several runs. One event can resume multiple matching waits and trigger other functions.
- Keep systems independent. The sender publishes an event; it does not need to know which runs are waiting.
- Keep an audit trail. Inngest stores events for audit and Insights.
Use step.waitForEvent for most waits. If you must address one run directly and need lower resume latency, use step.waitForSignal. A signal resumes one run through a transactional API.
Wait for invoice review
This function waits up to seven days for an invoice review. It matches the review to the invoice that started the run.
import { eventType, Inngest, staticSchema } from "inngest";
type ApprovalRequested = { invoiceId: string };
type ApprovalRecorded = { invoiceId: string; approved: boolean };
const approvalRequested = eventType("invoice/approval.requested", {
schema: staticSchema<ApprovalRequested>(),
});
const approvalRecorded = eventType("invoice/approval.recorded", {
schema: staticSchema<ApprovalRecorded>(),
});
const inngest = new Inngest({ id: "billing" });
export const awaitInvoiceReview = inngest.createFunction(
{ id: "await-invoice-review", triggers: [approvalRequested] },
async ({ event, step }) => {
const review = await step.waitForEvent("wait-for-review", {
event: approvalRecorded,
match: "data.invoiceId",
timeout: "7d",
});
if (review === null) {
return { invoiceId: event.data.invoiceId, status: "timed-out" };
}
return {
invoiceId: event.data.invoiceId,
status: review.data.approved ? "approved" : "rejected",
};
}
);
Send invoice/approval.recorded with the same invoiceId after the wait starts. The returned event provides review.data.approved; a timeout returns null.
API
const event = await step.waitForEvent(id, { event, timeout, match });
id: a stable step ID.event: the name of the event to wait for.timeout: how long to wait, such as"7d".match: a field both events share, such as"data.invoiceId". For more specific matches, use anifexpression instead.- Returns the matching event, or
nullif the timeout expires.
Always handle the timeout, for example by recording an expired request or sending a reminder. See the step.waitForEvent reference for every option, including if expressions.
Match the right event
Use a stable request or entity ID in both events. An event name alone can match unrelated runs. Sessions do not filter step.waitForEvent(); the event name and match or if determine the match.
The wait only sees events sent after it starts listening. If an external system can send a response before the wait begins, arrange the sequence so the wait is registered first or check the external state before waiting. A matching event sent earlier will not resume this wait.
Use step.waitForEvent() for events that can fan out to multiple functions or runs. For direct, latency-sensitive resumption of one run, see step.waitForSignal.
Limits and related pages
- Check the current platform limits when choosing a timeout and shaping event or run data. The limit for a running step is a separate setting from a durable wait.
- In a
group.parallel()race, a losingstep.waitForEvent()remains active until its timeout. Use a short timeout when that pattern applies. - For a delay with no external event, use step.sleep.