Skip to main content

Overview

The Conversational Agent is Podium’s AI-powered shopping companion engine. It gives developers two endpoints — synchronous and streaming — that turn a user message into an intelligent, context-aware conversation with access to the full commerce stack. The agent:
  • Knows the user — loads their intent profile, interaction history, and conversation memory before every turn
  • Uses tools — can search products, get recommendations, record interactions, update profiles, and create orders mid-conversation
  • Streams responses — delivers progressive text deltas, tool execution events, and product cards over Server-Sent Events
  • Remembers everything — persistent conversation history with automatic summarization keeps context across sessions
  • Is configurable — persona, tone, rules, enabled tools, and model are all overridable per request

Endpoints

MethodPathDescription
POST/companion/agent/chatSynchronous chat — returns a complete response
POST/companion/agent/chat/streamStreaming chat — returns SSE events as the agent thinks
Both endpoints share the same request body.

Request Schema

{
  "userId": "clxyz1234567890",
  "message": "I need a hydrating serum under $50, no parabens",
  "config": {
    "persona": {
      "name": "Scout",
      "vertical": "skincare",
      "tone": "warm and knowledgeable"
    },
    "rules": [
      "Always check the user's avoidance list before recommending",
      "Include price in every product mention"
    ],
    "enabledTools": ["search_products", "get_recommendations", "record_interaction"],
    "maxHistory": 20
  }
}
FieldTypeRequiredDescription
userIdstring (CUID)YesThe Podium user to converse with
messagestring (1–4000 chars)YesThe user’s message
configobjectNoOverride default agent behavior
config.personaobjectNoAgent identity — name, vertical, tone
config.rulesstring[]NoAdditional behavioral rules injected into the system prompt
config.enabledToolsstring[]NoRestrict which tools the agent can use (defaults to all)
config.maxHistorynumberNoMax conversation turns to include in context

Synchronous Response

POST /companion/agent/chat returns a complete JSON response after the agent finishes thinking, executing tools, and composing its reply.
{
  "message": "I found some great options for you! Based on your dry skin and hydration focus, here are my top picks...",
  "products": [
    {
      "id": "clprod_abc123",
      "name": "Hyaluronic Acid Serum",
      "brand": "The Ordinary",
      "price": 12.99,
      "currency": "USD",
      "imageUrl": "https://cdn.example.com/ha-serum.jpg",
      "productUrl": "https://example.com/products/ha-serum"
    }
  ],
  "profileUpdated": true,
  "orderInitiated": false
}
FieldTypeDescription
messagestringThe agent’s text response
productsProductCard[]Products the agent surfaced during tool use (if any)
profileUpdatedbooleanWhether the agent updated the user’s intent profile
orderInitiatedbooleanWhether the agent started a purchase flow
orderIdstringThe companion order ID (if an order was created)

Streaming Response

POST /companion/agent/chat/stream returns a text/event-stream with progressive events as the agent works.

Event Types

EventPayloadWhen
text{ type: "text", content: "..." }Each text delta as the agent writes
tool_start{ type: "tool_start", tool: "search_products" }When the agent begins a tool call
tool_result{ type: "tool_result", tool: "search_products", products: [...] }When a tool returns results
done{ type: "done", message: "...", products: [...], quickReplies?: [...] }Final event with complete response
memory_queued{ type: "memory_queued" }When the system queues a memory extraction job
confirmation_required{ type: "confirmation_required", productId, productName, productBrand, amount, currency, imageUrl?, spendSummary }When a purchase needs user confirmation
spend_limit_exceeded{ type: "spend_limit_exceeded", reason, spendSummary, resetInfo? }When a purchase exceeds spend limits
error{ type: "error", error: "..." }On failure
Where spendSummary is: { dailySpent: number, dailyLimit: number, txMax: number }

SSE Client Example

