Skip to content

Invoice

Create programmable invoices via API and let your customers pay through the standard checkout gateway. Invoices support partial payments, tax calculations, line items, and automated status tracking.

Required permission: invoices

How it works

  1. Create an invoice via API with customer details, line items, and due date
  2. Send the invoice (transitions from draft to sent)
  3. Share the payment_url with your customer
  4. Customer opens the payment URL and pays via bank transfer or PayID
  5. Invoice status updates automatically: partialpaid
  6. You receive webhook events for each status change

Create Invoice

POST /v1/checkout/invoice

Creates a new invoice in draft status. You must send the invoice before customers can pay.

Authentication

Requires a secret key (sk_*).

Request body

ParameterTypeRequiredDescription
customer_namestringYesCustomer's full name (max 255 characters)
customer_emailstringYesCustomer's email address
customer_addressstringNoCustomer's street address
customer_citystringNoCustomer's city (max 100)
customer_statestringNoCustomer's state/region (max 100)
customer_countrystringNoCustomer's country (max 100)
line_itemsarrayYesArray of line item objects (at least one)
line_items[].descriptionstringYesDescription of the item (max 500)
line_items[].quantitynumberYesQuantity (supports decimals, min 0.01)
line_items[].unit_priceintegerYesUnit price in kobo
tax_ratenumberNoTax rate as percentage (0–100, e.g., 7.5 for 7.5%). Default: 0
due_datestringYesDue date in ISO 8601 format
invoice_numberstringNoCustom invoice number (max 64). Auto-generated if omitted (e.g., INV-202603-001)
referencestringNoYour unique reference for reconciliation (max 255)
notestringNoNote to display on the invoice payment page (max 2000)
metadataobjectNoCustom key-value data stored with the invoice

Example request

bash
curl -X POST https://api.zevpaycheckout.com/v1/checkout/invoice \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_test_your_secret_key" \
  -d '{
    "customer_name": "Chidi Okonkwo",
    "customer_email": "chidi@example.com",
    "customer_address": "15 Admiralty Way, Lekki Phase 1",
    "customer_city": "Lagos",
    "customer_state": "Lagos",
    "customer_country": "Nigeria",
    "line_items": [
      {
        "description": "Website Development",
        "quantity": 1,
        "unit_price": 35000000
      },
      {
        "description": "Monthly Hosting (12 months)",
        "quantity": 12,
        "unit_price": 500000
      }
    ],
    "tax_rate": 7.5,
    "due_date": "2026-03-31T00:00:00Z",
    "reference": "PROJECT-2026-001",
    "note": "Thank you for your business!",
    "metadata": {
      "project_id": "PRJ-123"
    }
  }'
javascript
const response = await fetch(
  "https://api.zevpaycheckout.com/v1/checkout/invoice",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer sk_test_your_secret_key",
    },
    body: JSON.stringify({
      customer_name: "Chidi Okonkwo",
      customer_email: "chidi@example.com",
      customer_address: "15 Admiralty Way, Lekki Phase 1",
      customer_city: "Lagos",
      customer_state: "Lagos",
      customer_country: "Nigeria",
      line_items: [
        { description: "Website Development", quantity: 1, unit_price: 35000000 },
        { description: "Monthly Hosting (12 months)", quantity: 12, unit_price: 500000 },
      ],
      tax_rate: 7.5,
      due_date: "2026-03-31T00:00:00Z",
      reference: "PROJECT-2026-001",
      note: "Thank you for your business!",
      metadata: { project_id: "PRJ-123" },
    }),
  }
);
const data = await response.json();
python
import requests

response = requests.post(
    "https://api.zevpaycheckout.com/v1/checkout/invoice",
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer sk_test_your_secret_key",
    },
    json={
        "customer_name": "Chidi Okonkwo",
        "customer_email": "chidi@example.com",
        "customer_address": "15 Admiralty Way, Lekki Phase 1",
        "customer_city": "Lagos",
        "customer_state": "Lagos",
        "customer_country": "Nigeria",
        "line_items": [
            {"description": "Website Development", "quantity": 1, "unit_price": 35000000},
            {"description": "Monthly Hosting (12 months)", "quantity": 12, "unit_price": 500000},
        ],
        "tax_rate": 7.5,
        "due_date": "2026-03-31T00:00:00Z",
        "reference": "PROJECT-2026-001",
        "note": "Thank you for your business!",
        "metadata": {"project_id": "PRJ-123"},
    },
)
data = response.json()

