Sessions
Sessions group function runs that belong to the same user flow, conversation, or job.
Add meta.sessions to an event when you want the Inngest dashboard to show all runs related to the same external ID. For example, all runs started by messages in the same AI conversation can share a conversation_id session.
Sessions do not change which functions run. They add event metadata that makes related runs easier to find and inspect.
When to use sessions
Use sessions when you already have a domain ID that ties multiple function runs together.
Common examples include AI conversations, agent runs, support tickets, imports, customer workflows, and long-running business processes.
When not to use sessions
Sessions are for high-cardinality identifiers — one ID per conversation, ticket, or job. They are not for low-cardinality labels like environment: prod.
Sessions are also best for IDs you expect to search and inspect repeatedly. For label-style or one-off filtering, use Insights or the normal runs search instead.
Add sessions to an event
Add sessions inside the event's top-level meta object.
import { inngest } from "./client";
await inngest.send({
name: "app/message.created",
data: {
messageId: "msg_01JYY8R9C5R6VAW5EJ0P4K7V90",
conversationId: "conv_1234",
},
meta: {
sessions: {
conversation_id: "conv_1234",
},
},
});
The object key is the session key. The value is the session ID.
In the example above:
conversation_idis the session keyconv_1234is the session ID
You can use the same session key across many events. Each unique session ID becomes a session in the dashboard. You can include up to 5 sessions keys.
When sending events with the TypeScript SDK, meta.sessions is supported in v4.7.0 and later.
Webhook transforms can also add sessions by returning meta.sessions in the transformed event. See webhook transforms for more details.
View sessions in the dashboard
Open the Inngest dashboard and select an environment.
Go to AI > Sessions.

Search for a session key, such as conversation_id.
![]()
The results page shows each session ID for that key, along with:
- number of runs
- failed runs and failure rate
- last active time
- functions seen in that session
Click a session row to open the detail page. The detail page shows the runs for that specific session key and ID.
Sessions are scoped to the selected environment. If you do not see a session, check that you are in the environment where the event was sent.
Data freshness
Sessions use the same indexed event and run data that powers Insights. This index is designed for search, filtering, and investigation across activity that Inngest has recorded.
Because the index is updated asynchronously, newly sent events, recently started runs, and status changes may take a short time to appear in Sessions. Treat Sessions as a historical view for finding and inspecting related runs, not as a realtime source of truth for the current state of an active workflow.
Choose a good session key
Use a session key for the kind of thing you want to inspect later.
Good examples:
conversation_idagent_run_idticket_idworkflow_idimport_id
Avoid putting the session ID in the session key. For example, use this:
meta: {
sessions: {
conversation_id: "conv_1234",
},
}
Not this:
meta: {
sessions: {
"conversation_id:conv_1234": "true",
},
}
Session IDs are treated as opaque strings. They can contain characters like : or /, and the dashboard will keep the session key and ID separate.
Supported values
Session IDs can be strings or finite numbers. Numbers are stored as strings.
null is also a valid value: it clears a propagated session rather than creating one (see overriding propagated sessions). Other value types, including booleans, objects, and arrays, are rejected.
Session keys must be non-empty strings; null is not a valid key.
await inngest.send({
name: "app/agent.step.completed",
data: {
stepId: "step_1",
},
meta: {
sessions: {
conversation_id: "conv_1234",
thread_id: 29563,
},
},
});
Use stable IDs that are safe to show in the dashboard. Do not store secrets or sensitive personal data in session IDs.
Limits
- Each event can include up to 5 entries in
meta.sessions. For non-batched functions, that means the run can be associated with up to 5 sessions. - For batched functions, the run combines sessions from every event in the batch and can be associated with up to 25 unique session key/ID pairs.
- Session keys and IDs cannot be empty.
- Session IDs must be strings or finite numbers. Numbers are normalized to strings.
| Field | Limit |
|---|---|
| Session key | 128 bytes |
| Session ID | 512 bytes |
Session ID limits apply after numbers are normalized to strings.
Session propagation
A run's sessions automatically propagate into events created by that run, starting in inngest-js v4.18.0.
Session propagation applies to step.sendEvent(), step.invoke(), inngest.send(), deferred functions started with defer(), and failure and lifecycle events (inngest/function.finished, .failed, .cancelled).
Events are limited to 5 sessions. When combining propagated sessions with manual sessions would exceed the limit, manual sessions are kept first. Propagated sessions that are not overridden are then added in alphabetical order until the limit is reached.
Overriding propagated sessions
In some cases, you may want to override a propagated session. Manually set sessions always override propagated sessions.
If you'd like to clear a session without setting a new one, set the key to null.
await step.invoke("summarize-conversation", {
function: summarizeConversation,
data: { conversationId: event.data.conversationId },
meta: {
sessions: {
conversation_id: null,
user_id: "usr_xyz",
},
},
});
To clear all propagated sessions, set the entire sessions object to null.
await step.invoke("summarize-conversation", {
function: summarizeConversation,
data: { conversationId: event.data.conversationId },
meta: {
sessions: null,
},
});
Disabling propagation
Session propagation is on by default. You can disable propagation by configuring the client:
const inngest = new Inngest({
id: "my-app",
sessionPropagation: false, // disable propagation in this client for inngest.send, step.sendEvent, step.invoke, and defer
});
Runtime caveats
Session propagation with inngest.send() will break within runtimes that don't support async local storage, such as Cloudflare Workers when nodejs_compat is off.
Sessions and step.waitForEvent()
step.waitForEvent() does not match on sessions — matching is based only on the event name and any match or if expression. The matched event's sessions are returned on result.meta?.sessions.
Sessions and batching
Batching allows a function to process multiple events in a single run. Batching affects sessions in two ways:
- The run's sessions: the run is associated with the sessions from every event in the batch. Inngest combines them, orders them alphanumerically, and keeps the first 25 unique session key/ID pairs.
- Propagated sessions: events created by the run only carry the intersection of sessions from all triggering events — the same session key and ID must be present on every event in the batch. Propagated sessions remain limited to 5.
If you are triggering a run based off a single event in the batch, we encourage you to read from the triggering event and manually set sessions.
Inspect or edit sessions with middleware
You may want to inspect or edit sessions for encryption or validation. You can use middleware to inspect the propagated and manual sessions before they are sent to the server.
For example, this middleware stops an internal session key from propagating to child runs.
import { Middleware } from "inngest";
import type { EventMeta } from "inngest";
export class SessionPolicy extends Middleware.BaseMiddleware {
readonly id = "session-policy";
// Runs for `step.sendEvent()` and `inngest.send()`.
transformSendEvent(arg: Middleware.TransformSendEventArgs) {
for (const event of arg.events) {
delete event.meta?.propagated_sessions?.internal_trace_id;
}
return arg;
}
// Runs for every step; `step.invoke()` carries the event it will send.
transformStepInput(arg: Middleware.TransformStepInputArgs) {
if (arg.stepInfo.stepType === "invoke") {
const opts = arg.input[0] as { payload: { meta?: EventMeta } };
delete opts.payload.meta?.propagated_sessions?.internal_trace_id;
}
return arg;
}
}
Related docs
- Agent Evals overview to understand how sessions, traces, scores, and experiments fit together
- Score a function run for attaching outcomes to runs and steps