async function chatWithAgent(userId: string, message: string) {
  const response = await fetch(
    "https://api.podium.build/api/v1/companion/agent/chat/stream",
    {
      method: "POST",
      headers: {
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ userId, message }),
    }
  );

  const reader = response.body!.getReader();
  const decoder = new TextDecoder();
  let buffer = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop()!;

    for (const line of lines) {
      if (line.startsWith("data: ")) {
        const event = JSON.parse(line.slice(6));

        switch (event.type) {
          case "text":
            process.stdout.write(event.content);
            break;
          case "tool_start":
            console.log(`\n[Using ${event.tool}...]`);
            break;
          case "tool_result":
            if (event.products?.length) {
              console.log(`\n[Found ${event.products.length} products]`);
            }
            break;
          case "memory_queued":
            console.log("[Memory extraction queued]");
            break;
          case "confirmation_required":
            console.log(`\nConfirm purchase: ${event.productName} (${event.productBrand}) — $${event.amount} ${event.currency}`);
            console.log(`Daily spend: $${event.spendSummary.dailySpent}/$${event.spendSummary.dailyLimit}`);
            break;
          case "spend_limit_exceeded":
            console.log(`\nSpend limit exceeded: ${event.reason}`);
            if (event.resetInfo) console.log(`Resets: ${event.resetInfo}`);
            break;
          case "done":
            if (event.quickReplies?.length) {
              console.log("\nSuggested replies:", event.quickReplies);
            }
            console.log("\n--- Complete ---");
            return event;
          case "error":
            throw new Error(event.error);
        }
      }
    }
  }
}

Built-in Tools

The agent has access to 7 commerce tools that map directly to Podium API operations. Each tool is callable by the AI during conversation — no developer code needed.
ToolDescriptionMaps To
search_productsSearch the product catalog by keyword, category, brand, or price rangeGET /companion/products
get_recommendationsGet AI-ranked products based on the user’s profile and interaction historyGET /companion/recommendations/{userId}
update_profileUpdate the user’s intent profile (preferences, constraints, avoidances)PATCH /companion/profile/{userId}
record_interactionRecord a product interaction (RANK_UP, RANK_DOWN, SKIP, PURCHASED, PURCHASE_INTENT)POST /companion/interactions
initiate_orderBegin a purchase flow — collects shipping and payment details conversationallyState machine (multi-turn)
get_order_historyRetrieve the user’s recent ordersGET /companion/orders
create_orderCreate a companion order with collected shipping detailsPOST /companion/orders

Tool Selection

By default, all tools are enabled. Restrict the tool set per request to control what the agent can do:
{
  "userId": "clxyz123",
  "message": "What should I try next?",
  "config": {
    "enabledTools": ["search_products", "get_recommendations"]
  }
}
This is useful for:
  • Browse-only mode: Enable only search_products and get_recommendations
  • Profile-building mode: Enable update_profile and record_interaction
  • Full commerce mode: Enable all tools including create_order

Conversational Order Flow

When a user expresses purchase intent, the agent uses a multi-turn state machine to collect the required information: The order state persists across messages, so the user can provide information across multiple turns naturally.

Memory & Context

Persistent History

Every conversation turn (user message + agent response) is stored in persistent memory. On each new message, the agent loads:
  1. Intent profile — the user’s preferences, constraints, avoidances, and behavioral signals
  2. Conversation history — recent messages (up to maxHistory turns)
  3. Conversation summary — a compressed summary of older conversations
  4. Agent state — any in-progress workflows (e.g., pending orders)
History is persisted across sessions — not just held in memory. When a user returns hours or days later, the agent has full context from previous conversations.

Automatic Summarization

Summary extraction happens automatically at conversation milestones:
TriggerConditionPurpose
Early captureTurns 3–5, no existing memoryCapture initial preferences quickly
Regular cadenceEvery 10 user turnsKeep memory current as conversation evolves
Session gap>2 hours since last messageSummarize before context goes cold
When summarization triggers, Podium:
  1. Generates a summary of older messages
  2. Trims the history to keep only recent turns
  3. Stores the summary for future context
