inngest-events
This skill teaches event design and delivery patterns for Inngest, covering event payload structure, naming conventions, idempotency with event IDs, scheduled…
Identity proven by GitHub's verified organisation badge (inngest)
- Agents
- Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, claude.ai and API
- License
- Apache-2.0 (permissive)
- Pin
- 082798apinned Oct 9, 2026
- Context cost
- about 2,900 tokenswhen the skill loads
- GitHub stars
- 28on inngest/inngest-skills
- Scripts
- None found
What it does
“Use 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 webhooks. Covers Inngest event schema, payload format, naming conventions, IDs for idempotency, the ts param, fan-out patterns, and system events like inngest/function.failed.”
Generated summary from the skill's own text
This skill teaches event design and delivery patterns for Inngest, covering event payload structure, naming conventions, idempotency with event IDs, scheduled delivery with timestamps, fan-out patterns for decoupled processing, and system event handling. It provides guidance on creating robust event schemas, implementing deduplication within a 24-hour window, and leveraging events for reliable event-driven workflows.
When to use it
- Designing event-driven architectures with multiple downstream handlers from single events
- Implementing idempotent event processing with deduplication requirements
- Handling webhook integrations like Stripe with at-least-once delivery semantics
- Building loosely coupled services that react to domain events independently
When not to
- Request-response patterns where synchronous calls are more appropriate
- When event ordering guarantees beyond timestamp-based ordering are required
- Systems not using Inngest as the event backbone
Install
These commands install from the publisher's repository and need no account.
skills CLI
npx skills add inngest/inngest-skills --skill inngest-eventsInstalls from the repository's default branch, which may have changed since the commit we checked.
GitHub CLI 2.90 or later
gh skill install inngest/inngest-skills inngest-events@082798a627bbe9c3ed3a6638a9f56116a035f122Installs the commit we checked (082798a).
Copy by hand
Download the folder at 082798a and put it in .claude/skills/inngest-events for Claude Code and in .agents/skills/inngest-events for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.
Copy for your agent
A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.
Prompt
Install the agent skill "inngest-events" by Inngest. Source, pinned to commit 082798a627bbe9c3ed3a6638a9f56116a035f122: https://github.com/inngest/inngest-skills/tree/082798a627bbe9c3ed3a6638a9f56116a035f122/skills/inngest-events 1. Download that folder at exactly this commit. Do not use the default branch. 2. Copy every file unchanged, keeping the folder structure, into .claude/skills/inngest-events/ for Claude Code and .agents/skills/inngest-events/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist. 3. Do not edit the files, and do not run any script in the folder unless I ask you to. 4. When you are done, list the files you copied and where you put them. Listing and checks: https://www.agentnative.dev/skills/inngest/inngest-events-inngest-skills
SKILL.md at the pin
---
name: inngest-events
description: Use 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 webhooks. Covers Inngest event schema, payload format, naming conventions, IDs for idempotency, the ts param, fan-out patterns, and system events like inngest/function.failed.
---
# Inngest Events
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.
> **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.
## Event Payload Format
Every Inngest event is a JSON object with required and optional properties:
### Required Properties
```typescript
type Event = {
name: string; // Event type (triggers functions)
data: object; // Payload data (any nested JSON)
};
```
### Complete Schema
```typescript
type EventPayload = {
name: string; // Required: event type
data: Record<string, any>; // Required: event data
id?: string; // Optional: deduplication ID
ts?: number; // Optional: timestamp (Unix ms)
v?: string; // Optional: schema version
};
```
### Basic Event Example
```typescript
await inngest.send({
name: "billing/invoice.paid",
data: {
customerId: "cus_NffrFeUfNV2Hib",
invoiceId: "in_1J5g2n2eZvKYlo2C0Z1Z2Z3Z",
userId: "user_03028hf09j2d02",
amount: 1000,
metadata: {
accountId: "acct_1J5g2n2eZvKYlo2C0Z1Z2Z3Z",
accountName: "Acme.ai"
}
}
});
```
## Event Naming Conventions
**Use the Object-Action pattern:** `domain/noun.verb`
### Recommended Patterns
```typescript
// ✅ Good: Clear object-action pattern
"billing/invoice.paid";
"user/profile.updated";
"order/item.shipped";
"ai/summary.completed";
// ✅ Good: Domain prefixes for organization
"stripe/customer.created";
"intercom/conversation.assigned";
"slack/message.posted";
// ❌ Avoid: Unclear or inconsistent
"payment"; // What happened?
"user_update"; // Use dots, not underscores
"invoiceWasPaid"; // Too verbose
```
### Naming Guidelines
- **Past tense:** Events describe what happened (`created`, `updated`, `failed`)
- **Dot notation:** Use dots for hierarchy (`billing/invoice.paid`)
- **Prefixes:** Group related events (`api/user.created`, `webhook/stripe.received`)
- **Consistency:** Establish patterns and stick to them
## Event IDs and Idempotency
**When to use IDs:** Prevent duplicate processing when events might be sent multiple times.
### Basic Deduplication
```typescript
await inngest.send({
id: "cart-checkout-completed-ed12c8bde", // Unique per event type
name: "storefront/cart.checkout.completed",
data: {
cartId: "ed12c8bde",
items: ["item1", "item2"]
}
});
```
### ID Best Practices
```typescript
// ✅ Good: Specific to event type and instance
id: `invoice-paid-${invoiceId}`;
id: `user-signup-${userId}-${timestamp}`;
id: `order-shipped-${orderId}-${trackingNumber}`;
// ❌ Bad: Generic IDs shared across event types
id: invoiceId; // Could conflict with other events
id: "user-action"; // Too generic
id: customerId; // Same customer, different events
```
**Deduplication window:** 24 hours from first event reception
See **inngest-durable-functions** for idempotency configuration.
## The `ts` Parameter for Delayed Delivery
**When to use:** Schedule events for future processing or maintain event ordering.
### Future Scheduling
```typescript
const oneHourFromNow = Date.now() + 60 * 60 * 1000;
await inngest.send({
name: "trial/reminder.send",
ts: oneHourFromNow, // Deliver in 1 hour
data: {
userId: "user_123",
trialExpiresAt: "2024-02-15T12:00:00Z"
}
});
```
### Maintaining Event Order
```typescript
// Events with timestamps are processed in chronological order
const events = [
{
name: "user/action.performed",
ts: 1640995200000, // Earlier
data: { action: "login" }
},
{
name: "user/action.performed",
ts: 1640995260000, // Later
data: { action: "purchase" }
}
];
await inngest.send(events);
```
## Fan-Out Patterns
**Use case:** One event triggers multiple independent functions for reliability and parallel processing.
### Basic Fan-Out Implementation
```typescript
// Send single event
await inngest.send({
name: "user/signup.completed",
data: {
userId: "user_123",
email: "user@example.com",
plan: "pro"
}
});
// Multiple functions respond to same event
const sendWelcomeEmail = inngest.createFunction(
{ id: "send-welcome-email", triggers: [{ event: "user/signup.completed" }] },
async ({ event, step }) => {
await step.run("send-email", async () => {
return sendEmail({
to: event.data.email,
template: "welcome"
});
});
}
);
const createTrialSubscription = inngest.createFunction(
{ id: "create-trial", triggers: [{ event: "user/signup.completed" }] },
async ({ event, step }) => {
await step.run("create-subscription", async () => {
return stripe.subscriptions.create({
customer: event.data.stripeCustomerId,
trial_period_days: 14
});
});
}
);
const addToCrm = inngest.createFunction(
{ id: "add-to-crm", triggers: [{ event: "user/signup.completed" }] },
async ({ event, step }) => {
await step.run("crm-sync", async () => {
return crm.contacts.create({
email: event.data.email,
plan: event.data.plan
});
});
}
);
```
### Fan-Out Benefits
- **Independence:** Functions run separately; one failure doesn't affect others
- **Parallel execution:** All functions run simultaneously
- **Selective replay:** Re-run only failed functions
- **Cross-service:** Trigger functions in different codebases/languages
### Advanced Fan-Out with `waitForEvent`
In expressions, `event` = the **original** triggering event, `async` = the **new** event being matched. See [Expression Syntax Reference](../references/expressions.md) for full details.
```typescript
const orchestrateOnboarding = inngest.createFunction(
{ id: "orchestrate-onboarding", triggers: [{ event: "user/signup.completed" }] },
async ({ event, step }) => {
// Fan out to multiple services
await step.sendEvent("fan-out", [
{ name: "email/welcome.send", data: event.data },
{ name: "subscription/trial.create", data: event.data },
{ name: "crm/contact.add", data: event.data }
]);
// Wait for all to complete
const [emailResult, subResult, crmResult] = await Promise.all([
step.waitForEvent("email-sent", {
event: "email/welcome.sent",
timeout: "5m",
if: `event.data.userId == async.data.userId`
}),
step.waitForEvent("subscription-created", {
event: "subscription/trial.created",
timeout: "5m",
if: `event.data.userId == async.data.userId`
}),
step.waitForEvent("crm-synced", {
event: "crm/contact.added",
timeout: "5m",
if: `event.data.userId == async.data.userId`
})
]);
// Complete onboarding
await step.run("complete-onboarding", async () => {
return completeUserOnboarding(event.data.userId);
});
}
);
```
See **inngest-steps** for additional patterns including `step.invoke`.
## System Events
Inngest emits system events for function lifecycle monitoring:
### Available System Events
```typescript
// Function execution events
"inngest/function.failed"; // Function failed after retries
"inngest/function.finished"; // Function finished - completed or failed
"inngest/function.cancelled"; // Function cancelled before completion
```
### Handling Failed Functions
```typescript
const handleFailures = inngest.createFunction(
{ id: "handle-failed-functions", triggers: [{ event: "inngest/function.failed" }] },
async ({ event, step }) => {
const { function_id, run_id, error } = event.data;
await step.run("log-failure", async () => {
logger.error("Function failed", {
functionId: function_id,
runId: run_id,
error: error.message,
stack: error.stack
});
});
// Alert on critical function failures
if (function_id.includes("critical")) {
await step.run("send-alert", async () => {
return alerting.sendAlert({
title: `Critical function failed: ${function_id}`,
severity: "high",
runId: run_id
});
});
}
// Auto-retry certain failures
if (error.code === "RATE_LIMIT_EXCEEDED") {
await step.run("schedule-retry", async () => {
return inngest.send({
name: "retry/function.requested",
ts: Date.now() + 5 * 60 * 1000, // Retry in 5 minutes
data: { originalRunId: run_id }
});
});
}
}
);
```
## Sending Events
### Client Setup
```typescript
// inngest/client.ts
import { Inngest } from "inngest";
export const inngest = new Inngest({
id: "my-app"
});
// You must set INNGEST_EVENT_KEY environment variable in production
```
### Single Event
```typescript
const result = await inngest.send({
name: "order/placed",
data: {
orderId: "ord_123",
customerId: "cus_456",
amount: 2500,
items: [
{ id: "item_1", quantity: 2 },
{ id: "item_2", quantity: 1 }
]
}
});
// Returns event IDs for tracking
console.log(result.ids); // ["01HQ8PTAESBZPBDS8JTRZZYY3S"]
```
### Batch Events
```typescript
const orderItems = await getOrderItems(orderId);
// Convert to events
const events = orderItems.map((item) => ({
name: "inventory/item.reserved",
data: {
itemId: item.id,
orderId: orderId,
quantity: item.quantity,
warehouseId: item.warehouseId
}
}));
// Send all at once (up to 512kb)
await inngest.send(events);
```
### Sending from Functions
```typescript
inngest.createFunction(
{ id: "process-order", triggers: [{ event: "order/placed" }] },
async ({ event, step }) => {
// Use step.sendEvent() instead of inngest.send() in functions
// for reliability and deduplication
await step.sendEvent("trigger-fulfillment", {
name: "fulfillment/order.received",
data: {
orderId: event.data.orderId,
priority: event.data.customerTier === "premium" ? "high" : "normal"
}
});
}
);
```
## Event Design Best Practices
### Schema Versioning
```typescript
// Use version field to track schema changes
await inngest.send({
name: "user/profile.updated",
v: "2024-01-15.1", // Schema version
data: {
userId: "user_123",
changes: {
email: "new@example.com",
preferences: { theme: "dark" }
},
// New field in v2 schema
auditInfo: {
changedBy: "user_456",
reason: "user_requested"
}
}
});
```
### Rich Context Data
```typescript
// Include enough context for all consumers
await inngest.send({
name: "payment/charge.succeeded",
data: {
// Primary identifiers
chargeId: "ch_123",
customerId: "cus_456",
// Amount details
amount: 2500,
currency: "usd",
// Context for different consumers
subscription: {
id: "sub_789",
plan: "pro_monthly"
},
invoice: {
id: "inv_012",
number: "INV-2024-001"
},
// Metadata for debugging
paymentMethod: {
type: "card",
last4: "4242",
brand: "visa"
},
metadata: {
source: "stripe_webhook",
environment: "production"
}
}
});
```
**Event design principles:**
1. **Self-contained:** Include all data consumers need
2. **Immutable:** Never modify event schemas after sending
3. **Traceable:** Include correlation IDs and audit trails
4. **Actionable:** Provide enough context for business logic
5. **Debuggable:** Include metadata for troubleshooting
From inngest/inngest-skills at 082798a, shown as plain text under its license: Apache-2.0 (permissive). The license text is in LICENSE.
View SKILL.md on GitHub at 082798aFiles
- SKILL.md12 KB · 3ae75749e843
1 file, 12 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.
Skill folder on GitHub at 082798aAlso in
The same SKILL.md content appears in these places too. We list it once and credit the oldest public source.
More from Inngest
inngest-agent-evals
Builds production evaluation workflows for AI agents on Inngest using direct and deferred scoring, sessions for grouping related runs, step experiments for…
Claude Code, Codex, Cursor +4 · about 2.6K tokens · pinned 6 days ago
inngest-agents
Build durable AI agents and multi-step agentic workflows using Inngest and AgentKit.
Claude Code, Codex, Cursor +4 · about 2.1K tokens · pinned 6 days ago
inngest-api-cli
This skill routes the agent to use the Inngest CLI `inngest api` command for operational tasks like debugging runs, querying events, syncing apps, invoking…
Claude Code, Codex, Cursor +4 · about 1.9K tokens · pinned 6 days ago
inngest-api
This skill enables agents to interact with the Inngest REST API v2 for Cloud operations, raw HTTP requests, and API reference lookups.
Claude Code, Codex, Cursor +4 · about 1.1K tokens · pinned 6 days ago
inngest-brownfield-audit
This skill audits existing TypeScript or JavaScript codebases to identify where Inngest can improve reliability and durability.
Claude Code, Codex, Cursor +4 · about 2.5K tokens · pinned 6 days ago
inngest-cli
This skill enables installation and operation of the Inngest CLI for local development and testing with the Dev Server, which provides a browser UI at…
Claude Code, Codex, Cursor +4 · about 3.4K tokens · pinned 6 days ago
Related in Backend and APIs
inngest-flow-control
This skill provides Inngest flow control mechanisms to manage API rate limits, prevent duplicate processing, and ensure fair resource allocation.
by InngestOfficial publisher
Claude Code, Codex, Cursor +4 · about 2.1K tokens · pinned 6 days ago
inngest-middleware
This skill teaches how to implement middleware in Inngest durable functions to handle cross-cutting concerns like structured logging, error tracking…
by InngestOfficial publisher
Claude Code, Codex, Cursor +4 · about 2.4K tokens · pinned 6 days ago
wix-headless
This skill configures Wix backend services (Stores, Bookings, CMS, Blog, Events, Forms, etc.) for a Wix Headless frontend by inferring needed capabilities…
by Wix.comOfficial publisher
Contains scripts
Claude Code, Codex, Cursor +3 · about 3.8K tokens · pinned 6 days ago
inngest-durable-functions
Build fault-tolerant, long-running workflows using Inngest's durable execution model in TypeScript.
by InngestOfficial publisher
Claude Code, Codex, Cursor +4 · about 3.5K tokens · pinned 6 days ago
inngest-realtime
Stream live updates from Inngest v4 durable workflows to browser UIs via typed channels and topics.
by InngestOfficial publisher
Claude Code, Codex, Cursor +4 · about 4.5K tokens · pinned 6 days ago
inngest-setup
This skill sets up Inngest, a durable execution platform, in TypeScript projects for building retry-safe webhook handlers, background jobs, and scheduled tasks.
by InngestOfficial publisher
Claude Code, Codex, Cursor +4 · about 2.9K tokens · pinned 6 days ago
Open roles at Inngest
No open roles at Inngest on the Agent Native jobs board right now.
Browse the jobs board