Response

json
{
  "success": true,
  "data": {
    "public_id": "abc123def456ghi78",
    "invoice_number": "INV-202603-001",
    "status": "draft",
    "customer_name": "Chidi Okonkwo",
    "customer_email": "chidi@example.com",
    "customer_address": "15 Admiralty Way, Lekki Phase 1",
    "customer_city": "Lagos",
    "customer_state": "Lagos",
    "customer_country": "Nigeria",
    "subtotal": 41000000,
    "tax_rate": 7.5,
    "tax_amount": 3075000,
    "total": 44075000,
    "amount_paid": 0,
    "currency": "NGN",
    "due_date": "2026-03-31T00:00:00.000Z",
    "issued_at": null,
    "paid_at": null,
    "cancelled_at": null,
    "merchant_reference": "PROJECT-2026-001",
    "metadata": { "project_id": "PRJ-123" },
    "note": "Thank you for your business!",
    "payment_url": "https://invoice.zevpaycheckout.com/pay/abc123def456ghi78?token=...",
    "created_at": "2026-03-08T10:00:00.000Z"
  }
}

Response fields

FieldTypeDescription
public_idstringUnique public identifier for the invoice
invoice_numberstringHuman-readable invoice number (auto-generated or custom)
statusstringCurrent status: draft, sent, partial, paid, overdue, cancelled
customer_namestringCustomer's full name
customer_emailstringCustomer's email address
customer_addressstring | nullCustomer's street address
customer_citystring | nullCustomer's city
customer_statestring | nullCustomer's state/region
customer_countrystring | nullCustomer's country
subtotalintegerSubtotal in kobo (sum of line items, before tax)
tax_ratenumberTax rate as a percentage (e.g., 7.5)
tax_amountintegerTax amount in kobo
totalintegerTotal amount in kobo (subtotal + tax)
amount_paidintegerCumulative amount paid so far, in kobo
currencystringCurrency code. Always "NGN"
due_datestringDue date in ISO 8601 format
issued_atstring | nullTimestamp when invoice was sent (ISO 8601). null for drafts
paid_atstring | nullTimestamp when invoice was fully paid. null until paid
cancelled_atstring | nullTimestamp when invoice was cancelled. null unless cancelled
merchant_referencestring | nullYour custom reference, if provided
metadataobject | nullYour custom key-value data, if provided
notestring | nullInvoice note, if provided
payment_urlstringURL for the customer to view and pay the invoice
created_atstringInvoice creation timestamp (ISO 8601)

Amounts

All monetary amounts in the API are in kobo (1 NGN = 100 kobo). For example, 35000000 kobo = ₦350,000.00.

Payment URL

Store the public_id (and optionally the payment_url) in your database. The payment_url resolves to the payment page when the invoice is payable, or shows a receipt when paid.


Update Invoice

PATCH /v1/checkout/invoice/:publicId

Updates a draft invoice. Only invoices with status: "draft" can be updated — once sent, an invoice cannot be modified.

Authentication

Requires a secret key (sk_*).

Path parameters

ParameterTypeDescription
publicIdstringThe invoice's public_id

Request body

All fields are optional. Only include the fields you want to change.

ParameterTypeDescription
customer_namestringCustomer's full name
customer_emailstringCustomer's email address
customer_addressstringCustomer's street address
customer_citystringCustomer's city
customer_statestringCustomer's state/region
customer_countrystringCustomer's country
line_itemsarrayReplacement line items (replaces all existing items)
line_items[].descriptionstringItem description
line_items[].quantitynumberQuantity
line_items[].unit_priceintegerUnit price in kobo
tax_ratenumberTax rate as percentage (0–100)
due_datestringDue date in ISO 8601 format
invoice_numberstringCustom invoice number
notestringInvoice note
metadataobjectCustom key-value data

Line items replacement

When you include line_items in an update, all existing line items are replaced — not merged. Always send the complete list of line items you want on the invoice.

Example request

