Checkout sessions
Create a TeePay hosted checkout page from your server and redirect your customer to it.
A checkout session is a TeePay-hosted payment page for one amount. You create it from your server,
redirect the customer to checkout_url, and TeePay collects the phone number, sends the prompt and
shows the result. You never handle payment details.
Create a session
curl https://api.teepay.me/v1/checkout-sessions \
-H "Authorization: Bearer $TEEPAY_API_KEY" \
-H "Idempotency-Key: order-1001" \
-H "Content-Type: application/json" \
-d '{
"title": "Order 1001",
"description": "2 x Coffee bag",
"amount_minor": "500000",
"currency": "TZS",
"expires_in": 86400,
"reference": "order-1001",
"metadata": { "cart": "c-77" }
}'Prop
Type
Unknown fields are rejected, so typos never pass silently.
Response 201
{
"id": "7c0e5d2a-1b3f-4c8e-9a6d-2f4b8e1c0d9a",
"object": "checkout_session",
"status": "open",
"title": "Order 1001",
"description": "2 x Coffee bag",
"currency": "TZS",
"amount_minor": "500000",
"checkout_url": "https://checkout.teepay.me/checkout/…",
"payment_profile_id": null,
"reference": "order-1001",
"metadata": { "cart": "c-77" },
"payment": null,
"environment": "live",
"created_at": "2026-10-28T10:00:00Z",
"expires_at": "2026-10-29T10:00:00Z"
}Redirect the customer to checkout_url.
Session status
status | Meaning |
|---|---|
open | Waiting for payment. A failed attempt keeps it open so the customer can retry. |
processing | A payment is in progress. Do not fulfil yet. |
paid | TeePay verified and ledger-posted the payment. payment.reference is set. Fulfil. |
expired | The page expired without a verified payment. |
unavailable | Status could not be read right now. Try again; never assume payment. |
Payment profiles
A payment profile is optional. Without one, the customer sees TeePay's standard checkout with your business name and mobile money, and returns to teepay.me afterwards. With a profile (or a default profile on your account) the page uses its branding, the customer returns to its return URL, and webhooks go to its webhook URL.
Return and webhook URLs are only configured in the dashboard, never per request, so a leaked key cannot redirect your customers or your webhooks elsewhere.
Confirm payment
The payment.completed webhook for a session has:
"source": { "type": "checkout_session", "id": "7c0e5d2a-1b3f-4c8e-9a6d-2f4b8e1c0d9a" }Or read the session:
Idempotency and limits
- Same
Idempotency-Keyand same body: the same session, withIdempotent-Replayed: true. - Same key, different body:
409 idempotency_conflict. - 30 sessions per account per minute, plus your account's collection limits.
- Errors specific to sessions:
invalid_amount,unsupported_currency,payment_profile_unavailable(an unknownpayment_profile_id).