Skip to main content

Overview

Podium provides a built-in subscription system for companion agents, enabling developers to monetize their AI experiences with freemium gating, usage tracking, and Stripe-powered billing. The system handles the full lifecycle: free users hit configurable limits, see upgrade prompts, check out via Stripe, and immediately unlock premium capabilities. All subscription state is managed server-side — your agent just checks the gate.

Subscription Tiers

Every companion user starts on FREE and moves through a standard billing lifecycle:
TierDescription
FREEDefault tier with usage limits
TRIALINGTrial period (configurable, default 14 days)
ACTIVEPaid subscriber
PAST_DUEPayment failed, grace period active
CANCELEDSubscription ended

Usage Gating

Free-tier users have configurable monthly limits. These defaults can be adjusted per organization:
LimitDefaultDescription
Monthly messages25Total agent messages per billing period
Monthly recommendations5Personalized recommendation requests
Soft nudge threshold20 messages”You’re almost at your limit” prompt
Halfway milestone50% of message limitProgress notification

Gate Check

Before processing a user action, call the status endpoint and evaluate the gate result:
type GateResult =
  | { allowed: true; nearLimit: boolean; milestone?: string }
  | { allowed: false; reason: 'messages' | 'recs' }
When allowed is true, the action proceeds normally. Use nearLimit to show soft upgrade prompts before the user hits the wall. When allowed is false, reason tells you which limit was hit so you can show the appropriate upgrade CTA.
Subscribers (ACTIVE and TRIALING tiers) bypass all usage gates. The gate check always returns allowed: true for paying users.

Memory Gating

Memory extraction runs for all users regardless of tier. Free users still have their behavioral patterns, preferences, and conversation history analyzed and stored. The difference is in how that intelligence is used:
  • Free users — receive standard recommendations based on their intent profile and interactions
  • Subscribers — receive personalized, memory-aware recommendations where the agent’s accumulated understanding of the user is loaded into every conversation turn
This means upgrading immediately unlocks all accumulated intelligence — there’s no “cold start” when a free user converts to paid. The agent already knows them.
This design creates a powerful conversion mechanic: the longer a free user engages, the more valuable upgrading becomes, because the agent has been building their memory the entire time.

API Endpoints

All subscription endpoints are prefixed with /api/v1/companion/subscription.

Create Checkout Session

curl -X POST https://api.podium.build/api/v1/companion/subscription/checkout \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "clxyz1234567890",
    "email": "user@example.com"
  }'
Response:
{
  "url": "https://checkout.stripe.com/c/pay_cs_..."
}
Redirect the user to the returned url to complete payment. On success, Stripe sends a webhook that automatically upgrades the user’s tier to ACTIVE.

Customer Portal

curl -X POST https://api.podium.build/api/v1/companion/subscription/portal \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "clxyz1234567890"
  }'
Response:
{
  "url": "https://billing.stripe.com/p/session/..."
}
Opens the Stripe customer portal where the user can update payment methods, view invoices, and cancel their subscription.

Subscription Status

curl https://api.podium.build/api/v1/companion/subscription/{userId}/status \
  -H "Authorization: Bearer YOUR_API_KEY"
Response:
{
  "status": "ACTIVE",
  "currentPeriodEnd": 1711929600,
  "trialEndsAt": null,
  "isSubscribed": true,
  "usage": {
    "messageCount": 12,
    "recCount": 3,
    "messageLimit": 25,
    "recLimit": 5
  }
}
FieldTypeDescription
statusstringCurrent tier: FREE, TRIALING, ACTIVE, PAST_DUE, or CANCELED
currentPeriodEndnumberUnix timestamp of current billing period end
trialEndsAtnumber?Unix timestamp of trial expiry (null if not trialing)
isSubscribedbooleantrue if user has an active or trialing subscription
usage.messageCountnumberMessages sent this period
usage.recCountnumberRecommendations requested this period
usage.messageLimitnumberMessage limit for current tier
usage.recLimitnumberRecommendation limit for current tier

Webhook

POST /api/v1/webhooks/stripe/subscription
The platform automatically processes these Stripe webhook events:
EventEffect
checkout.session.completedUser tier set to ACTIVE (or TRIALING if trial configured)
customer.subscription.updatedTier and period end synced
customer.subscription.deletedUser tier set to CANCELED
invoice.payment_failedUser tier set to PAST_DUE
You don’t need to handle these webhooks yourself — the platform processes them automatically. Only configure a webhook endpoint in Stripe if you need to react to subscription changes in your own application logic.

Endpoint Summary

MethodPathDescription
POST/companion/subscription/checkoutCreate a Stripe checkout session
POST/companion/subscription/portalOpen Stripe customer portal
GET/companion/subscription/{userId}/statusGet subscription status and usage

Integration Example

Here’s a typical flow for gating a message behind the subscription check:
const apiKey = process.env.PODIUM_API_KEY

// 1. Check subscription status and usage
const status = await fetch(
  `https://api.podium.build/api/v1/companion/subscription/${userId}/status`,
  { headers: { Authorization: `Bearer ${apiKey}` } }
).then(r => r.json())

if (status.isSubscribed) {
  // Subscriber — proceed with full memory-aware experience
  await sendAgentMessage(userId, message)
} else if (status.usage.messageCount < status.usage.messageLimit) {
  // Free user within limits — proceed with standard experience
  await sendAgentMessage(userId, message)

  if (status.usage.messageCount >= 20) {
    // Near limit — show soft nudge
    showUpgradePrompt('You\'re almost at your free message limit')
  }
} else {
  // Free user at limit — trigger upgrade flow
  const checkout = await fetch(
    'https://api.podium.build/api/v1/companion/subscription/checkout',
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ userId, email: userEmail }),
    }
  ).then(r => r.json())

  // Redirect user to checkout.url
  redirectToCheckout(checkout.url)
}

Best Practices

  • Check status before every gated action — don’t cache subscription state client-side for longer than a single session
  • Use soft nudges before hard gates — the nearLimit flag and milestone notifications reduce churn by warning users before they hit the wall
  • Surface the value of upgrading — when a free user hits a gate, remind them that their accumulated intelligence is waiting: “Sage has been learning your preferences — upgrade to unlock personalized recommendations”
  • Handle PAST_DUE gracefully — consider a short grace period before downgrading the experience, as many payment failures resolve automatically