bash
curl -X PATCH https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_test_your_secret_key" \
  -d '{
    "note": "Updated: payment due by end of month",
    "due_date": "2026-04-15T00:00:00Z"
  }'
javascript
const response = await fetch(
  "https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78",
  {
    method: "PATCH",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer sk_test_your_secret_key",
    },
    body: JSON.stringify({
      note: "Updated: payment due by end of month",
      due_date: "2026-04-15T00:00:00Z",
    }),
  }
);
const data = await response.json();
python
import requests

response = requests.patch(
    "https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78",
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer sk_test_your_secret_key",
    },
    json={
        "note": "Updated: payment due by end of month",
        "due_date": "2026-04-15T00:00:00Z",
    },
)
data = response.json()

Response

Returns the updated invoice object (same shape as Create Invoice response).

Errors

StatusMessageCause
404Invoice not foundInvoice does not exist or does not belong to your account
400Only draft invoices can be editedInvoice has already been sent, paid, or cancelled
400Invoice total must be greater than zeroUpdated line items result in zero or negative total

List Invoices

GET /v1/checkout/invoice

Returns a paginated list of your invoices, ordered by creation date (newest first).

Authentication

Requires a secret key (sk_*).

Query parameters

ParameterTypeRequiredDescription
pageintegerNoPage number (min 1). Default: 1
page_sizeintegerNoItems per page (1–100). Default: 20
statusstringNoFilter by status: draft, sent, partial, paid, overdue, cancelled
customer_emailstringNoFilter by exact customer email address
searchstringNoSearch by invoice number, customer name, or customer email (partial match)

customer_email vs search

customer_email performs an exact match on the email field. search performs a partial match (contains) across invoice number, customer name, and email. Use customer_email when you know the exact email; use search for flexible lookups.

Example request

bash
# List all sent invoices
curl "https://api.zevpaycheckout.com/v1/checkout/invoice?status=sent&page=1&page_size=20" \
  -H "Authorization: Bearer sk_test_your_secret_key"

# Filter by customer email
curl "https://api.zevpaycheckout.com/v1/checkout/invoice?customer_email=chidi@example.com" \
  -H "Authorization: Bearer sk_test_your_secret_key"
javascript
// List all sent invoices
const response = await fetch(
  "https://api.zevpaycheckout.com/v1/checkout/invoice?status=sent&page=1",
  {
    headers: { "Authorization": "Bearer sk_test_your_secret_key" },
  }
);
const data = await response.json();

// Filter by customer email
const byEmail = await fetch(
  "https://api.zevpaycheckout.com/v1/checkout/invoice?customer_email=chidi@example.com",
  {
    headers: { "Authorization": "Bearer sk_test_your_secret_key" },
  }
);
python
import requests

# List all sent invoices
response = requests.get(
    "https://api.zevpaycheckout.com/v1/checkout/invoice",
    headers={"Authorization": "Bearer sk_test_your_secret_key"},
    params={"status": "sent", "page": 1, "page_size": 20},
)
data = response.json()

# Filter by customer email
by_email = requests.get(
    "https://api.zevpaycheckout.com/v1/checkout/invoice",
    headers={"Authorization": "Bearer sk_test_your_secret_key"},
    params={"customer_email": "chidi@example.com"},
)

Response

