Bonaventure OgetoBy Bonaventure Ogeto|

Paystack Payment Requests and Invoices via API

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.

What Payment Requests Are

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.

Creating a Payment Request

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 code
  • amount: 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.

Adding Line Items and Tax

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.

Tracking Payment Status

// 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 payment
  • success: Customer has paid
  • expired: 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.

Managing Payment Requests

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.

Automated Invoicing Workflow

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');
    }
  }
}

Payment Requests vs Initialize Transaction

Both result in the customer paying through Paystack checkout. The difference is who controls the timing:

AspectPayment RequestInitialize Transaction
Who triggersYou send, customer pays laterCustomer clicks pay now
DeliveryEmail with payment linkRedirect or popup on your site
Timing controlCustomer decides whenImmediate
Best forInvoicing, B2B, servicesE-commerce, SaaS checkout
Line itemsSupportedNot built-in (use metadata)
Due datesSupportedNot 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.

Stay Up to Date

Paystack updates their invoicing features periodically. We keep these guides current when things change.

Sign up for the McTaba newsletter to get notified about new Paystack features and guides.

Key Takeaways

  • Payment requests are Paystack-generated invoices with built-in payment links. The customer receives an email and can pay with one click.
  • Create a payment request by POSTing to /paymentrequest with the customer identifier, amount, description, and optional due date.
  • Payment requests can be one-time or recurring. For recurring invoices, use Paystack subscriptions instead of manually creating repeated requests.
  • You can archive, void, or resend payment requests via the API. Voiding a request makes the payment link inactive.
  • Payment request status progresses from "pending" to "success" when paid, or stays pending if unpaid. Track via API polling or webhooks.
  • Payment requests are ideal for service businesses, freelancers, and B2B billing where you send invoices to clients rather than collecting payment at checkout.

Frequently Asked Questions

Can the customer pay a payment request with any payment channel?
Yes. When the customer clicks the payment link in the invoice email, they are taken to the standard Paystack checkout page where all available payment channels are shown. They can pay by card, bank transfer, USSD, or any other channel enabled on your account.
Can I create a payment request for a customer who is not in my Paystack customer list?
Yes. You can use the customer email address directly. If the customer does not exist in your Paystack records, Paystack creates a customer record automatically.
What happens when a payment request passes its due date?
The payment link typically remains active past the due date unless you have configured it to expire. The due date is primarily informational, helping you track overdue invoices. You can void the request manually if you want to deactivate the link after the due date.
Can I edit a payment request after creating it?
Payment request update capabilities may be limited. If you need to change the amount or details, it is usually easier to void the original request and create a new one with the correct information.
Is there a limit to how many payment requests I can send?
Paystack does not publish a strict limit on payment requests, but excessive creation in a short period may trigger rate limiting. For high-volume invoicing, spread your requests over time and monitor API responses for rate limit errors.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon