Documentation Index
Fetch the complete documentation index at: https://podium.build/docs/llms.txt
Use this file to discover all available pages before exploring further.
Build an agent that learns what a user wants through structured preferences, delivers personalized product recommendations, and can purchase on their behalf. The Beauty Companion is a live implementation of this pattern.
What You’ll Build
Prerequisites
npm install @podium-sdk/node-sdk
import { createPodiumClient, ApiError } from '@podium-sdk/node-sdk';
const client = createPodiumClient({
apiKey: process.env.PODIUM_API_KEY,
});
Step 1: Create an Intent Profile
The intent profile is the agent’s understanding of what the user wants. Build it from a quiz, conversation, or any structured input.
async function createUserProfile(userId: string, preferences: {
skinType: string;
concerns: string[];
priceRange: { min: number; max: number };
brands: string[];
avoidances: string[];
}) {
const profile = await client.companion.createProfile({
userId,
requestBody: preferences,
});
return profile;
}
await createUserProfile('user_abc', {
skinType: 'combination',
concerns: ['acne', 'dark spots', 'texture'],
priceRange: { min: 15, max: 50 },
brands: ['CeraVe', 'The Ordinary', 'Paula\'s Choice'],
avoidances: ['parabens', 'fragrance', 'sulfates'],
});
Incremental Profile Updates
For step-by-step quiz flows, use PATCH to add fields without overwriting the full profile:
await client.companion.updateProfile({
userId: 'user_abc',
requestBody: {
skinType: 'combination',
},
});
// Later, add concerns
await client.companion.updateProfile({
userId: 'user_abc',
requestBody: {
concerns: ['acne', 'dark spots'],
},
});
Step 2: Get Personalized Recommendations
The recommendation engine uses the intent profile, interaction history, and enrichment baselines to rank products.
const recs = await client.companion.listRecommendations({
userId: 'user_abc',
count: 5,
category: 'skincare',
});
for (const product of recs.recommendations) {
console.log(`${product.name} by ${product.brand} — $${product.price}`);
console.log(` Score: ${product.relevanceScore}`);
console.log(` Reason: ${product.matchReason}`);
}
Step 3: Record Interactions
Every interaction signal improves future recommendations. The agent records explicit feedback — not passive tracking.
type InteractionAction = 'RANK_UP' | 'RANK_DOWN' | 'SKIP' | 'PURCHASED' | 'PURCHASE_INTENT' | 'NUDGE_OPENED';
async function recordInteraction(
userId: string,
productId: string,
action: InteractionAction,
score?: number
) {
await client.companion.createInteractions({
requestBody: {
userId,
productId,
action,
score, // optional 0-1 normalized relevance score
},
});
}
await recordInteraction('user_abc', 'prod_serum_01', 'RANK_UP');
await recordInteraction('user_abc', 'prod_cream_02', 'SKIP');
await recordInteraction('user_abc', 'prod_serum_01', 'PURCHASE_INTENT');
Retrieve Interaction History
const interactions = await client.companion.listInteractions({
userId: 'user_abc',
});
Step 4: Browse the Product Catalog
The companion has its own product catalog with filtering:
const products = await client.companion.listProducts({
category: 'skincare',
minPrice: 15,
maxPrice: 50,
inStock: true,
search: 'retinol',
limit: 20,
offset: 0,
});
Step 5: Place an Order
When the user confirms a purchase, create a concierge order. The order starts in PENDING_PAYMENT and must be settled via x402 USDC.
async function placeOrder(
userId: string,
productId: string,
email: string,
shippingAddress: { street: string; city: string; state: string; zip: string }
) {
const order = await client.companion.createOrders({
requestBody: {
userId,
productId,
shippingAddress,
email,
},
});
await recordInteraction(userId, productId, 'PURCHASED');
return order;
}
Track Order Status
const orders = await client.companion.listOrders({
userId: 'user_abc',
});
for (const order of orders.orders) {
console.log(`${order.id}: ${order.status}`);
// PENDING_PAYMENT → PAID → FULFILLING → SHIPPED → DELIVERED
}
Update Order Status (Admin)
await client.companion.updateOrders({
orderId: 'order_xyz',
requestBody: {
status: 'SHIPPED',
fulfillmentNotes: 'Tracking: 1Z999AA10123456784',
},
});
Step 6: Award Engagement Points
Reward users for interacting with the agent — quiz completions, swipe sessions, purchases.
await client.companion.createProfilePoints({
userId: 'user_abc',
requestBody: {
amount: 100,
details: { reason: 'quiz_completion', step: 'full_profile' },
},
});
Putting It Together
Here’s a complete agent loop — the core of what a Telegram bot, MCP server, or mobile app would run:
import { createPodiumClient } from '@podium-sdk/node-sdk';
const client = createPodiumClient({ apiKey: process.env.PODIUM_API_KEY });
async function agentLoop(userId: string) {
let profile = await client.companion.listProfile({ userId }).catch(() => null);
if (!profile) {
profile = await client.companion.createProfile({
userId,
requestBody: {
skinType: 'combination',
concerns: ['hydration'],
priceRange: { min: 10, max: 40 },
brands: [],
avoidances: ['fragrance'],
},
});
}
const recs = await client.companion.listRecommendations({
userId,
count: 5,
});
// Present to user, collect feedback
for (const rec of recs.recommendations) {
const userAction = await getUserFeedback(rec); // your UI layer
await client.companion.createInteractions({
requestBody: {
userId,
productId: rec.productId,
action: userAction, // 'RANK_UP', 'SKIP', etc.
},
});
if (userAction === 'PURCHASE_INTENT') {
const order = await client.companion.createOrders({
requestBody: {
userId,
productId: rec.productId,
shippingAddress: await getShippingAddress(userId),
email: await getUserEmail(userId),
},
});
console.log(`Order created: ${order.id}`);
}
}
await client.companion.createProfilePoints({
userId,
requestBody: { amount: 25, details: { reason: 'session_complete' } },
});
}