$npx -y skills add inngest/inngest-skills --skill inngest-eventsUse when designing event-driven workflows, decoupling services, implementing fan-out patterns (one trigger, many downstream handlers), implementing idempotent event handling with IDs (24-hour dedupe window), or handling at-least-once delivery from external sources like Stripe web
| 1 | # Inngest Events |
| 2 | |
| 3 | Master Inngest event design and delivery patterns. Events are the foundation of Inngest - learn to design robust event schemas, implement idempotency, leverage fan-out patterns, and handle system events effectively. |
| 4 | |
| 5 | > **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. |
| 6 | |
| 7 | ## Event Payload Format |
| 8 | |
| 9 | Every Inngest event is a JSON object with required and optional properties: |
| 10 | |
| 11 | ### Required Properties |
| 12 | |
| 13 | ```typescript |
| 14 | type Event = { |
| 15 | name: string; // Event type (triggers functions) |
| 16 | data: object; // Payload data (any nested JSON) |
| 17 | }; |
| 18 | ``` |
| 19 | |
| 20 | ### Complete Schema |
| 21 | |
| 22 | ```typescript |
| 23 | type EventPayload = { |
| 24 | name: string; // Required: event type |
| 25 | data: Record<string, any>; // Required: event data |
| 26 | id?: string; // Optional: deduplication ID |
| 27 | ts?: number; // Optional: timestamp (Unix ms) |
| 28 | v?: string; // Optional: schema version |
| 29 | }; |
| 30 | ``` |
| 31 | |
| 32 | ### Basic Event Example |
| 33 | |
| 34 | ```typescript |
| 35 | await inngest.send({ |
| 36 | name: "billing/invoice.paid", |
| 37 | data: { |
| 38 | customerId: "cus_NffrFeUfNV2Hib", |
| 39 | invoiceId: "in_1J5g2n2eZvKYlo2C0Z1Z2Z3Z", |
| 40 | userId: "user_03028hf09j2d02", |
| 41 | amount: 1000, |
| 42 | metadata: { |
| 43 | accountId: "acct_1J5g2n2eZvKYlo2C0Z1Z2Z3Z", |
| 44 | accountName: "Acme.ai" |
| 45 | } |
| 46 | } |
| 47 | }); |
| 48 | ``` |
| 49 | |
| 50 | ## Event Naming Conventions |
| 51 | |
| 52 | **Use the Object-Action pattern:** `domain/noun.verb` |
| 53 | |
| 54 | ### Recommended Patterns |
| 55 | |
| 56 | ```typescript |
| 57 | // ✅ Good: Clear object-action pattern |
| 58 | "billing/invoice.paid"; |
| 59 | "user/profile.updated"; |
| 60 | "order/item.shipped"; |
| 61 | "ai/summary.completed"; |
| 62 | |
| 63 | // ✅ Good: Domain prefixes for organization |
| 64 | "stripe/customer.created"; |
| 65 | "intercom/conversation.assigned"; |
| 66 | "slack/message.posted"; |
| 67 | |
| 68 | // ❌ Avoid: Unclear or inconsistent |
| 69 | "payment"; // What happened? |
| 70 | "user_update"; // Use dots, not underscores |
| 71 | "invoiceWasPaid"; // Too verbose |
| 72 | ``` |
| 73 | |
| 74 | ### Naming Guidelines |
| 75 | |
| 76 | - **Past tense:** Events describe what happened (`created`, `updated`, `failed`) |
| 77 | - **Dot notation:** Use dots for hierarchy (`billing/invoice.paid`) |
| 78 | - **Prefixes:** Group related events (`api/user.created`, `webhook/stripe.received`) |
| 79 | - **Consistency:** Establish patterns and stick to them |
| 80 | |
| 81 | ## Event IDs and Idempotency |
| 82 | |
| 83 | **When to use IDs:** Prevent duplicate processing when events might be sent multiple times. |
| 84 | |
| 85 | ### Basic Deduplication |
| 86 | |
| 87 | ```typescript |
| 88 | await inngest.send({ |
| 89 | id: "cart-checkout-completed-ed12c8bde", // Unique per event type |
| 90 | name: "storefront/cart.checkout.completed", |
| 91 | data: { |
| 92 | cartId: "ed12c8bde", |
| 93 | items: ["item1", "item2"] |
| 94 | } |
| 95 | }); |
| 96 | ``` |
| 97 | |
| 98 | ### ID Best Practices |
| 99 | |
| 100 | ```typescript |
| 101 | // ✅ Good: Specific to event type and instance |
| 102 | id: `invoice-paid-${invoiceId}`; |
| 103 | id: `user-signup-${userId}-${timestamp}`; |
| 104 | id: `order-shipped-${orderId}-${trackingNumber}`; |
| 105 | |
| 106 | // ❌ Bad: Generic IDs shared across event types |
| 107 | id: invoiceId; // Could conflict with other events |
| 108 | id: "user-action"; // Too generic |
| 109 | id: customerId; // Same customer, different events |
| 110 | ``` |
| 111 | |
| 112 | **Deduplication window:** 24 hours from first event reception |
| 113 | |
| 114 | See **inngest-durable-functions** for idempotency configuration. |
| 115 | |
| 116 | ## The `ts` Parameter for Delayed Delivery |
| 117 | |
| 118 | **When to use:** Schedule events for future processing or maintain event ordering. |
| 119 | |
| 120 | ### Future Scheduling |
| 121 | |
| 122 | ```typescript |
| 123 | const oneHourFromNow = Date.now() + 60 * 60 * 1000; |
| 124 | |
| 125 | await inngest.send({ |
| 126 | name: "trial/reminder.send", |
| 127 | ts: oneHourFromNow, // Deliver in 1 hour |
| 128 | data: { |
| 129 | userId: "user_123", |
| 130 | trialExpiresAt: "2024-02-15T12:00:00Z" |
| 131 | } |
| 132 | }); |
| 133 | ``` |
| 134 | |
| 135 | ### Maintaining Event Order |
| 136 | |
| 137 | ```typescript |
| 138 | // Events with timestamps are processed in chronological order |
| 139 | const events = [ |
| 140 | { |
| 141 | name: "user/action.performed", |
| 142 | ts: 1640995200000, // Earlier |
| 143 | data: { action: "login" } |
| 144 | }, |
| 145 | { |
| 146 | name: "user/action.performed", |
| 147 | ts: 1640995260000, // Later |
| 148 | data: { action: "purchase" } |
| 149 | } |
| 150 | ]; |
| 151 | |
| 152 | await inngest.send(events); |
| 153 | ``` |
| 154 | |
| 155 | ## Fan-Out Patterns |
| 156 | |
| 157 | **Use case:** One event triggers multiple independent functions for reliability and parallel processing. |
| 158 | |
| 159 | ### Basic Fan-Out Implementation |
| 160 | |
| 161 | ```typescript |
| 162 | // Send single event |
| 163 | await inngest.send({ |
| 164 | name: "user/signup.completed", |
| 165 | data: { |
| 166 | userId: "user_123", |
| 167 | email: "user@example.com", |
| 168 | plan: "pro" |
| 169 | } |
| 170 | }); |
| 171 | |
| 172 | // Multiple functions respond to same event |
| 173 | const sendWelcomeEmail = inngest.createFunction( |
| 174 | { id: "send-welcome-email", triggers: [{ event: "user/signup.completed" }] }, |
| 175 | async ({ event, step }) => { |
| 176 | await step.run("send-email", async () => { |
| 177 | return sendEmail({ |