Hosted checkout
Hosted checkout is the path you’ll use for real customers. You create a session server-side;
Trile hosts a page where the customer verifies their phone, tops up their
wallet (eSewa/Khalti), and authorizes the subscription. You get back
a subscription.created event.
It exists because the customer needs to do things your server can’t do for them — verify a phone via OTP, complete KYC, and pay a provider. Hosted checkout wraps all of that.
Create a session (your server)
Section titled “Create a session (your server)”curl -s "$TRILE_API/v1/checkout/sessions" \ -H "x-api-key: $TRILE_KEY" -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "priceId": "price_01ARZ3NDEKTSV4RRFFQ69G5FAY", "successUrl": "https://yourapp.com/welcome", "cancelUrl": "https://yourapp.com/pricing" }'import { Configuration, CheckoutApi } from '@trilehq/sdk'
const checkout = new CheckoutApi(new Configuration({ apiKey: process.env.TRILE_KEY }))
const { data: session } = await checkout.createCheckoutSession({ createCheckoutSession: { priceId: 'price_01ARZ3NDEKTSV4RRFFQ69G5FAY', successUrl: 'https://yourapp.com/welcome', cancelUrl: 'https://yourapp.com/pricing', }, idempotencyKey: crypto.randomUUID(),})// session.publicUrl → redirect the customer hereResponse:
{ "success": true, "data": { "id": "cs_01ARZ3NDEKTSV4RRFFQ69G5FB9", "status": "open", "environment": "live", "priceId": "price_01ARZ3NDEKTSV4RRFFQ69G5FAY", "linkId": null, "successUrl": "https://yourapp.com/welcome", "cancelUrl": "https://yourapp.com/pricing", "expiresAt": "2026-07-15T04:15:00.000Z", "publicUrl": "https://checkout.trile.app/cs_01ARZ3NDEKTSV4RRFFQ69G5FB9?token=one-time-token", "publicToken": "one-time-token", "createdAt": "2026-07-15T03:45:00.000Z" }, "meta": { "requestId": "req_..." }}Redirect the customer to publicUrl — it embeds publicToken, a one-time credential for
this session. Sessions expire (expiresAt); an expired session can’t be completed, so mint a
fresh one per checkout attempt. Optionally pre-bind an existing customer with customerId.
What the customer does on the hosted page
Section titled “What the customer does on the hosted page”- Enters their phone; Trile sends an OTP (
/otp/send) and verifies it (/otp/verify), binding the customer to the session. - Sees the plan and the first-cycle amount vs. their current wallet balance (
/balance). - If short, tops up via eSewa/Khalti (
/topup→ provider →/topup/return, settled server-side). - Confirms — Trile authorizes and creates the subscription (
/complete), then redirects to yoursuccessUrl.
These /v1/checkout/sessions/:token/* endpoints power Trile’s own checkout UI. You normally
don’t call them yourself unless you’re building a fully custom checkout (see below).
Fulfil server-side, not on redirect
Section titled “Fulfil server-side, not on redirect”The redirect to successUrl is a UX convenience — don’t grant access based on it alone. A
user can close the tab, and redirects can be spoofed. Instead:
- Register a webhook for
subscription.created. - Grant access when you receive (and verify) that event.
- Optionally reconcile via the event log.
Static checkout links
Section titled “Static checkout links”For an evergreen “subscribe” button, a checkout link (clink_…) resolves to a fresh session
each visit via GET /v1/checkout/links/:linkId — handy to embed in marketing pages without
minting a session per visitor server-side. There’s no SDK method for links yet — call the
endpoint directly.
Building a fully custom checkout
Section titled “Building a fully custom checkout”If you must own the UI end to end, the per-session endpoints are public (anonymous + a
per-session bearer issued at OTP verify): balance, otp/send, otp/verify, topup,
topup/return, and complete. Treat the bearer as a short-lived session credential and follow
the same order as the hosted flow above. This is significantly more work — prefer the hosted
page unless you have a strong reason.