Substructure vs. Inngest for AI agents
Checked August 7, 2026
Inngest is for TypeScript teams who want durable background jobs of every kind: step functions with retries and checkpoints, plus AgentKit to assemble an agent inside your own app. Substructure is the assembled agent: config in git, running in Slack, with every step of the loop open to your code.
Both survive crashes. The difference is how much you assemble before anyone on your team can talk to an agent.
| Use Inngest when | Use Substructure when |
|---|---|
| Agents are functions among many durable jobs | The agent is the product |
| You need concurrency keys, throttling, debounce | You need Slack, MCP auth, and approval |
| Your team is TypeScript | Your code is Go, Ruby, or anything on HTTP |
What Substructure does that Inngest does not
- Ships a working agent, not primitives. AgentKit gives you
createAgent,createNetwork, routers, shared state, and MCP tool loading. The app that hosts them, the memory beyond one run, and the Slack app are yours. - Installs Slack in one command. Inngest has no Slack surface.
- Holds the MCP credentials. In AgentKit, MCP auth is yours to build.
- Asks a person as a feature. In AgentKit it is
a documented pattern:
a tool calls
step.waitForEvent()and you build the surface that sends the resuming event. - Charges nothing per step. Inngest meters executions, a run plus every step, so an agent that thinks in many steps multiplies its own bill. Your compute bills separately, because Inngest never runs your code.
Side by side
Building the agent
| Inngest | Substructure | |
|---|---|---|
| What you get | Durable steps, plus AgentKit | A running agent |
| Language | TypeScript; AgentKit is pre-1.0 | Any language, over HTTP |
| Loop control | Yours, in your app | Every step, over the wire |
| Human approval | step.waitForEvent() plus a UI | An engine feature |
Running it
| Inngest | Substructure | |
|---|---|---|
| Durability | Checkpointed steps | Every step, before it runs |
| Who runs your code | You do; Inngest orchestrates | You do, as one HTTP call |
| Flow control | Concurrency, throttle, debounce, priority | Retries and deadlines |
| Self-host | A binary, plus Postgres and Redis for multi-node | One binary and SQLite |
Surfaces and price
| Inngest | Substructure | |
|---|---|---|
| Slack | Yours to build | One command |
| MCP auth | Yours | The engine's |
| Streaming | Realtime, plus a React hook | AG-UI, an open protocol |
| Licence | Fair-source server; Apache SDKs | Open source |
| Price | Metered executions, steps included | Flat, bring your own key |
What you write
const oncall = createAgent({
name: "oncall",
system: "You are the on-call assistant.",
model: openai({ model: "gpt-5" }),
});
const network = createNetwork({ agents: [oncall] });[worker.main] # optional: your code in the loop
url = "https://example.com/agent"
[agent.oncall]
llm = "openrouter"
model = "z-ai/glm-5.2"
system = "You are the on-call assistant."
mcp = [{ id = "sentry", tools = { read_only = true } }]
worker = "main"
[agent.oncall.slack]
name = "Oncall"That worker line does more than it looks like. The engine sends every decision
to the block's URL as JSON, and your endpoint can accept the step, change the
prompt, use a different model, or run the tool in your own system.
Where Inngest is the better choice
- Flow control. Per-key concurrency, throttling, debounce, and priority as one-line config, and nobody near it does this better.
- One platform for billing pipelines, ETL, drip campaigns, and some agents.
step.ai.infer, which is the right shape for serverless economics.- AgentKit, if you want the whole agent as TypeScript in your own repo.
Questions
Is durability really different? Both save steps. Inngest orchestrates code you host; Substructure runs the agent loop itself and calls you when it needs a decision.
Can I keep Inngest for other jobs? Yes, and a worker can trigger Inngest functions.
What does it cost when the agents get busy? The same. There is no per-step or per-token charge.
The quick start takes about five minutes.