Invoices API

Create an invoice from your own platform and send its link to your customer. We issue the invoice (PDF, payment page, email), collect the payment and tell you what happened through the invoice's own webhooks.

Every call needs a server key (sk_live_ or sk_test_). An invoice belongs to the key's business and mode: another business's invoice, or a test invoice read with a live key, is a 404. The invoice is issued by your business, under the legal name, TRN and GCT registration number in your settings.

Create an Invoice

POST/v1/invoices

Scope: invoice.create

Request Body

{
  "customer": { "name": "Marcia Thompson", "email": "[email protected]", "phone": "+18765550142" },
  "currency": "JMD",
  "items": [
    { "description": "Full wash", "quantity": 2, "unitPrice": 500.00, "discount": 100.00, "taxRate": 0.15 }
  ],
  "pricesIncludeTax": false,
  "dueDate": "2026-10-31",
  "total": 1035.00,
  "reference": "ORD-1042",
  "metadata": { "orderId": "1042" },
  "send": "none",
  "successUrl": "https://yoursite.com/orders/1042",
  "notificationUrl": "https://yoursite.com/webhooks/pay",
  "idempotencyKey": "ord-1042-invoice"
}
  • taxRate is a fraction: 0.15 is 15%. With pricesIncludeTax: falseeach line's net is quantity × unit price − discount and its tax is net × rate. With true, unit prices and discounts include tax and the tax is worked out of them.
  • Optional checks: an item's taxAmount, and the invoice's total and taxTotal. Each must match what the items work out to within 0.01, or the request is refused with validation_error. A matching taxAmount is used as given, so your rounding is kept.
  • send: "email" emails the invoice to customer.email now, and we send payment reminders. "none" (the default) means we never message your customer: send them the hostedUrl yourself, by SMS for example.
  • dueDate defaults to your payment terms. reference (at most 100 characters) is yours to search by. metadata takes up to 50 string pairs and comes back in webhooks.
  • notificationUrl / notificationSecret follow the same rules as on checkout sessions: an https URL on a public host, and a secret of 16–200 characters. If you leave out the secret we generate one and return it once, as notificationSecret.
  • idempotencyKey (or an Idempotency-Key header): repeating the create with the same key returns the first invoice.
  • issuer and number are for businesses allowed to issue invoices for others. Without that permission they are refused with custom_issuer_not_allowed (403), never ignored.

Response

{
  "object": "invoice",
  "id": "Q7K2XM",
  "number": "INV-0482",
  "reference": "ORD-1042",
  "status": "open",
  "currency": "JMD",
  "subtotal": 1000.00,
  "discount": 100.00,
  "net": 900.00,
  "tax": 135.00,
  "total": 1035.00,
  "amountPaid": 0,
  "amountRefunded": 0,
  "amountDue": 1035.00,
  "dueDate": "2026-10-31T00:00:00+00:00",
  "createdAt": "2026-10-09T15:04:00+00:00",
  "isLive": true,
  "hostedUrl": "https://pay.mplex.net/view-invoice/Q7K2XM",
  "pdfUrl": "https://checkout.mplex.net/v1/invoices/Q7K2XM/pdf",
  "customer": { "name": "Marcia Thompson", "email": "[email protected]", "phone": "+18765550142" },
  "items": [
    { "description": "Full wash", "quantity": 2, "unitPrice": 500.00, "discount": 100.00, "net": 900.00, "taxRate": 0.15, "tax": 135.00, "total": 1035.00 }
  ],
  "metadata": { "orderId": "1042" },
  "issuer": { "legalName": "Yard Shop Limited", "tradingName": "Yard Shop", "trn": "123-456-789", "gctNumber": "GCT-0099", "taxLabel": "GCT" },
  "notificationSecret": "whsec_…"
}

Statuses: draft, open (awaiting payment), partially_paid, paid, past_due, void. The customer pays on hostedUrlwith your business's payment methods.

Retrieve an Invoice

GET/v1/invoices/{id}

Scope: invoice.read

Returns the invoice as above (without notificationSecret).

Find Invoices by Reference

GET/v1/invoices?reference=ORD-1042

Scope: invoice.read

Your business's invoices with that reference, newest first (at most 100), as a list: { "object": "list", "data": [ …invoices ], "hasMore": false }.

Download the PDF

GET/v1/invoices/{id}/pdf

Scope: invoice.read

Returns application/pdf: the issued invoice with its issuer block, TRN, GCT number and tax breakdown.

Send (or Resend) an Invoice

POST/v1/invoices/{id}/send

Scope: invoice.update

Emails the invoice to its customer and raises invoice.sent. From then on we also send payment reminders. A paid or void invoice is refused with invoice_not_payable (409).

Void an Invoice

POST/v1/invoices/{id}/void

Scope: invoice.update

{ "reason": "Order cancelled" }

An unpaid invoice can no longer be paid once voided, and invoice.voided follows. Voiding an invoice that is already void returns it unchanged. An invoice that has been paid, in full or in part, is refused with invoice_not_voidable (409): refund it instead.

Refund an Invoice

POST/v1/invoices/{id}/refund

Scope: invoice.refund

{ "reason": "Customer returned the item" }

Refunds every payment on the invoice in full; partial refunds aren't available yet. Each refund follows the same rules as checkout session refunds: the same payment_not_refundable and refund_failed errors, including whether a manual refund is needed. A refund that failed is safe to retry. Repeating a refund that worked returns alreadyRefunded: true. An invoice with no payment is refused with nothing_to_refund (409).

{
  "invoiceId": "Q7K2XM",
  "refunds": [
    { "paymentId": 4821, "referenceNumber": "PAY-4821", "status": "refunded", "amount": 1035.00, "currency": "JMD", "alreadyRefunded": false }
  ],
  "alreadyRefunded": false,
  "invoice": { "id": "Q7K2XM", "status": "paid", "amountRefunded": 1035.00 }
}

Webhooks

An invoice's events go to its own notificationUrl, signed with its secret (the X-Webhook-Signature header, sha256= plus the hex HMAC of the body), and also to any webhook endpoints set up in your dashboard:

  • invoice.sent: emailed to the customer
  • invoice.paid: paid in full
  • invoice.payment_failed: a payment attempt failed
  • invoice.past_due: the due date passed unpaid
  • invoice.voided: voided; it can no longer be paid
  • invoice.refunded: a payment on it was refunded
{
  "id": "evt_…",
  "type": "invoice.paid",
  "data": {
    "object": {
      "object": "invoice",
      "identifier": "Q7K2XM",
      "invoiceNumber": "INV-0482",
      "reference": "ORD-1042",
      "status": "Paid",
      "total": 1035.00,
      "taxAmount": 135.00,
      "totalPaid": 1035.00,
      "currency": "JMD",
      "metadata": { "orderId": "1042" }
    }
  }
}