Skip to content

Events ​

An event says that something happened: a user signed up, an order was paid. You send events to the engine. Every function whose trigger matches the event's name gets a run.

The sending code on this page is in examples/ts/send-events.ts and examples/go/events/main.go. Both are small scripts, not servers.

What an event holds ​

json
{
  "name": "order/paid",
  "data": { "orderId": "o_123", "total": 4200 },
  "id": "payment-pi_3Nx",
  "ts": 1792400000000,
  "user": { "id": "u_42" }
}
FieldNeededWhat it is
nameyesWhat happened. 1 to 256 characters. Names starting with inngest/ are kept for the engine.
datanoAny JSON. Your function reads it as event.data. Default {}.
idnoYour own ID, up to 256 characters. Two events with the same id are one event.
tsnoWhen it happened, in ms since 1970. Default: when the engine got it. A time in the future starts the runs later.
usernoAny JSON about who did it. Your function reads it as event.user.

The engine gives every event an ID, like evt_01M5WT35Z8M2KWAATH0ECRJSKW. Your function gets the event with that ID, the name, data, ts and user.

Names. Use thing/what-happened, in the past tense: user/signed-up, order/paid. Names are case-sensitive and must match the trigger exactly.

Data. Put in what a function needs to start (IDs, a few fields), not whole records. The function can load the rest in a step, and gets fresh data that way.

Send events from your app ​

You need an event key in FLOW_EVENT_KEY. Make one in the dashboard: Add app makes one for the app, and the Keys page makes more.

ts
// One event. The answer holds the ID the engine gave it.
const { ids } = await flow.send({
  name: "order/paid",
  data: { orderId: "o_123", total: 4200 },
});
console.log("one event:", ids);
go
// One event. The answer is the ID the engine gave it.
id, err := client.Send(ctx, flow.Event{
	Name: "order/paid",
	Data: map[string]any{"orderId": "o_123", "total": 4200},
})
if err != nil {
	log.Fatal(err)
}
log.Println("one event:", id)

Up to 100 events in one request:

ts
// Up to 100 events in one request. All are taken, or none.
const many = await flow.send([
  { name: "user/signed-up", data: { userId: "u_1" } },
  { name: "user/signed-up", data: { userId: "u_2" } },
]);
console.log("two events:", many.ids);
go
// Up to 100 events in one request. All are taken, or none.
ids, err := client.SendMany(ctx, []flow.Event{
	{Name: "user/signed-up", Data: map[string]any{"userId": "u_1"}},
	{Name: "user/signed-up", Data: map[string]any{"userId": "u_2"}},
})
if err != nil {
	log.Fatal(err)
}
log.Println("two events:", ids)

Both give back the IDs the engine gave the events, in order. A request is all or nothing: if one event is bad, none are taken, and you get an error with the engine's answer:

ts
// A bad event fails the whole request with an Error that holds the engine's answer.
try {
  await flow.send({ name: "inngest/anything" });
} catch (err) {
  console.log("refused:", err instanceof Error ? err.message : err);
}
go
// A bad event fails the whole request with an error that holds the engine's answer.
if _, err := client.Send(ctx, flow.Event{Name: "inngest/anything"}); err != nil {
	log.Println("refused:", err)
}
refused: sending events failed (400): event name inngest/anything is reserved

The SDK does not retry a failed send. If it matters, retry it yourself, with the same id (below).

On Cloudflare Workers, settings live in the handler's env, not in process.env. Pass it: await flow.send(event, { env }).

Send an event only once ​

Networks fail and senders retry. To make sure a retry does not start a second run, give the event an id that is the same every time you send the same thing, for example the ID of the payment it is about:

ts
// The same `id` twice: the engine sees one event, with one event ID.
const paymentId = "pi_3Nx";
const first = await flow.send({ id: `payment-${paymentId}`, name: "order/paid", data: { orderId: "o_124" } });
const again = await flow.send({ id: `payment-${paymentId}`, name: "order/paid", data: { orderId: "o_124" } });
console.log("sent twice:", first.ids[0], again.ids[0]);
go
// The same ID twice: the engine sees one event, with one event ID.
paid := flow.Event{
	ID:   flow.StrPtr("payment-pi_3Nx"),
	Name: "order/paid",
	Data: map[string]any{"orderId": "o_124"},
}
first, err := client.Send(ctx, paid)
if err != nil {
	log.Fatal(err)
}
again, err := client.Send(ctx, paid)
if err != nil {
	log.Fatal(err)
}
log.Println("sent twice:", first, again)

Both sends gave back the same event ID:

sent twice: evt_8XWXAS18Q0H46SM5WPFWYT7CH0 evt_8XWXAS18Q0H46SM5WPFWYT7CH0

Each function runs once for that event ID. This holds while the engine keeps that run: while it runs, and 3 days after it ends (4 days after it failed). The same id in another workspace is another event. Events sent with step.sendEvent follow the same rule.

A function with debounce or batchEvents handles a repeat its own way. debounce takes it as a new event: it pushes the start back, and once the run has started, a repeat starts a new wait. batchEvents skips a repeat that is in a batch now, or was in one up to 1 hour ago.

To run a function once per value in the data (one run per order, whatever the event), use the function's idempotency option instead.

Start the runs later ​

Set ts to a time in the future. The engine makes the runs now, as Queued, and starts them at that time.

ts
// `ts` in the future: the runs are made now and start at that time.
const inAMinute = Date.now() + 60_000;
const later = await flow.send({ name: "cart/reminder", data: { cartId: "c_9" }, ts: inAMinute });
console.log("for later:", later.ids);
go
// Timestamp in the future: the runs are made now and start at that time.
later, err := client.Send(ctx, flow.Event{
	Name:      "cart/reminder",
	Data:      map[string]any{"cartId": "c_9"},
	Timestamp: time.Now().Add(time.Minute).UnixMilli(),
})
if err != nil {
	log.Fatal(err)
}
log.Println("for later:", later)

There is no upper limit on ts. Singleton and throttle are applied when the event arrives, not at ts.

This does not work for functions with debounce or batching. See Crons and scheduled jobs for all the ways to wait.

Send events from inside a function ​

Inside a function, use step.sendEvent (Go: step.Send), not flow.send. The step sends the events once, even though the function body runs many times. See Functions and steps.

Send events over HTTP ​

Any program can send events with POST /e and the event key. See Webhooks and events and the HTTP API.

Events the engine sends ​

Names that start with inngest/ belong to the engine: inngest/scheduled.timer, inngest/function.failed, inngest/function.cancelled and inngest/function.invoked. You can not send them. Your functions can be triggered by inngest/function.failed and inngest/function.cancelled. The other two are only the first event of a cron run or an invoked run: a trigger on them starts nothing. See the table.

Limits ​

WhatLimit
Events per request (flow.send, POST /e, step.sendEvent)100
One event (all its fields, as JSON)1 MB
One request body10 MB
name, id256 characters
How long events are kept (for the dashboard)4 days