End-to-end checklist
Run through every item. Don't ship until each one passes.
Catalog visibility
-
GET /agents/by-slug/<slug>returns 200 with full DTO. - Marketplace homepage shows the agent (if
featured: true) or browse grid shows it (if not). - Detail page
/agent/<slug>renders without console errors. - Capabilities, steps, changelog, photos all appear.
- Tag/category color theme looks correct.
- Mobile viewport (375px) renders cleanly.
Pricing
- All pricing tiers show on the detail page.
- Recommended tier is visually highlighted.
- Free tier "Get started" goes to dashboard subscription without Stripe.
- Paid tier "Subscribe" redirects to Stripe Checkout.
- On successful checkout, you land back at
returnUrl(or/dashboardif none). - Dashboard
/dashboardshows the new subscription withstatus: 'active'.
OAuth (Track A)
- Visiting the agent's frontend redirects unauthenticated users to the marketplace login.
- After login + consent, the user is redirected back with a session cookie set on the agent backend.
-
GET <agent>/mereturns{ id, email, name, marketplaceRole }. - Signing out on the marketplace does not automatically sign the user out of the agent (separate sessions — expected).
- Refresh tokens work: leave the session idle for >10 hours, return, no re-login needed.
Subscription resolution
- Agent backend can call
GET /me/subscriptions/<agentId>server-to-server withAuthorization: Bearer <access_token>. - An unsubscribed user receives 404.
- A subscribed user receives the full subscription DTO including the embedded tier.
- After upgrading on the marketplace, the agent reflects the new tier within one token refresh cycle (or sooner if the agent doesn't cache the hint).
Quota arithmetic
- Define one metric of each kind (
fixed+rolling) for testing. - On the free tier,
consumereturns 200 until quota is hit, then 429 witherror.code === "QUOTA_EXCEEDED"anderror.details.remaining === 0. - On the paid tier, the consume cap matches the tier's
quotaLimit. -
releaseon a fixed metric decrementsusedand is reflected inGET /me/agents/:agentId/usage/<slug>. -
releaseon a rolling metric returns 422. - Same
clientReqIdused twice onconsumedoesn't double-charge. - A 0-quota gate (boolean feature) correctly blocks the agent's UI for that tier.
Stripe lifecycle
-
checkout.session.completedwebhook fires →user_agent_subscriptionsrow is upserted withstatus: 'active'. -
customer.subscription.updated(after a plan change) updates the local row. -
customer.subscription.deletedflips status tocanceled. -
invoice.payment_failedflips status topast_due. - All events appear in
stripe_eventswithprocessedAtset. - Reprocessing the same event ID is a no-op (idempotency).
Admin
-
/admin/agentslists the agent. - Edit form persists updates.
-
/admin/interactionsshows a row for each demo request, signup, waitlist hit, subscription creation. - Adding a notification recipient via
/admin/interactions/recipientsactually receives emails on next interaction.
RBAC
- An
org_membercannot call any of the admin endpoints (/admin/*, agent CRUD, tier CRUD) — receives 403 witherror.code === "FORBIDDEN". - An
org_admincannot create or edit agents — receives 403. - A
platform_admincan do all of the above.
Audit log
-
POSTHOG_API_KEYis set in production. Requests appear in the PostHog project within ~30s (the batch interval). - Failed requests log a row with
result: 'failure'and anerrorCode.
Security
- OAuth
redirectUrisallow-list contains only legitimate origins. -
FRONTEND_URLenv var on the backend matches the actual marketplace UI origin. - Stripe webhook signature verification is succeeding (no 400s in webhook logs).
- Cookies are
HttpOnly,Secure,SameSite=Nonein production. - No secrets are leaked in audit log entries.
Coming-soon mode (optional)
If launching as coming_soon:
- Agent card renders the "Coming soon" badge.
- Click opens the
NotifyDialog. - Email submit creates an
agent_waitlistrow and aninteractionsrow withtype: 'notify_request'. - Notification recipients receive an email.
Done
When every checkbox passes, move on to Publishing & approval.