Skip to main content

What is ctx.memory?

ctx.memory is a per-session key-value store backed by Redis. It is automatically available when the request comes from an active conversation (has a socketId) and the company has a workflow API key configured. Typical use cases:
  • Multi-step flows — remember which step the user is on
  • Shopping carts — accumulate products during the conversation
  • Counters — limit login attempts, retry tracking
  • Temporary preferences — language, format, filters during the session

Quick start

index.ts
import { define, z } from "@jelou/functions";

export default define({
  name: "registration-flow",
  description: "Multi-step registration with session memory",
  input: z.object({
    answer: z.string().optional(),
  }),
  handler: async (input, ctx) => {
    if (!ctx.memory.available) {
      return { error: "Memory not available outside of a conversation" };
    }

    const step = await ctx.memory.get("step", "start");

    if (step === "start") {
      await ctx.memory.set("step", "name", 3600);
      return { question: "What is your name?" };
    }

    if (step === "name") {
      await ctx.memory.set("name", input.answer || "", 3600);
      await ctx.memory.set("step", "email", 3600);
      return { question: "What is your email?" };
    }

    const name = await ctx.memory.get("name", "");
    await ctx.memory.delete("step");
    await ctx.memory.delete("name");
    return { completed: true, name, email: input.answer };
  },
});

Check availability

if (!ctx.memory.available) {
  ctx.log("Memory not available — no socketId or workflow API key");
  return { error: "memory_unavailable" };
}
ctx.memory.available is false when the socketId is missing (requests outside a conversation) or the workflow API key is not configured. Calling methods on an unavailable client throws an Error.

Primitives vs JSON

Use set()/get() for simple values and setJson()/getJson() for objects:
await ctx.memory.set("step", "confirmation", 3600);
const step = await ctx.memory.get("step", "start");

await ctx.memory.setJson("cart", { items: [], total: 0 }, 86400);
const cart = await ctx.memory.getJson("cart", { items: [], total: 0 });
The return type of get() matches the type of the default value:
const name = await ctx.memory.get("name", "anonymous");   // string
const attempts = await ctx.memory.get("attempts", 0);     // number
const verified = await ctx.memory.get("verified", false); // boolean

TTL (time to live)

All values expire automatically. TTL is specified in seconds.
MethodTTLMaximum
set()Optional86,400 (24h)
setJson()Required86,400 (24h)
await ctx.memory.set("step", "payment");                  // no explicit TTL
await ctx.memory.set("step", "payment", 1800);            // expires in 30 min
await ctx.memory.setJson("cart", cart, 86400);            // expires in 24h (maximum)

Limits

RestrictionValue
Max length of set()255 characters
Max TTL86,400 seconds (24h)
ScopePer session (socketId)
Values from set() that exceed 255 characters throw an Error. For larger data, use setJson().

Common patterns

handler: async (input, ctx) => {
  const step = await ctx.memory.get("step", "start");

  if (step === "start") {
    await ctx.memory.set("step", "data", 3600);
    return { next: "data" };
  }

  if (step === "data") {
    await ctx.memory.set("name", input.name, 3600);
    await ctx.memory.set("step", "confirm", 3600);
    return { next: "confirm", name: input.name };
  }

  const name = await ctx.memory.get("name", "");
  await ctx.memory.delete("step");
  await ctx.memory.delete("name");
  return { completed: true, name };
}

Error handling

import { MemoryApiError } from "@jelou/functions";

try {
  await ctx.memory.set("step", "payment", 3600);
} catch (err) {
  if (err instanceof MemoryApiError) {
    ctx.log("Memory API failed", { status: err.status, code: err.code });
    if (err.isRateLimit()) {
      return { error: "rate_limit", retryAfter: 2 };
    }
  }
  throw err;
}

When to use ctx.memory vs a database?

ctx.memoryExternal database
ScopePer session (socketId)Global
PersistenceTemporary (max 24h)Permanent
SetupZero — includedRequires connection string in secrets
Ideal forConversation state, temporary cacheUser data, history, config