Invoices & Recurring Billing
Issue itemised invoices with a hosted payment page, and switch on recurring renewals when a customer should be billed on a schedule.
https://api.paysasa.com/api/v1Authx-api-keyFormatJSONIssue itemized invoices and share a hosted payment page. For recurring plans with customers, products, and lifecycle events, use the Subscriptions API. You can also enable recurring on a template invoice to generate a new invoice each billing cycle.
Hosted payment page: Every invoice includes payment_url (e.g. https://app.paysasa.com/pay/invoice/inv_…). Customers can pay with M-PESA without signing in, or with wallet after sign-in.
Create an invoice. Requires business_id when using API key auth. Amounts are in minor units (KES 500 = 50000).
| Parameter | Type | Description |
|---|---|---|
business_id * | string | Your business ID (required with API key) |
line_items * | array | [{ description, quantity, unit_price }] — unit_price in minor units |
to | string | Payer email, phone, or pay handle (optional) |
due_date | string | ISO datetime when payment is due |
status | string | draft | pending (default pending) |
metadata | object | Your subscription/plan IDs — returned on GET |
external_reference | string | e.g. sub_12345 from your billing system |
recurring | object | Subscription template — see below |
// Monthly recurring invoice
const res = await fetch("https://api.paysasa.com/api/v1/invoices", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "apk_live_a1b2c3d4.7f9e0a...",
},
body: JSON.stringify({
business_id: "biz_xxxxxxxxxxxxxxxx",
to: "[email protected]",
currency: "KES",
status: "pending",
due_date: new Date(Date.now() + 7 * 86400000).toISOString(),
external_reference: "sub_plan_pro_001",
metadata: { subscription_id: "sub_plan_pro_001", plan: "pro" },
line_items: [
{
description: "Pro plan — monthly",
quantity: 1,
unit_price: 299900, // KES 2,999.00
},
],
recurring: {
enabled: true,
interval: "monthly",
every: 1,
end_at: null, // or ISO date to stop renewals
},
business_notes: "Thanks for subscribing to Pro.",
}),
});
const { invoice, payment_url } = await res.json();
// Share payment_url with your customer or store it in your app{
"invoice": {
"id": "inv_01hx...",
"invoice_number": "INV-ABC123-0001",
"amount": 299900,
"amount_paid": 0,
"amount_remaining": 299900,
"currency": "KES",
"status": "pending",
"payment_url": "https://app.paysasa.com/pay/invoice/inv_01hx...",
"recurring": { "enabled": true, "interval": "monthly", "every": 1, "next_run": "..." },
"metadata": { "subscription_id": "sub_plan_pro_001", "plan": "pro" }
},
"payment_url": "https://app.paysasa.com/pay/invoice/inv_01hx..."
}List invoices for your business. With API key auth, pass business_id (required).
curl "https://api.paysasa.com/api/v1/invoices?business_id=biz_xxxxxxxxxxxxxxxx&status=pending" \
-H "x-api-key: apk_live_a1b2c3d4.7f9e0a..."Retrieve invoice status (paid, pending, amount_remaining). Poll after customer pays on the hosted page.
Email or SMS the invoice link to the customer (includes payment_url).
await fetch("https://api.paysasa.com/api/v1/invoices/inv_01hx.../send", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "apk_live_a1b2c3d4.7f9e0a...",
},
body: JSON.stringify({
email: "[email protected]",
message: "Your subscription renewal is ready.",
}),
});No authentication — used by the hosted payment page. Returns amount, line items, and status only.
No authentication — start M-PESA payment from your own UI (returns hosted_url). The hosted page at /pay/invoice/:id uses this for the payer checkout experience.
const res = await fetch("https://api.paysasa.com/api/v1/invoices/inv_01hx.../checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
amount: 299900,
customer_phone: "254712345678",
}),
});
const { hosted_url } = await res.json();
window.location.href = hosted_url;Recurring invoices: Set recurring.enabled: true on a template invoice to generate a new invoice each cycle. For plan-based billing, use the Subscriptions API.