Back to docs

Agent hosting

PureVPS cross-sell + product catalog

Agent Hosting (PureVPS Cross-Sell)

PrivateRouter sells AI inference + chat. PureVPS sells the persistent compute environments to run an agent. The /agent-hosting surface glues the two together so customers can buy both from one place — even though the actual VPS provisioning lives on PureVPS, not here.


What's in the product

Public surface

  • /agent-hosting — index page with hero, FAQ, and a 3-col grid of all status='active' products
  • /agent-hosting/[slug] — per-product detail page; the primary CTA links to purevps_product_url (opens in a new tab)
  • Homepage band — shows the 3 is_featured=true products
  • Footer link — present on every page

Member surface

  • /dashboard — a dismissable upsell card sits between the balance card and the activity chart
  • /billing/success — after a topup completes, a dismissable banner invites the user to view VPS plans

Both dismissals are local-only (localStorage); we don't track the state server-side because it's purely a UX nudge.

Admin surface

  • /admin/agent-hosting — full CRUD, status filter, audit-logged
  • Sidebar link sits between "Billing" and "GPU Nodes"

Backend

Schema — agent_hosting_products

ColumnTypeNotes
idUUID PK
slugvarchar(80) UNIQUEURL-safe identifier
namevarchar(120)display name
taglinevarchar(200)one-line pitch
descriptiontextlong-form copy
image_urlvarchar(500) NULLoptional
monthly_price_usdnumeric(10,2)dollars
included_credits_usdnumeric(10,2) NULLNULL = no credits bundled
purevps_product_urlvarchar(500)deep-link to PureVPS checkout
statusvarchar(20)active | draft | archived
is_featuredbooleanshows on homepage band
sort_orderintegerlower = earlier
created_at / updated_attimestamptz

Sort precedence for public listing: is_featured DESC, sort_order ASC, name ASC

Endpoints

MethodPathAuthNotes
GET/api/agent-hosting/productspublicactive-only
GET/api/admin/agent-hosting/productsadminincludes drafts
POST/api/admin/agent-hosting/productsadmin409 on slug clash
GET/api/admin/agent-hosting/products/{id}admin
PATCH/api/admin/agent-hosting/products/{id}adminpartial update
DELETE/api/admin/agent-hosting/products/{id}adminhard delete

All mutating admin endpoints write an admin_audit_logs row.

Decimal serialisation

The API serialises Decimal columns as JSON strings ("49.00"), not numbers — match this on the frontend types.


Seeded catalog (idempotent on container boot)

SlugNameMonthlyCreditsFeatured
hermes-vpsHermes Agent VPS$49$10
openclaw-vpsOpenClaw VPS$59$15
openhands-vpsOpenHands VPS$59$15
coding-agent-vpsCoding Agent VPS$39$5
browser-agent-vpsBrowser Agent VPS$49$10

Seeding lives in apps/api/scripts/seed.py — UPSERT by slug, runs as part of the api container's boot command.

purevps_product_url values are placeholders (https://purevps.com/order?product=<slug>). When PureVPS confirms the real deep-link shape, update via the admin UI or edit the seeder.


Adding a new product

Through the admin UI (recommended)

  1. Visit /admin/agent-hosting
  2. Click New product
  3. Fill in slug (lowercase, hyphens, must be unique), name, tagline, description, monthly price, PureVPS URL
  4. Pick status: active to publish immediately, draft to stage
  5. Save — the row appears in /agent-hosting within ~5 minutes (the public page revalidates every 300s)

Through the seeder (for catalog defaults)

Add an entry to the seed list in apps/api/scripts/seed.py. The seeder upserts by slug so re-running is idempotent. Restart the api container to apply.


What's not integrated yet (M7 explicit non-goals)

  1. Real PureVPS provisioning API. The CTA is a deep-link, not a programmatic provisioning call. When a customer clicks "Deploy on PureVPS", they leave PrivateRouter and complete the order on PureVPS's own checkout.
  2. Bundle billing. The page copy may imply a discount when buying API credits + VPS together, but there's no enforcement. Adding a real bundle discount means either a discount-code flow on PrivateRouter's Stripe topup or shared cart with PureVPS — both are separate projects.
  3. Provisioning templates. "Preconfigured for Hermes / OpenClaw / OpenHands" is a marketing claim; the actual VPS templates live in the PureVPS catalog, not in this repo.
  4. Cross-product identity. A user who buys a Hermes VPS through PureVPS doesn't auto-sign-in to PrivateRouter. They get an API key the normal way.

These are all M7+ follow-ups. The current scope is "make the cross-sell discoverable and the catalog editable".


Smoke test

./infra/scripts/smoke-m7.sh --keep

15 checks, runs in seconds. Covers public listing, public page render, per-product render + 404, full admin CRUD round-trip including audit log writes, 403 for non-admin, homepage band, dashboard/success render.


Open ops questions

  • Real PureVPS deep-link URL shape — currently https://purevps.com/order?product=<slug>. Confirm with PureVPS.
  • Should the dismissal state move server-side? localStorage is fine for MVP but means the upsell re-appears if a user clears their browser. Low priority.
  • Bundle pricing strategy — if we want "save 15% when bundled" to be real, decide which side runs the discount.