json
{
  "success": true,
  "data": {
    "items": [
      {
        "public_id": "abc123def456ghi78",
        "invoice_number": "INV-202603-001",
        "status": "sent",
        "customer_name": "Chidi Okonkwo",
        "customer_email": "chidi@example.com",
        "customer_address": "15 Admiralty Way, Lekki Phase 1",
        "customer_city": "Lagos",
        "customer_state": "Lagos",
        "customer_country": "Nigeria",
        "subtotal": 41000000,
        "tax_rate": 7.5,
        "tax_amount": 3075000,
        "total": 44075000,
        "amount_paid": 0,
        "currency": "NGN",
        "due_date": "2026-03-31T00:00:00.000Z",
        "issued_at": "2026-03-08T10:05:00.000Z",
        "paid_at": null,
        "cancelled_at": null,
        "merchant_reference": "PROJECT-2026-001",
        "metadata": { "project_id": "PRJ-123" },
        "note": "Thank you for your business!",
        "payment_url": "https://invoice.zevpaycheckout.com/pay/abc123def456ghi78?token=...",
        "created_at": "2026-03-08T10:00:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "page_size": 20,
    "total_pages": 1
  }
}

Pagination fields

FieldTypeDescription
itemsarrayArray of invoice objects (same fields as Create response)
totalintegerTotal number of invoices matching the filters
pageintegerCurrent page number
page_sizeintegerNumber of items per page
total_pagesintegerTotal number of pages

Get Invoice

GET /v1/checkout/invoice/:publicId

Returns a single invoice with its line items and payment history.

Authentication

Requires a secret key (sk_*).

Path parameters

ParameterTypeDescription
publicIdstringThe invoice's public_id

Example request

bash
curl https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78 \
  -H "Authorization: Bearer sk_test_your_secret_key"
javascript
const response = await fetch(
  "https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78",
  {
    headers: { "Authorization": "Bearer sk_test_your_secret_key" },
  }
);
const data = await response.json();
python
import requests

response = requests.get(
    "https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78",
    headers={"Authorization": "Bearer sk_test_your_secret_key"},
)
data = response.json()

Response

json
{
  "success": true,
  "data": {
    "public_id": "abc123def456ghi78",
    "invoice_number": "INV-202603-001",
    "status": "partial",
    "customer_name": "Chidi Okonkwo",
    "customer_email": "chidi@example.com",
    "customer_address": "15 Admiralty Way, Lekki Phase 1",
    "customer_city": "Lagos",
    "customer_state": "Lagos",
    "customer_country": "Nigeria",
    "subtotal": 41000000,
    "tax_rate": 7.5,
    "tax_amount": 3075000,
    "total": 44075000,
    "amount_paid": 20000000,
    "currency": "NGN",
    "due_date": "2026-03-31T00:00:00.000Z",
    "issued_at": "2026-03-08T10:05:00.000Z",
    "paid_at": null,
    "cancelled_at": null,
    "merchant_reference": "PROJECT-2026-001",
    "metadata": { "project_id": "PRJ-123" },
    "note": "Thank you for your business!",
    "payment_url": "https://invoice.zevpaycheckout.com/pay/abc123def456ghi78?token=...",
    "created_at": "2026-03-08T10:00:00.000Z",
    "line_items": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440001",
        "description": "Website Development",
        "quantity": 1,
        "unit_price": 35000000
      },
      {
        "id": "550e8400-e29b-41d4-a716-446655440002",
        "description": "Monthly Hosting (12 months)",
        "quantity": 12,
        "unit_price": 500000
      }
    ],
    "payments": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440003",
        "amount": 20000000,
        "paid_at": "2026-03-10T14:30:00.000Z",
        "payer_name": "CHIDI OKONKWO",
        "payment_channel": "bank_transfer"
      }
    ]
  }
}

Line item fields

FieldTypeDescription
idstringLine item UUID
descriptionstringItem description
quantitynumberQuantity
unit_priceintegerUnit price in kobo

Payment fields

FieldTypeDescription
idstringPayment record UUID
amountintegerPayment amount in kobo
paid_atstringPayment timestamp (ISO 8601)
payer_namestring | nullName of the person who made the payment
payment_channelstring | nullPayment channel: "bank_transfer" or "payid"

Send Invoice

Transitions an invoice from draft to sent. Sets the issued_at timestamp and makes the payment_url active.

POST /v1/checkout/invoice/:publicId/send

Authentication

Requires a secret key (sk_*).

Path parameters

ParameterTypeDescription
publicIdstringThe invoice's public_id

Example request

bash
curl -X POST https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78/send \
  -H "Authorization: Bearer sk_test_your_secret_key"
javascript
const response = await fetch(
  "https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78/send",
  {
    method: "POST",
    headers: { "Authorization": "Bearer sk_test_your_secret_key" },
  }
);
const data = await response.json();
python
import requests

response = requests.post(
    "https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78/send",
    headers={"Authorization": "Bearer sk_test_your_secret_key"},
)
data = response.json()

Response

Returns the updated invoice object with status: "sent" and issued_at populated.

Errors

StatusMessageCause
404Invoice not foundInvoice does not exist or does not belong to your account
400Only draft invoices can be sentInvoice is not in draft status