This keeps the agent’s context window efficient while preserving long-term knowledge about the user.

Agent Summary → Intent Profile

Summaries are stored on the user’s intent profile (agentSummary field), making conversation insights available to other parts of the platform — recommendations, the agentic product feed, and downstream analytics. Memory is also stored as a structured AgentMemory object on the user’s profile. This structured memory — preferences, goals, concerns, avoidances, products tried, and category-aware price ranges — feeds directly into recommendation scoring, reason tag generation, and proactive nudges. See Memory & Intelligence for the full schema and scoring details.

Personas

Configure the agent’s identity and behavior per request:
{
  "config": {
    "persona": {
      "name": "Scout",
      "vertical": "skincare",
      "tone": "warm, knowledgeable, concise"
    },
    "rules": [
      "Never recommend products with parabens",
      "Always mention the user's skin type when giving advice",
      "If the user seems unsure, suggest a quiz to build their profile"
    ]
  }
}
FieldEffect
persona.nameThe agent’s name (used in the system prompt)
persona.verticalProduct domain expertise (skincare, fashion, food, etc.)
persona.toneCommunication style
rulesHard behavioral rules injected into every system prompt
Different verticals can share the same agent infrastructure with entirely different personalities and expertise.

Proactive Nudges

Beyond reactive conversations, Podium’s agent can proactively re-engage users through scheduled nudges. The nudge system runs as a background cron job and generates personalized outreach based on user signals.

Nudge Types

TypeTriggerExample
re_engagementUser hasn’t chatted in several days”Hey! I noticed some new serums from The Ordinary that match your hydration goals — want me to show you?”
purchase_follow_upRecent purchase completed”How’s the HA serum working for you? Let me know and I can adjust your recommendations.”
profile_deepeningIncomplete preference profile”I noticed you haven’t told me about your SPF preferences — want to do a quick quiz?”
seasonalSeasonal product relevance”Winter’s here — time to switch up your moisturizer. Want me to find something heavier-duty?”

How It Works

  1. User selection — the system identifies users eligible for nudges based on activity signals (days since last conversation, profile completeness, recent purchases)
  2. Signal gathering — for each eligible user, the system collects their profile, loved products, recent purchases, conversation summary, and profile gaps
  3. Nudge generation — AI generates a personalized, contextual message using the user’s full signal set
  4. Delivery — the nudge is published to configured channels (Telegram, email, push) and logged
  5. History integration — nudge messages are appended to the conversation history so the agent has full context if the user replies
Nudges are logged and rate-limited to prevent over-messaging. Each nudge includes the nudgeType for analytics.

Quick-Reply Chips

The agent can suggest contextual quick-reply options to keep the conversation flowing. Chips are returned in the done event:
{
  "type": "done",
  "message": "I found some great hydrating serums for you!",
  "products": [...],
  "quickReplies": ["Yes, show me serums", "What about moisturizers?", "Skip for now"]
}
Quick replies are contextually generated based on:
FactorBehavior
Warm vs cold startNew users get onboarding-oriented chips (“Take the skin quiz”, “Tell me your budget”). Returning users get product-oriented chips.
Session saturationMax 3 chip sets per session to avoid overwhelming the user
Conversation stateChips adapt to context — quiz mode offers answer options, browsing mode offers category pivots, checkout mode offers confirmation/cancel
Quick replies are suggestions, not constraints — the user can always type a freeform message instead.

Durable Chat History

Conversations are persisted to durable storage. You can retrieve a user’s full chat history for display in your UI or for analytics:
GET /api/v1/companion/agent/chat/history/:userId?limit=50&before=cursor_id

Response

[
  {
    "id": "msg_abc123",
    "role": "user",
    "content": "I need a hydrating serum under $50",
    "timestamp": "2026-03-18T14:30:00Z"
  },
  {
    "id": "msg_abc124",
    "role": "assistant",
    "content": "I found some great options for you!",
    "timestamp": "2026-03-18T14:30:02Z",
    "products": [...]
  }
]
ParameterTypeDefaultDescription
limitinteger50Max messages to return (1–100)
beforestringCursor-based pagination — pass a message id to fetch older messages
History is ordered newest-first. Page backward through the conversation by passing the last message’s id as the before parameter.

