Invoices
Create, send, and manage invoices. Invoices support partial payments and full lifecycle tracking via webhooks.
List Invoices
GET /invoices/
Headers
| Field | Description |
|---|---|
Authorization | Bearer your API key |
Query Parameters
| Type | Required | Description |
|---|---|---|
page | No | Page number to fetch (default: 1) |
per_page | No | Number of records per page (default: 20) |
status | No | Filter by draft, sent, paid, partially_paid, or overdue |
q | No | Search invoice title, invoice number, or customer name/email |
- cURL
- Node.js
- Python
- Go
curl -X GET https://sandbox.payscribe.ng/api/v1/invoices/?page=1&per_page=20 \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/invoices/?page=1&per_page=20', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/invoices/?page=1&per_page=20',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/invoices/?page=1&per_page=20", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
{
"status": true,
"description": "Invoices fetched.",
"message": {
"details": {
"invoices": [{
"id": "inv_abc123",
"invoice_no": "INV-2026-00001",
"title": "August design services",
"description": "Monthly product design retainer",
"currency": "NGN",
"total_amount": 50000,
"paid_amount": 0,
"status": "draft",
"issue_date": "2026-08-01",
"due_date": "2026-08-15",
"customer": {"id": "cus_abc123", "name": "John Doe", "email": "customer@example.com"},
"created_at": "2026-08-01 09:12:00"
}],
"total": 1,
"page": 1,
"per_page": 20
}
},
"status_code": 200
}
Create Invoice
POST /invoices/
Headers
| Field | Description |
|---|---|
Authorization | Bearer your API key |
Content-Type | application/json |
Parameters
| Type | Required | Description |
|---|---|---|
customer_id | string | Yes |
currency | string | Yes |
items | array | Yes |
title | string | No |
description | string | No |
issue_date | string | No |
due_date | string | No |
po_number | string | No |
discount_amount | number | No |
tax_amount | number | No |
shipping_amount | number | No |
allow_partial | boolean | No |
post_type | string | No |
Each item can include description, quantity, unit, rate, line_discount_amount, and line_tax_amount.
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/invoices/ \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_abc123",
"title": "August design services",
"currency": "NGN",
"description": "Monthly product design retainer",
"issue_date": "2026-08-01",
"due_date": "2026-08-15",
"allow_partial": false,
"post_type": "draft",
"items": [{"description": "Product design retainer", "quantity": 1, "unit": "month", "rate": 50000}]
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/invoices/', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"customer_id": "cus_abc123",
"title": "August design services",
"currency": "NGN",
"description": "Monthly product design retainer",
"issue_date": "2026-08-01",
"due_date": "2026-08-15",
"allow_partial": false,
"post_type": "draft",
"items": [{"description": "Product design retainer", "quantity": 1, "unit": "month", "rate": 50000}]
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"customer_id": "cus_abc123",
"title": "August design services",
"currency": "NGN",
"description": "Monthly product design retainer",
"issue_date": "2026-08-01",
"due_date": "2026-08-15",
"allow_partial": false,
"post_type": "draft",
"items": [{"description": "Product design retainer", "quantity": 1, "unit": "month", "rate": 50000}]
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/invoices/',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"customer_id": "cus_abc123",
"title": "August design services",
"currency": "NGN",
"description": "Monthly product design retainer",
"issue_date": "2026-08-01",
"due_date": "2026-08-15",
"allow_partial": false,
"post_type": "draft",
"items": [{"description": "Product design retainer", "quantity": 1, "unit": "month", "rate": 50000}]
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/invoices/", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
{
"status": true,
"description": "Invoice created successfully.",
"message": {
"details": {
"id": "inv_abc123",
"invoice_no": "INV-2026-00001",
"status": "draft",
"total_amount": 50000,
"currency": "NGN"
}
},
"status_code": 200
}
Get Invoice
GET /invoices/{id}
Headers
| Field | Description |
|---|---|
Authorization | Bearer your API key |
Path Parameters
| Type | Required | Description |
|---|---|---|
id | string | Yes |
- cURL
- Node.js
- Python
- Go
curl -X GET https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123 \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
{
"status": true,
"description": "Invoice fetched.",
"message": {
"details": {
"id": "inv_abc123",
"invoice_no": "INV-2026-00001",
"title": "August design services",
"description": "Monthly product design retainer",
"currency": "NGN",
"total_amount": 50000,
"paid_amount": 0,
"status": "draft",
"issue_date": "2026-08-01",
"due_date": "2026-08-15",
"po_number": null,
"allow_partial": false,
"sent_at": null,
"paid_at": null,
"created_at": "2026-08-01 09:12:00",
"customer": {"id": "cus_abc123", "name": "John Doe", "email": "customer@example.com", "phone": "+2348012345678", "address": "12 Admiralty Way, Lekki"},
"items": [{"position": 1, "description": "Product design retainer", "quantity": 1, "unit": "month", "rate": 50000, "line_discount_amount": 0, "line_tax_amount": 0, "amount": 50000}],
"transactions": [],
"business": {"id": "b_abc123", "name": "Acme Ltd", "address": "12 Admiralty Way, Lekki", "logo_url": "https://res.cloudinary.com/.../logo.png"},
"payments": {
"wallets": {
"is_enabled": true,
"mode": {
"payscribe": {"title": "Pay with your Payscribe wallet", "meta": []},
"palmpay": {"title": "Connect and pay with your Palmpay wallet", "meta": []}
}
},
"ussd": {"is_enabled": true, "mode": []},
"bank_transfer": {"is_enabled": true, "mode": []}
}
}
},
"status_code": 200
}
transactions is empty until a payment is recorded against the invoice. Once populated, each entry has the shape {"id": "...", "amount": 25000, "description": "...", "remark": "...", "status": "success", "created_at": "...", "updated_at": "..."}.
Update Invoice
PATCH /invoices/{id}
Headers
| Field | Description |
|---|---|
Authorization | Bearer your API key |
Content-Type | application/json |
Path Parameters
| Type | Required | Description |
|---|---|---|
id | string | Yes |
Parameters
| Type | Required | Description |
|---|---|---|
customer_id | string | Yes |
currency | string | Yes |
items | array | Yes |
title | string | No |
due_date | string | No |
description | string | No |
po_number | string | No |
discount_amount | number | No |
tax_amount | number | No |
shipping_amount | number | No |
allow_partial | boolean | No |
Although this uses PATCH, the current API rewrites every omitted field on this list with a default (an omitted
customer_idis written back asnull, unlinking the invoice's customer). Send customer_id, currency, and items with every update. Apaidorcancelledinvoice cannot be updated (400 A paid or cancelled invoice cannot be edited.).
- cURL
- Node.js
- Python
- Go
curl -X PATCH https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123 \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_abc123",
"title": "August design services",
"currency": "NGN",
"description": "Monthly product design retainer",
"issue_date": "2026-08-01",
"due_date": "2026-09-01",
"allow_partial": false,
"items": [{"description": "Product design retainer", "quantity": 1, "unit": "month", "rate": 75000}]
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123', {
method: 'PATCH',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"customer_id": "cus_abc123",
"title": "August design services",
"currency": "NGN",
"description": "Monthly product design retainer",
"issue_date": "2026-08-01",
"due_date": "2026-09-01",
"allow_partial": false,
"items": [{"description": "Product design retainer", "quantity": 1, "unit": "month", "rate": 75000}]
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"customer_id": "cus_abc123",
"title": "August design services",
"currency": "NGN",
"description": "Monthly product design retainer",
"issue_date": "2026-08-01",
"due_date": "2026-09-01",
"allow_partial": false,
"items": [{"description": "Product design retainer", "quantity": 1, "unit": "month", "rate": 75000}]
}''')
response = requests.patch(
'https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"customer_id": "cus_abc123",
"title": "August design services",
"currency": "NGN",
"description": "Monthly product design retainer",
"issue_date": "2026-08-01",
"due_date": "2026-09-01",
"allow_partial": false,
"items": [{"description": "Product design retainer", "quantity": 1, "unit": "month", "rate": 75000}]
}`)
request, err := http.NewRequest(http.MethodPatch, "https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
{
"status": true,
"description": "Invoice updated.",
"message": {
"details": {
"id": "inv_abc123",
"invoice_no": "INV-2026-00001",
"status": "draft",
"total_amount": 75000,
"currency": "NGN"
}
},
"status_code": 200
}
Send Invoice
Triggers an email to the customer with a payment link.
POST /invoices/{id}/send
Headers
| Field | Description |
|---|---|
Authorization | Bearer your API key |
Path Parameters
| Type | Required | Description |
|---|---|---|
id | string | Yes |
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123/send \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123/send', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123/send',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123/send", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
{
"status": true,
"description": "Invoice sent to customer.",
"message": {"details": []},
"status_code": 200
}
Delete Invoice
DELETE /invoices/{id}
Headers
| Field | Description |
|---|---|
Authorization | Bearer your API key |
Path Parameters
| Type | Required | Description |
|---|---|---|
id | string | Yes |
A paid invoice cannot be deleted (400 A paid invoice cannot be deleted.).
- cURL
- Node.js
- Python
- Go
curl -X DELETE https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123 \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123', {
method: 'DELETE',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.delete(
'https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodDelete, "https://sandbox.payscribe.ng/api/v1/invoices/inv_abc123", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
{
"status": true,
"description": "Invoice deleted.",
"message": {"details": []},
"status_code": 200
}
Webhooks
| Event | Description |
|---|---|
invoice.sent | Invoice emailed to customer |
invoice.paid | Invoice paid in full |
invoice.partially_paid | Partial payment received |
invoice.overdue | Past due date without full payment |
See Webhooks for payload format and verification.
Was this page helpful?