API Reference
Checkout
Create checkout sessions and poll their status.
Create a checkout session
POST /api/jamidev/checkoutPublic (used by hosted checkout pages) or authenticated with a bearer token.
With the SDK:
const checkout = await jami.createCheckout({
productId: '665f1c2ab8d3a2f4e1a9c222',
customer: { email: 'buyer@example.com', phone: '0912345678' },
gateway: 'telebirr',
});Request body
{
"productId": "665f1c2ab8d3a2f4e1a9c222",
"amount": 15000,
"customer": {
"email": "buyer@example.com",
"phone": "0912345678",
"name": "Abebe B.",
"externalId": "user_42"
},
"collectedFields": { "<checkoutFieldId>": "value" },
"gateway": "telebirr",
"checkoutLink": "cl_Q4sp2Xr5ybOi",
"successUrl": "https://yoursite.com/thanks?c={CHECKOUT_ID}"
}| Field | Notes |
|---|---|
productId | Required. Must be a published product. Accepts either the public p_… product id (shown in your dashboard) or the raw ObjectId. |
amount | Only for pwyw products — integer minor units, ≥ the product minimum. Ignored for fixed (server uses the configured price) and free. |
customer.email | Required. Upserts the customer per organization. |
customer.phone | Required for paid products — the payment push goes to this number. Ethiopian formats accepted (09…, 2519…, 9…). |
collectedFields | Values keyed by checkout-field id; required fields are enforced. |
gateway | telebirr | mpesa | cbe (default telebirr). Anything else is rejected. |
checkoutLink | Optional cl_… token — merges the link's metadata into the order and applies its success URL. Stale tokens are ignored, never an error. |
successUrl | Optional. Honored for token callers; anonymous calls only when the URL's host matches one of the org's registered redirect URLs. |
Responses
Free product — the order is created immediately:
{ "sessionId": "665f…", "orderId": "665f…" }Paid, production — direct (phone-push) payment; keep the buyer on your page and poll:
{ "sessionId": "665f…", "checkoutUrl": null, "mode": "direct" }Paid, sandbox — redirect the buyer to the simulator:
{ "sessionId": "665f…", "checkoutUrl": "https://jami.bio/jamidev/simulate/665f…", "mode": "redirect" }Sessions expire after 30 minutes if unpaid.
Poll session status
GET /api/jamidev/checkout/{sessionId}Public. Safe to poll every ~3 seconds — or let the SDK poll for you:
const status = await jami.waitForCheckout(sessionId); // resolves on completed/expired{
"sessionId": "665f…",
"status": "open",
"expiresAt": "2026-07-11T10:00:00.000Z",
"amount": 10000,
"currency": "ETB",
"orderId": null,
"orderStatus": null
}status:open→completedorexpired.- When
orderIdis non-null, the payment succeeded and benefits are granted. - The endpoint actively reconciles: while a session is open it checks the payment provider directly (throttled), so polling resolves even if a provider webhook is delayed. Expired sessions are finalized automatically.
List orders
See Orders.
