
Inngest Steps
Model delays, human gates, polling, invokes, and parallel work as Inngest steps so workflows survive restarts, retries independently, and stay within step data limits.
Overview
Inngest-steps is an agent skill most often used in Build (also Operate iterate) that teaches Inngest step methods for durable delays, waits, invokes, and parallel workflow segments.
Install
npx skills add https://github.com/inngest/inngest-skills --skill inngest-stepsWhat is this skill?
- Covers step.run, step.sleep, step.waitForEvent, step.waitForSignal, step.sendEvent, step.invoke, step.ai
- Stress rule: non-deterministic work only inside steps because each step replays from function start
- Documented caps: max 1,000 steps and 4MB total step data per function
- Patterns for cart abandonment delays, approval timeouts, webhook waits, and parallel execution
- TypeScript-first with pointer to Inngest docs for Python and Go
- Maximum 1,000 steps per Inngest function
- Maximum 4MB total step data per function
Adoption & trust: 2.7k installs on skills.sh; 23 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need 24-hour delays or webhook gates in your app but vanilla async code loses state on restart and retries duplicate API calls.
Who is it for?
Indie backends building abandonment emails, approval flows, async webhooks, and agent pipelines on Inngest TypeScript.
Skip if: Simple one-shot HTTP handlers with no retry semantics, or teams not using Inngest orchestration.
When should I use this skill?
Implementing delays that survive restarts, human or webhook waits with timeouts, polling without crash data loss, step.invoke chains, memoized expensive steps, or parallel async segments in Inngest.
What do I get? / Deliverables
You structure Inngest functions with correct step boundaries so sleeps, waits, and invokes retry safely and respect 1,000-step and 4MB data limits.
- Inngest functions using appropriate step APIs
- Retry-safe workflow code with side effects isolated in steps
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Step methods are how durable backend workflows are authored before they run in production. step.run, step.sleep, step.waitForEvent, and related APIs are server-side orchestration primitives, not UI or marketing tasks.
Where it fits
Add step.waitForSignal so a moderation queue unblocks a user-generated content pipeline.
Poll a slow third-party job inside step.run with memoization so retries do not re-bill the vendor.
Simulate step replay to verify no API calls sit outside step boundaries.
Extend a live function with step.sendEvent without exceeding 1,000 steps per run.
How it compares
Use for Inngest-native durability—not for in-memory setTimeout hacks or undifferentiated generic queue snippets.
Common Questions / FAQ
Who is inngest-steps for?
Developers implementing event-driven business logic that must survive crashes, deploys, and independent per-step retries.
When should I use inngest-steps?
In build when coding workflows; in ship when testing retry behavior; in operate when adding waits or invokes to live functions without double side effects.
Is inngest-steps safe to install?
It documents orchestration patterns; review the Security Audits panel on this Prism page for the hosting repo before install.
Workflow Chain
Then invoke: inngest durable functions
SKILL.md
READMESKILL.md - Inngest Steps
# Inngest Steps Build robust, durable workflows with Inngest's step methods. Each step is a separate HTTP request that can be independently retried and monitored. > **These skills are focused on TypeScript.** For Python or Go, refer to the [Inngest documentation](https://www.inngest.com/llms.txt) for language-specific guidance. Core concepts apply across all languages. ## Core Concept **🔄 Critical: Each step re-runs your function from the beginning.** Put ALL non-deterministic code (API calls, DB queries, randomness) inside steps, never outside. **📊 Step Limits:** Every function has a maximum of 1,000 steps and 4MB total step data. ```typescript // ❌ WRONG - will run 4 times export default inngest.createFunction( { id: "bad-example", triggers: [{ event: "test" }] }, async ({ step }) => { console.log("This logs 4 times!"); // Outside step = bad await step.run("a", () => console.log("a")); await step.run("b", () => console.log("b")); await step.run("c", () => console.log("c")); } ); // ✅ CORRECT - logs once each export default inngest.createFunction( { id: "good-example", triggers: [{ event: "test" }] }, async ({ step }) => { await step.run("log-hello", () => console.log("hello")); await step.run("a", () => console.log("a")); await step.run("b", () => console.log("b")); await step.run("c", () => console.log("c")); } ); ``` ## step.run() Execute retriable code as a step. **Each step ID can be reused** - Inngest automatically handles counters. ```typescript // Basic usage const result = await step.run("fetch-user", async () => { const user = await db.user.findById(userId); return user; // Always return useful data }); // Synchronous code works too const transformed = await step.run("transform-data", () => { return processData(result); }); // Side effects (no return needed) await step.run("send-notification", async () => { await sendEmail(user.email, "Welcome!"); }); ``` **✅ DO:** - Put ALL non-deterministic logic inside steps - Return useful data for subsequent steps - Reuse step IDs in loops (counters handled automatically) **❌ DON'T:** - Put deterministic logic in steps unnecessarily - Forget that each step = separate HTTP request ## step.sleep() Pause execution without using compute time. ```typescript // Duration strings await step.sleep("wait-24h", "24h"); await step.sleep("short-delay", "30s"); await step.sleep("weekly-pause", "7d"); // Use in workflows await step.run("send-welcome", () => sendEmail(email)); await step.sleep("wait-for-engagement", "3d"); await step.run("send-followup", () => sendFollowupEmail(email)); ``` ## step.sleepUntil() Sleep until a specific datetime. ```typescript const reminderDate = new Date("2024-12-25T09:00:00Z"); await step.sleepUntil("wait-for-christmas", reminderDate); // From event data const scheduledTime = new Date(event.data.remind_at); await step.sleepUntil("wait-for-scheduled-time", scheduledTime); ``` ## step.waitForEvent() **🚨 CRITICAL: waitForEvent ONLY catches events sent AFTER this step executes.** - ❌ Event sent before waitForEvent runs → will NOT be caught - ✅ Event sent after waitForEvent runs → will be caught - Always check for `null` return (means timeout, event never arrived) ```typescript // Basic event waiting with timeout