To create a Paystack payment request, POST to /paymentrequest with the customer code or email, amount, and due date. Paystack generates an invoice and sends it to the customer via email with a payment link. You can track the payment status via the API or wait for the charge.success webhook when the customer pays.
A payment request is an invoice that Paystack creates and sends on your behalf. It includes the amount, a description, your business name, and a payment link. The customer opens the email, clicks "Pay," and completes payment through the standard Paystack checkout.
Payment requests are different from Initialize Transaction in a key way: with Initialize Transaction, you control when the checkout opens. With payment requests, you send an invoice and wait for the customer to act on it. The customer decides when to pay.
This is the natural flow for service businesses, freelancers, agencies, and B2B billing: you do the work, you send an invoice, the client pays when they receive it.
async function createPaymentRequest(customerEmail, amountInKobo, description) {
var response = await fetch('https://api.paystack.co/paymentrequest', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
customer: customerEmail,
amount: amountInKobo,
description: description,
due_date: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
send_notification: true,
}),
});
var data = await response.json();
if (data.status) {
return {
requestCode: data.data.request_code,
id: data.data.id,
status: data.data.status,
};
}
throw new Error('Failed to create payment request: ' + data.message);
}
// Usage
var invoice = await createPaymentRequest(
'client@company.com',
7500000, // 75,000 NGN
'Website development - Phase 1 milestone payment'
);
console.log('Invoice created: ' + invoice.requestCode);
Key parameters:
customer: The customer's email address or Paystack customer codeamount: Amount in the smallest currency unit (kobo for NGN)description: What the invoice is for. This appears in the email and on the payment page.due_date: When the payment is due. Optional but useful for tracking overdue invoices.send_notification: Whether Paystack should email the invoice to the customer. Set to true for automatic delivery.line_items: Optional array of line items with name, amount, and quantity for detailed invoices.tax: Optional array of tax objects to add tax lines to the invoice.Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
For detailed invoices, break down the total into line items:
var response = await fetch('https://api.paystack.co/paymentrequest', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
customer: 'client@company.com',
amount: 8625000, // Total including tax
description: 'Web Development Services - July 2026',
line_items: [
{ name: 'Frontend Development', amount: 5000000, quantity: 1 },
{ name: 'API Integration', amount: 2500000, quantity: 1 },
],
tax: [
{ name: 'VAT', amount: 1125000 },
],
due_date: '2026-08-01T00:00:00.000Z',
send_notification: true,
}),
});
Line items and tax lines appear on the invoice the customer receives, giving them a clear breakdown of what they are paying for. This is important for B2B invoicing where clients need to match invoices to purchase orders or expense reports.
// Fetch a specific payment request
async function getPaymentRequest(requestCode) {
var response = await fetch(
'https://api.paystack.co/paymentrequest/' + requestCode,
{
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
}
);
var data = await response.json();
return data.data;
}
// List all payment requests with filters
async function listPaymentRequests(status) {
var url = 'https://api.paystack.co/paymentrequest';
if (status) {
url = url + '?status=' + status;
}
var response = await fetch(url, {
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
});
var data = await response.json();
return data.data;
}
// Find overdue invoices
async function findOverdueInvoices() {
var pending = await listPaymentRequests('pending');
var today = new Date();
return pending.filter(function(req) {
return req.due_date && new Date(req.due_date) < today;
});
}
Payment request statuses:
pending: Invoice sent, waiting for paymentsuccess: Customer has paidexpired: Past the due date without payment (if expiry is enabled)You can also listen for webhooks related to payment requests. When the customer pays through the invoice link, Paystack sends a charge.success event that you can use to update your records immediately.
Resend a notification
If the customer did not receive or lost the invoice email, you can resend the notification:
async function resendInvoice(requestCode) {
var response = await fetch(
'https://api.paystack.co/paymentrequest/notify/' + requestCode,
{
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
}
);
var data = await response.json();
return data;
}
Void a payment request
If the invoice was sent in error or the work was cancelled, void the request to deactivate the payment link:
async function voidPaymentRequest(requestCode) {
// Archive/void the request
var response = await fetch(
'https://api.paystack.co/paymentrequest/archive/' + requestCode,
{
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
}
);
return response.json();
}
Once voided, the payment link no longer works. The customer cannot pay a voided invoice. Make sure to communicate this to the customer if they have already received the email.
For recurring billing scenarios, you can automate the entire invoice lifecycle:
// Monthly invoicing job
async function generateMonthlyInvoices() {
var clients = await db.clients.findAll({
where: { billing_type: 'monthly', status: 'active' },
});
for (var i = 0; i < clients.length; i++) {
var client = clients[i];
var invoice = await createPaymentRequest(
client.email,
client.monthly_amount,
'Monthly retainer - ' + currentMonthName() + ' ' + currentYear()
);
await db.invoices.create({
client_id: client.id,
paystack_request_code: invoice.requestCode,
amount: client.monthly_amount,
status: 'sent',
month: currentMonth(),
});
}
}
// Overdue follow-up job (runs daily)
async function followUpOverdue() {
var overdue = await findOverdueInvoices();
for (var i = 0; i < overdue.length; i++) {
var invoice = overdue[i];
var daysPastDue = Math.floor(
(Date.now() - new Date(invoice.due_date).getTime()) / (24 * 60 * 60 * 1000)
);
if (daysPastDue === 1) {
await resendInvoice(invoice.request_code);
} else if (daysPastDue === 7) {
await sendCustomReminder(invoice, 'gentle');
} else if (daysPastDue === 14) {
await sendCustomReminder(invoice, 'urgent');
}
}
}
Both result in the customer paying through Paystack checkout. The difference is who controls the timing:
| Aspect | Payment Request | Initialize Transaction |
|---|---|---|
| Who triggers | You send, customer pays later | Customer clicks pay now |
| Delivery | Email with payment link | Redirect or popup on your site |
| Timing control | Customer decides when | Immediate |
| Best for | Invoicing, B2B, services | E-commerce, SaaS checkout |
| Line items | Supported | Not built-in (use metadata) |
| Due dates | Supported | Not applicable |
Use payment requests when you are billing clients after delivering a service. Use Initialize Transaction when the customer is on your site ready to buy now.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course