📡
Inngest+Stompy

Event-driven functions with event-driven memory

Events trigger, memory persists

The Problem

Inngest is event-driven done right. Events trigger functions. Functions compose into workflows. Steps provide durability. Everything scales automatically, serverlessly. The developer experience is exceptional—define your function, subscribe to events, deploy, done.

But events are ephemeral by design.

Your user.signup event fires. A function processes it beautifully—welcome email, database update, analytics tracking. Then it's gone. When the next user.signup fires, the function has no memory of previous signups. It can't know that this is the 47th user from the same company domain, or that signups with certain email patterns tend to churn quickly.

You've built sophisticated event workflows. user.signup triggers onboarding. onboarding.complete triggers engagement. engagement.low triggers win-back. Each function in the chain is stateless. The win-back function doesn't know that this user had support issues during onboarding. It can't know that users from this segment respond better to certain messaging.

Event streams are full of patterns. Seasonal spikes. User behavior clusters. Failure correlations. Your functions process these events, but they can't learn from them. The institutional knowledge that would make your event handling smarter lives nowhere—not in your code, not in your database, nowhere accessible to the next invocation.

Event-driven architecture needs event-driven memory.

How Stompy Helps

Stompy adds persistent memory to Inngest's ephemeral events.

Each function step can query for relevant context before processing. What do we know about this user from previous events? What patterns have emerged for similar events? What worked and what didn't? Your event handlers become context-aware.

After processing, capture insights as durable context. Not just "user signed up" but "user from enterprise domain, signed up via referral, pattern suggests high engagement likelihood." This becomes searchable knowledge for all future events involving this user—or similar users.

Your event-driven functions gain learning capabilities: - **Cross-event intelligence**: Connect dots across user journeys that span dozens of events - **Pattern-aware processing**: Handle events differently based on accumulated context - **Workflow memory**: Functions in a chain share context about what's happened so far - **Failure prediction**: Recognize event patterns that historically lead to problems

Inngest handles event delivery and function durability. Stompy handles the memory layer.

Integration Walkthrough

1

Create memory utilities for Inngest

Build helper functions that integrate Stompy with Inngest's step model.

import { inngest } from "./client";
// Stompy client for Inngest functions
const STOMPY_URL = "https://mcp.stompy.ai/sse";
const STOMPY_TOKEN = process.env.STOMPY_TOKEN!;
async function searchContext(query: string, limit = 5): Promise<any[]> {
const response = await fetch(STOMPY_URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${STOMPY_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
tool: "context_search",
params: { query, limit }
})
});
const data = await response.json();
return data.contexts || [];
}
async function saveContext(
topic: string,
content: string,
tags: string[] = []
): Promise<void> {
await fetch(STOMPY_URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${STOMPY_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
tool: "lock_context",
params: {
topic,
content,
tags: ["inngest", ...tags].join(",")
}
})
});
}
2

Build context-aware event handlers

Integrate memory into your Inngest functions for smarter event processing.

export const userSignupHandler = inngest.createFunction(
{ id: "user-signup-with-memory" },
{ event: "app/user.signup" },
async ({ event, step }) => {
const { userId, email, referralSource } = event.data;
const domain = email.split("@")[1];
// Step 1: Get context about this user and similar patterns
const userContext = await step.run("get-user-context", async () => {
const [userHistory, domainPatterns] = await Promise.all([
searchContext(`user:${userId} events interactions`, 10),
searchContext(`domain:${domain} signup patterns`, 5)
]);
return {
previousInteractions: userHistory.length,
knownPatterns: domainPatterns
};
});
// Step 2: Adjust onboarding based on context
const onboardingType = await step.run("determine-onboarding", async () => {
if (userContext.previousInteractions > 0) {
return "returning-user";
}
const hasEnterprisePattern = userContext.knownPatterns.some(
p => p.content.includes("enterprise")
);
return hasEnterprisePattern ? "enterprise" : "standard";
});
// Step 3: Send appropriate welcome
await step.run("send-welcome", async () => {
await sendWelcomeEmail(email, onboardingType);
});
// Step 4: Save event context for future reference
await step.run("save-signup-context", async () => {
await saveContext(
`signup_${userId}_${Date.now()}`,
`User: ${userId}
Email domain: ${domain}
Referral: ${referralSource}
Onboarding type: ${onboardingType}
Previous interactions: ${userContext.previousInteractions}`,
["signup", `domain:${domain}`, `onboarding:${onboardingType}`]
);
});
return { userId, onboardingType };
}
);
3

Build cross-event pattern detection

Create functions that analyze patterns across your event streams.

export const weeklyPatternAnalysis = inngest.createFunction(
{ id: "weekly-event-pattern-analysis" },
{ cron: "0 9 * * 1" }, // Every Monday at 9am
async ({ step }) => {
// Step 1: Analyze signup patterns
const signupPatterns = await step.run("analyze-signups", async () => {
const signups = await searchContext("signup last 7 days", 100);
const byDomain: Record<string, number> = {};
const byOnboarding: Record<string, number> = {};
for (const s of signups) {
const domain = s.content.match(/Email domain: (\S+)/)?.[1];
const onboarding = s.content.match(/Onboarding type: (\S+)/)?.[1];
if (domain) byDomain[domain] = (byDomain[domain] || 0) + 1;
if (onboarding) byOnboarding[onboarding] = (byOnboarding[onboarding] || 0) + 1;
}
return { total: signups.length, byDomain, byOnboarding };
});
// Step 2: Analyze engagement patterns
const engagementPatterns = await step.run("analyze-engagement", async () => {
const engagements = await searchContext("engagement events last 7 days", 100);
// Analyze engagement levels, feature usage, etc.
return analyzeEngagement(engagements);
});
// Step 3: Save weekly insights
await step.run("save-weekly-insights", async () => {
await saveContext(
`weekly_patterns_${new Date().toISOString().split('T')[0]}`,
`Weekly Event Analysis
Signups: ${signupPatterns.total}
Top domains: ${JSON.stringify(signupPatterns.byDomain, null, 2)}
Onboarding distribution: ${JSON.stringify(signupPatterns.byOnboarding, null, 2)}
Engagement Summary:
${JSON.stringify(engagementPatterns, null, 2)}`,
["weekly-report", "patterns", "analytics"]
);
});
// Step 4: Notify team if significant patterns detected
if (signupPatterns.total > 1000 || engagementPatterns.churnRisk > 0.2) {
await step.run("notify-team", async () => {
await sendSlackNotification({
channel: "#product-insights",
message: `Significant patterns detected this week. Check Stompy for details.`
});
});
}
return { signupPatterns, engagementPatterns };
}
);

What You Get

  • Event handlers that understand user context from all previous interactions
  • Cross-event pattern detection that reveals trends across your event streams
  • Workflow functions that share context about user journeys
  • Integration that preserves Inngest's step durability guarantees
  • Serverless memory that scales with your serverless functions

Ready to give Inngest a memory?

Join the waitlist and be the first to know when Stompy is ready. Your Inngest projects will never forget again.