Never drop an event.

Inline event handling often fails silently. Inngest reliably executes your functions, retries on failure, and shows you exactly what happened.

Sully Omar, Co-founder at Otto (Acquired by Cohere)

Inngest completely transformed how we handle AI orchestration at Cohere. Its intuitive developer experience, built-in multi-tenant concurrency, and flow control allowed us to scale without the complexity of other tools or the need to build custom solutions. What would have taken us a month.

Any source,any path

Inngest handles every event no matter how they enter, making them reliable, observable, and retryable from Day 1.

  • Third-party providers

    (webhooks)

    Point any provider at an Inngest webhook URL. Trigger functions that retry automatically.

  • Your own code

    Inngest.Send()

    INNGEST.SEND()

    Fire events from anywhere in your codebase. Trigger multiple functions in parallel.

Durability defined in code

charge-credit-card/ attempt 1
FAILED
throw new Error(...)
charge-credit-card/ attempt 2
FAILED
throw new Error(...)
charge-credit-card/ attempt 3
FAILED
throw new Error(...)
charge-credit-card/ attempt 4
FAILED
throw new Error(...)
charge-credit-card/ attempt 5
SUCCEEDED
return { ok: true }

In Practice

Write the handler. Inngest does the rest.

No queue setup. No worker process. No retry logic to write. Just a function that reacts to an event.

inngest/functions/payment-failed.ts
// Triggered by a Stripe webhook OR inngest.send() — same code either way
export const onPaymentFailed = inngest.createFunction(
{ id: "on-payment-failed" },
{ event: "stripe/invoice.payment_failed" },
async ({ event, step }) => {
const user = await step.run("load-user", async () =>
getUserByStripeId(event.data.customer)
);
await step.run("downgrade-plan", async () =>
billing.downgrade(user.id)
);
await step.run("send-email", async () =>
sendPaymentFailedEmail(user.email)
);
// If send-email fails — only that step retries. User is not double-downgraded.
}
);
// A second function on the same event. Fan-out, zero config.
export const alertSlack = inngest.createFunction(
{ id: "alert-slack-payment-failed" },
{ event: "stripe/invoice.payment_failed" },
async ({ event }) =>
slack.notify(`Payment failed: ${event.data.customer}`)
);

Integrations

Works witheverything you use.

Point any webhook URL at Inngest and start writing functions in minutes.

What teams have built with Inngest to handle webhooks & events

FAQ

  • Inngest is not an event bus. It's an orchestration layer that triggers durable, step-based functions from events — so when an event fires, the resulting job runs reliably with retries, state, and full observability.

  • Inngest can receive webhooks directly and trigger functions from them. Instead of writing custom handler logic with retries and error handling, you define steps in code and Inngest manages execution.

  • Only the failed step retries — not the entire function. Inngest tracks completed steps, so no work is duplicated and the webhook payload is never lost.

  • Only the failed step retries, not the entire job. Inngest tracks which steps completed and resumes from the point of failure when your deployment is back up.

  • Yes. A single event sent to Inngest can fan out to multiple functions running in parallel, each with their own retries and execution state. No custom pub/sub logic required.

  • Yes. You can trigger a function immediately from an event, delay execution by a set duration, or wait for a follow-up event before continuing — all defined in code.

  • Inngest stores your event history. You can replay events against a fixed version of your function — reprocessing affected runs without re-triggering the original source.