Cancel Invoice

Cancels an invoice. Only invoices with status draft, sent, partial, or overdue can be cancelled.

POST /v1/checkout/invoice/:publicId/cancel

Authentication

Requires a secret key (sk_*).

Path parameters

ParameterTypeDescription
publicIdstringThe invoice's public_id

Example request

bash
curl -X POST https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78/cancel \
  -H "Authorization: Bearer sk_test_your_secret_key"
javascript
const response = await fetch(
  "https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78/cancel",
  {
    method: "POST",
    headers: { "Authorization": "Bearer sk_test_your_secret_key" },
  }
);
const data = await response.json();
python
import requests

response = requests.post(
    "https://api.zevpaycheckout.com/v1/checkout/invoice/abc123def456ghi78/cancel",
    headers={"Authorization": "Bearer sk_test_your_secret_key"},
)
data = response.json()

Response

Returns the updated invoice object with status: "cancelled" and cancelled_at populated.

Errors

StatusMessageCause
404Invoice not foundInvoice does not exist or does not belong to your account
400Cannot cancel an invoice with status "paid"Invoice is already fully paid

Invoice Status Lifecycle

draft → sent → partial → paid
  ↓       ↓       ↓
  └───────┴───────┴──→ cancelled

sent/partial → overdue (automatic, when past due date)
overdue → paid (if payment received after due date)
overdue → cancelled
StatusDescription
draftInvoice created but not yet sent to customer. Payment URL is not usable
sentInvoice sent, awaiting payment. Payment URL is active
partialCustomer has made a partial payment
paidInvoice fully paid
overduePast due date, not fully paid (set automatically by hourly check)
cancelledInvoice cancelled by merchant

Webhook Events

Subscribe to these events via your webhook configuration:

EventDescription
invoice.createdInvoice created via API
invoice.sentInvoice status changed to sent
invoice.payment_receivedPayment received (partial or full)
invoice.paidInvoice fully paid
invoice.overdueInvoice past due date, not fully paid
invoice.cancelledInvoice cancelled

See the full Webhook Events Reference for complete payload documentation.

Example webhook payload

json
{
  "event": "invoice.payment_received",
  "data": {
    "public_id": "abc123def456ghi78",
    "invoice_number": "INV-202603-001",
    "status": "partial",
    "customer_name": "Chidi Okonkwo",
    "customer_email": "chidi@example.com",
    "subtotal": 41000000,
    "tax_rate": 7.5,
    "tax_amount": 3075000,
    "total": 44075000,
    "amount_paid": 20000000,
    "currency": "NGN",
    "due_date": "2026-03-31T00:00:00.000Z",
    "issued_at": "2026-03-08T10:05:00.000Z",
    "paid_at": null,
    "merchant_reference": "PROJECT-2026-001",
    "metadata": { "project_id": "PRJ-123" },
    "payment_url": "https://invoice.zevpaycheckout.com/pay/abc123def456ghi78?token=...",
    "payment_amount": 20000000,
    "amount_remaining": 24075000,
    "payer_name": "CHIDI OKONKWO",
    "payment_channel": "bank_transfer"
  }
}

Error Responses

All error responses follow this format:

json
{
  "statusCode": 400,
  "message": "Only draft invoices can be edited.",
  "error": "Bad Request"
}

Common errors

StatusMessageEndpoint
401API key is requiredAll — missing or invalid API key
403This endpoint requires a secret keyAll — using a public key instead of secret key
403Invoices are only available for business accountsCreate — personal account trying to create invoices
400At least one line item is requiredCreate — empty line_items array
400Invoice total must be greater than zeroCreate/Update — calculated total is zero or negative
400Invalid due date formatCreate/Update — invalid ISO date string
400Only draft invoices can be editedUpdate — invoice is not in draft status
400Only draft invoices can be sentSend — invoice is not in draft status
404Invoice not foundGet/Update/Send/Cancel — invalid public_id or not your invoice

Try it — Create Invoice

API Playground
POSThttps://api.zevpaycheckout.com/v1/checkout/invoice

Try it — List Invoices

API Playground
GEThttps://api.zevpaycheckout.com/v1/checkout/invoice

ZevPay Checkout Developer Documentation