Purchase Mode & Spend Controls

The agent classifies each product by purchase mode, determining how the transaction is executed:
ModeBehaviorExample
x402_platformFull agent checkout via x402 paymentsProducts from Podium-integrated merchants
affiliateOpens retailer link for the userSephora, Ulta, Nordstrom, etc.
externalOpens direct retailer linkBrand DTC sites

Spend Controls

For x402_platform purchases, the agent enforces spend limits to protect users:
LimitValueDescription
Per-transaction max$200No single purchase can exceed this amount
Daily spend cap$100Rolling 24-hour limit across all agent-initiated purchases
Purchase flow:
  1. User expresses purchase intent
  2. Agent resolves the product’s purchase mode
  3. For x402_platform — agent checks spend limits and emits confirmation_required with product details and current spend summary
  4. User confirms → agent executes the purchase
  5. If limits would be exceeded → agent emits spend_limit_exceeded with the reason and reset timing
{
  "type": "confirmation_required",
  "productId": "prod_abc123",
  "productName": "Hyaluronic Acid Serum",
  "productBrand": "The Ordinary",
  "amount": 12.99,
  "currency": "USD",
  "imageUrl": "https://cdn.example.com/ha-serum.jpg",
  "spendSummary": {
    "dailySpent": 45.00,
    "dailyLimit": 100.00,
    "txMax": 200.00
  }
}

First-Return Greeting

For returning subscribers, the agent can generate a personalized greeting that references their history, preferences, and recent activity. This creates a warm re-engagement experience instead of a generic “How can I help you?” Send a greeting request:
{
  "userId": "clxyz1234567890",
  "message": "__greeting__",
  "config": {
    "greeting": true
  }
}
The agent uses the user’s AgentMemory to generate the greeting — referencing products they’ve tried, concerns they’ve mentioned, and goals they’re working toward. The response arrives through the standard streaming or synchronous endpoint.
Use first-return greetings when your app detects a returning user. The __greeting__ message is a special trigger — it’s not displayed to the user, it just signals the agent to generate a personalized welcome.

Example: Full Integration

import { createPodiumClient } from "@podium-sdk/node-sdk";

const client = createPodiumClient({ apiKey: "podium_live_..." });

// 1. Create a user with an intent profile
const user = await client.user.create({
  requestBody: { email: "jane@example.com" },
});

await client.companion.createProfile({
  userId: user.id,
  requestBody: {
    skinType: "DRY",
    concerns: ["HYDRATION", "ANTI_AGING"],
    priceRange: { min: 10, max: 75 },
    avoidances: ["parabens", "sulfates"],
  },
});

// 2. Start a conversation
const response = await fetch(
  "https://api.podium.build/api/v1/companion/agent/chat",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      userId: user.id,
      message: "What serums should I try for winter dryness?",
      config: {
        persona: {
          name: "Scout",
          vertical: "skincare",
          tone: "friendly and expert",
        },
      },
    }),
  }
);

const result = await response.json();
console.log(result.message);
console.log(`Products found: ${result.products?.length ?? 0}`);

// 3. Follow up — the agent remembers the conversation
const followUp = await fetch(
  "https://api.podium.build/api/v1/companion/agent/chat",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      userId: user.id,
      message: "I like the first one. Can you order it for me?",
    }),
  }
);

const orderResult = await followUp.json();
if (orderResult.orderInitiated) {
  console.log(`Order created: ${orderResult.orderId}`);
}

Endpoint Summary

MethodPathDescription
POST/companion/agent/chatSynchronous AI chat with tools
POST/companion/agent/chat/streamStreaming AI chat via SSE
GET/companion/agent/chat/history/:userIdRetrieve durable chat history (paginated)