API reference
Invoices
Raise invoices into the organization's approval workflow, read them, create links for your customers, and start online payments.
The API raises and reads invoices. It can't update, approve, return or void them: people do that in the Flux app, and you hear about it by webhook. See Invoice lifecycle .
The invoice object# An invoice raised into the organization's approval workflow.
Field Description id integer Flux's id for the invoice. number string The invoice number. Its format depends on the organization's settings: treat it as an opaque string. externalRef string · nullable Your reference, if you sent one. customerId integer The customer billed. currency string ISO currency code. One of NGN, USD, CAD, EUR, GBP. status string Where the invoice is in its lifecycle. One of awaiting_approval, awaiting_final_approval, returned, approved, paid, voided. overdue boolean Derived: approved, unpaid, not voided and past dueDate. paymentStatus string · nullable A note set by payment processing, such as successful or refunded: NGN 1,000.00. Display it; don't branch on it. invoiceType string · nullable The kind of invoice. One of setup, build, subscription, usage, performance, mixed, other. periodStart date · nullable Start of the billing period. periodEnd date · nullable End of the billing period. nextDueDate date · nullable When the next cycle's invoice is due. Informational. createdAt timestamp When the invoice was raised. Server time, no offset. approvedAt timestamp · nullable When it was fully approved. dueDate date When payment is due. paidAt timestamp · nullable When it was paid. subtotal number (decimal) Sum of the lines. discount number (decimal) Discount on the subtotal. taxableNet number (decimal) Amount VAT is charged on. vatAmount number (decimal) VAT charged, from the organization's VAT setting. vatLabel string · nullable How the VAT line is labelled, such as VAT (7.5%). Comes from the organization's VAT setting. total number (decimal) Amount the customer pays. lines array of Invoice line The invoice lines. payments array of Payment attempt · nullable Online payment attempts. Set only by GET /invoices/{id} when the key has payments:read; null everywhere else.
{
"id" : 5012 ,
"number" : "MAX-2026-0042" ,
"externalRef" : "ws_42-2026-11" ,
"customerId" : 812 ,
"currency" : "NGN" ,
"status" : "approved" ,
"overdue" : false ,
"paymentStatus" : null ,
"invoiceType" : "subscription" ,
"periodStart" : "2026-11-01T00:00:00" ,
"periodEnd" : "2026-11-30T00:00:00" ,
"nextDueDate" : "2026-12-01T00:00:00" ,
"createdAt" : "2026-11-01T09:00:00" ,
"approvedAt" : "2026-11-01T11:30:00" ,
"dueDate" : "2026-11-15T00:00:00" ,
"paidAt" : null ,
"subtotal" : 800000.00 ,
"discount" : 0.00 ,
"taxableNet" : 800000.00 ,
"vatAmount" : 60000.00 ,
"vatLabel" : "VAT (7.5%)" ,
"total" : 860000.00 ,
"lines" : [
{
"serviceId" : 7 ,
"description" : "Subscription — 3 entities (3 × 250,000.00)" ,
"detail" : "3 entities" ,
"quantity" : 3 ,
"unitPrice" : 250000.00 ,
"amount" : 750000.00
},
{
"serviceId" : 9 ,
"description" : "Onboarding" ,
"detail" : null ,
"quantity" : 1 ,
"unitPrice" : 50000.00 ,
"amount" : 50000.00
}
],
"payments" : [
{
"reference" : "T12-MAX-2026-0042-K3P9QZ_1" ,
"channel" : "paystack" ,
"succeeded" : false ,
"attemptedAt" : "2026-11-02T08:15:00"
}
]
}
Each entry in lines:
Field Description serviceId integer The service in the organization's catalogue. description string Ready to display: the service name, then — detail, then (qty × rate). detail string · nullable Your detail text for the line. quantity number (decimal) Quantity. 1 when the line was sent as an amount. unitPrice number (decimal) Rate per unit. amount number (decimal) Line amount, net of VAT.
Each entry in payments:
Field Description reference string The payment reference sent to Paystack. channel string The payment channel, such as paystack. succeeded boolean Whether Flux verified and accepted the payment. attemptedAt timestamp When the attempt started. Server time, no offset.
Raise an invoice# Raises an invoice into the organization's approval workflow, as if a person had raised it in the app. It's recorded as raised by the admin who created the API key. The customer isn't emailed until the invoice is approved.
Request body Field Description externalRef string optional Your stable reference for this invoice, such as ws_42-2026-11. At most 100 characters, unique per organization. A repeat returns the existing invoice.customerId integer required A customer in the organization.currency string required A supported ISO currency code. One of NGN, USD, CAD, EUR, GBP.dueDate date required When payment is due. Send YYYY-MM-DD.discount number (decimal) optional 0 or more, and no more than the subtotal. Default 0.periodStart date optional Billing period start. Send it together with periodEnd, or neither.periodEnd date optional Billing period end. On or after periodStart.invoiceType string optional The kind of invoice. One of setup, build, subscription, usage, performance, mixed, other.nextDueDate date optional When the next cycle's invoice is due. Informational.comments string optional A note for the organization's reviewers.lines array of Invoice line (request) required At least one line. The subtotal is the sum of the lines; you can't send a total.
Lines. At least one. Each references a serviceId in the organization's catalogue, and lines may use different services. Each takes either amount or quantity and unitPrice. quantity must be greater than 0, unitPrice 0 or more, and every line's amount greater than 0.Totals. The subtotal is the sum of the lines; you can't send one. VAT comes from the organization's VAT setting.Billing period. Send both periodStart and periodEnd, or neither. The end can't be before the start.Numbering. The invoice number's prefix comes from the first line's service category.No double billing. externalRef is checked permanently: a second request with the same value returns the existing invoice with 200. See Idempotency .Request cURL Node.js Python PHP C# Go
Copy curl -X POST "$FLUX_BASE_URL/api/v1/invoices" \
-H "Authorization: ApiKey $FLUX_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: invoice-ws_42-2026-11" \
-d '{
"externalRef": "ws_42-2026-11",
"customerId": 812,
"currency": "NGN",
"dueDate": "2026-11-15",
"discount": 0,
"periodStart": "2026-11-01",
"periodEnd": "2026-11-30",
"invoiceType": "subscription",
"nextDueDate": "2026-12-01",
"comments": "November subscription",
"lines": [
{
"serviceId": 7,
"quantity": 3,
"unitPrice": 250000,
"detail": "3 entities"
},
{
"serviceId": 9,
"amount": 50000
}
]
}'// Node.js 18+ (built-in fetch). Save as an .mjs file.
const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/invoices`, {
method: "POST",
headers: {
Authorization: `ApiKey ${process.env.FLUX_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "invoice-ws_42-2026-11",
},
body: JSON.stringify({
externalRef: "ws_42-2026-11",
customerId: 812,
currency: "NGN",
dueDate: "2026-11-15",
discount: 0,
periodStart: "2026-11-01",
periodEnd: "2026-11-30",
invoiceType: "subscription",
nextDueDate: "2026-12-01",
comments: "November subscription",
lines: [
{
serviceId: 7,
quantity: 3,
unitPrice: 250000,
detail: "3 entities",
},
{
serviceId: 9,
amount: 50000,
},
],
}),
});
console.log(response.status, await response.json());import os
import requests
response = requests.post(
f"{os.environ['FLUX_BASE_URL']}/api/v1/invoices",
headers={
"Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}",
"Idempotency-Key": "invoice-ws_42-2026-11",
},
json={
"externalRef": "ws_42-2026-11",
"customerId": 812,
"currency": "NGN",
"dueDate": "2026-11-15",
"discount": 0,
"periodStart": "2026-11-01",
"periodEnd": "2026-11-30",
"invoiceType": "subscription",
"nextDueDate": "2026-12-01",
"comments": "November subscription",
"lines": [
{
"serviceId": 7,
"quantity": 3,
"unitPrice": 250000,
"detail": "3 entities",
},
{
"serviceId": 9,
"amount": 50000,
},
],
},
timeout=30,
)
print(response.status_code, response.json())<?php
$body = <<<'JSON'
{
"externalRef": "ws_42-2026-11",
"customerId": 812,
"currency": "NGN",
"dueDate": "2026-11-15",
"discount": 0,
"periodStart": "2026-11-01",
"periodEnd": "2026-11-30",
"invoiceType": "subscription",
"nextDueDate": "2026-12-01",
"comments": "November subscription",
"lines": [
{
"serviceId": 7,
"quantity": 3,
"unitPrice": 250000,
"detail": "3 entities"
},
{
"serviceId": 9,
"amount": 50000
}
]
}
JSON;
$ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/invoices');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: ApiKey ' . getenv('FLUX_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: invoice-ws_42-2026-11',
],
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers;
using System.Text;
var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL");
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/api/v1/invoices");
request.Headers.Add("Idempotency-Key", "invoice-ws_42-2026-11");
request.Content = new StringContent("""
{
"externalRef": "ws_42-2026-11",
"customerId": 812,
"currency": "NGN",
"dueDate": "2026-11-15",
"discount": 0,
"periodStart": "2026-11-01",
"periodEnd": "2026-11-30",
"invoiceType": "subscription",
"nextDueDate": "2026-12-01",
"comments": "November subscription",
"lines": [
{
"serviceId": 7,
"quantity": 3,
"unitPrice": 250000,
"detail": "3 entities"
},
{
"serviceId": 9,
"amount": 50000
}
]
}
""", Encoding.UTF8, "application/json");
using var response = await http.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
body := strings.NewReader(`{
"externalRef": "ws_42-2026-11",
"customerId": 812,
"currency": "NGN",
"dueDate": "2026-11-15",
"discount": 0,
"periodStart": "2026-11-01",
"periodEnd": "2026-11-30",
"invoiceType": "subscription",
"nextDueDate": "2026-12-01",
"comments": "November subscription",
"lines": [
{
"serviceId": 7,
"quantity": 3,
"unitPrice": 250000,
"detail": "3 entities"
},
{
"serviceId": 9,
"amount": 50000
}
]
}`)
req, err := http.NewRequest("POST", os.Getenv("FLUX_BASE_URL")+"/api/v1/invoices", body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "invoice-ws_42-2026-11")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(out))
}Responses 201 Created: a new invoice201 Created: a new invoice Copy {
"id" : 5012 ,
"number" : "MAX-2026-0042" ,
"externalRef" : "ws_42-2026-11" ,
"customerId" : 812 ,
"currency" : "NGN" ,
"status" : "awaiting_approval" ,
"overdue" : false ,
"paymentStatus" : null ,
"invoiceType" : "subscription" ,
"periodStart" : "2026-11-01T00:00:00" ,
"periodEnd" : "2026-11-30T00:00:00" ,
"nextDueDate" : "2026-12-01T00:00:00" ,
"createdAt" : "2026-11-01T09:00:00" ,
"approvedAt" : null ,
"dueDate" : "2026-11-15T00:00:00" ,
"paidAt" : null ,
"subtotal" : 800000.00 ,
"discount" : 0.00 ,
"taxableNet" : 800000.00 ,
"vatAmount" : 60000.00 ,
"vatLabel" : "VAT (7.5%)" ,
"total" : 860000.00 ,
"lines" : [
{
"serviceId" : 7 ,
"description" : "Subscription — 3 entities (3 × 250,000.00)" ,
"detail" : "3 entities" ,
"quantity" : 3 ,
"unitPrice" : 250000.00 ,
"amount" : 750000.00
},
{
"serviceId" : 9 ,
"description" : "Onboarding" ,
"detail" : null ,
"quantity" : 1 ,
"unitPrice" : 50000.00 ,
"amount" : 50000.00
}
],
"payments" : null
}200 OK: an invoice with this externalRef already existsFlux returns the existing invoice, in its current state, and raises nothing.
200 OK: an invoice with this externalRef already exists Copy {
"id" : 5012 ,
"number" : "MAX-2026-0042" ,
"externalRef" : "ws_42-2026-11" ,
"customerId" : 812 ,
"currency" : "NGN" ,
"status" : "approved" ,
"overdue" : false ,
"paymentStatus" : null ,
"invoiceType" : "subscription" ,
"periodStart" : "2026-11-01T00:00:00" ,
"periodEnd" : "2026-11-30T00:00:00" ,
"nextDueDate" : "2026-12-01T00:00:00" ,
"createdAt" : "2026-11-01T09:00:00" ,
"approvedAt" : "2026-11-01T11:30:00" ,
"dueDate" : "2026-11-15T00:00:00" ,
"paidAt" : null ,
"subtotal" : 800000.00 ,
"discount" : 0.00 ,
"taxableNet" : 800000.00 ,
"vatAmount" : 60000.00 ,
"vatLabel" : "VAT (7.5%)" ,
"total" : 860000.00 ,
"lines" : [
{
"serviceId" : 7 ,
"description" : "Subscription — 3 entities (3 × 250,000.00)" ,
"detail" : "3 entities" ,
"quantity" : 3 ,
"unitPrice" : 250000.00 ,
"amount" : 750000.00
},
{
"serviceId" : 9 ,
"description" : "Onboarding" ,
"detail" : null ,
"quantity" : 1 ,
"unitPrice" : 50000.00 ,
"amount" : 50000.00
}
],
"payments" : null
}7 possible errors List invoices# GET /api/v1/invoicesinvoices:read
Lists invoices, newest first. Filters combine.
Query parameters Field Description customerId integer Only this customer's invoices. status string Only invoices with this status. An unknown value gets 400 invalid_request. One of awaiting_approval, awaiting_final_approval, returned, approved, paid, voided. overdue boolean true for only overdue invoices.externalRef string Only the invoice with this reference. limit integer Items per page, 1 to 100. Default 25. cursor string The nextCursor from the previous page. Leave it out for the first page.
Request cURL Node.js Python PHP C# Go
Copy curl "$FLUX_BASE_URL/api/v1/invoices?customerId=812&status=approved" \
-H "Authorization: ApiKey $FLUX_API_KEY"// Node.js 18+ (built-in fetch). Save as an .mjs file.
const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/invoices?customerId=812&status=approved`, {
headers: {
Authorization: `ApiKey ${process.env.FLUX_API_KEY}`,
},
});
console.log(response.status, await response.json());import os
import requests
response = requests.get(
f"{os.environ['FLUX_BASE_URL']}/api/v1/invoices",
headers={"Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}"},
params={
"customerId": 812,
"status": "approved",
},
timeout=30,
)
print(response.status_code, response.json())<?php
$ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/invoices?customerId=812&status=approved');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: ApiKey ' . getenv('FLUX_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers;
var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL");
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/api/v1/invoices?customerId=812&status=approved");
using var response = await http.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", os.Getenv("FLUX_BASE_URL")+"/api/v1/invoices?customerId=812&status=approved", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(out))
}Responses 200 OKList results always have payments: null.
{
"data" : [
{
"id" : 5012 ,
"number" : "MAX-2026-0042" ,
"externalRef" : "ws_42-2026-11" ,
"customerId" : 812 ,
"currency" : "NGN" ,
"status" : "approved" ,
"overdue" : false ,
"paymentStatus" : null ,
"invoiceType" : "subscription" ,
"periodStart" : "2026-11-01T00:00:00" ,
"periodEnd" : "2026-11-30T00:00:00" ,
"nextDueDate" : "2026-12-01T00:00:00" ,
"createdAt" : "2026-11-01T09:00:00" ,
"approvedAt" : "2026-11-01T11:30:00" ,
"dueDate" : "2026-11-15T00:00:00" ,
"paidAt" : null ,
"subtotal" : 800000.00 ,
"discount" : 0.00 ,
"taxableNet" : 800000.00 ,
"vatAmount" : 60000.00 ,
"vatLabel" : "VAT (7.5%)" ,
"total" : 860000.00 ,
"lines" : [
{
"serviceId" : 7 ,
"description" : "Subscription — 3 entities (3 × 250,000.00)" ,
"detail" : "3 entities" ,
"quantity" : 3 ,
"unitPrice" : 250000.00 ,
"amount" : 750000.00
},
{
"serviceId" : 9 ,
"description" : "Onboarding" ,
"detail" : null ,
"quantity" : 1 ,
"unitPrice" : 50000.00 ,
"amount" : 50000.00
}
],
"payments" : null
}
],
"nextCursor" : null ,
"hasMore" : false
}4 possible errors Status Code When 400invalid_requestValidation failed. The message says what, such as Add at least one invoice item . 401unauthorizedNo API key, an unknown key or a revoked key. 403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }. 429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.
Get an invoice# GET /api/v1/invoices/{id}invoices:read
Returns the invoice with its lines, period, type and status. With payments:read, it also includes the payment attempts.
Path parameters Field Description id integer required The invoice's id.
Request cURL Node.js Python PHP C# Go
Copy curl "$FLUX_BASE_URL/api/v1/invoices/5012" \
-H "Authorization: ApiKey $FLUX_API_KEY"// Node.js 18+ (built-in fetch). Save as an .mjs file.
const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/invoices/5012`, {
headers: {
Authorization: `ApiKey ${process.env.FLUX_API_KEY}`,
},
});
console.log(response.status, await response.json());import os
import requests
response = requests.get(
f"{os.environ['FLUX_BASE_URL']}/api/v1/invoices/5012",
headers={"Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}"},
timeout=30,
)
print(response.status_code, response.json())<?php
$ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/invoices/5012');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: ApiKey ' . getenv('FLUX_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers;
var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL");
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/api/v1/invoices/5012");
using var response = await http.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", os.Getenv("FLUX_BASE_URL")+"/api/v1/invoices/5012", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(out))
}Responses 200 OKThis example is for a key with payments:read. Without it, payments is null.
{
"id" : 5012 ,
"number" : "MAX-2026-0042" ,
"externalRef" : "ws_42-2026-11" ,
"customerId" : 812 ,
"currency" : "NGN" ,
"status" : "approved" ,
"overdue" : false ,
"paymentStatus" : null ,
"invoiceType" : "subscription" ,
"periodStart" : "2026-11-01T00:00:00" ,
"periodEnd" : "2026-11-30T00:00:00" ,
"nextDueDate" : "2026-12-01T00:00:00" ,
"createdAt" : "2026-11-01T09:00:00" ,
"approvedAt" : "2026-11-01T11:30:00" ,
"dueDate" : "2026-11-15T00:00:00" ,
"paidAt" : null ,
"subtotal" : 800000.00 ,
"discount" : 0.00 ,
"taxableNet" : 800000.00 ,
"vatAmount" : 60000.00 ,
"vatLabel" : "VAT (7.5%)" ,
"total" : 860000.00 ,
"lines" : [
{
"serviceId" : 7 ,
"description" : "Subscription — 3 entities (3 × 250,000.00)" ,
"detail" : "3 entities" ,
"quantity" : 3 ,
"unitPrice" : 250000.00 ,
"amount" : 750000.00
},
{
"serviceId" : 9 ,
"description" : "Onboarding" ,
"detail" : null ,
"quantity" : 1 ,
"unitPrice" : 50000.00 ,
"amount" : 50000.00
}
],
"payments" : [
{
"reference" : "T12-MAX-2026-0042-K3P9QZ_1" ,
"channel" : "paystack" ,
"succeeded" : false ,
"attemptedAt" : "2026-11-02T08:15:00"
}
]
}4 possible errors Status Code When 404not_foundUnknown id, or an id that belongs to another organization. 401unauthorizedNo API key, an unknown key or a revoked key. 403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }. 429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.
Create invoice links# POST /api/v1/invoices/{id}/linksinvoices:read
Returns signed links to the invoice PDF and its payment page, safe to show to your customer. payUrl is null when the invoice is paid or voided. Ask for fresh links each time you show them. See Signed links .
Path parameters Field Description id integer required The invoice's id.
Request cURL Node.js Python PHP C# Go
Copy curl -X POST "$FLUX_BASE_URL/api/v1/invoices/5012/links" \
-H "Authorization: ApiKey $FLUX_API_KEY"// Node.js 18+ (built-in fetch). Save as an .mjs file.
const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/invoices/5012/links`, {
method: "POST",
headers: {
Authorization: `ApiKey ${process.env.FLUX_API_KEY}`,
},
});
console.log(response.status, await response.json());import os
import requests
response = requests.post(
f"{os.environ['FLUX_BASE_URL']}/api/v1/invoices/5012/links",
headers={"Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}"},
timeout=30,
)
print(response.status_code, response.json())<?php
$ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/invoices/5012/links');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: ApiKey ' . getenv('FLUX_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers;
var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL");
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/api/v1/invoices/5012/links");
using var response = await http.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("POST", os.Getenv("FLUX_BASE_URL")+"/api/v1/invoices/5012/links", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(out))
}Responses 200 OK{
"pdfUrl" : "https://api.flux.example/public/invoices/NDIuNTAxMi5pcC4xNzk2MTE2NDAw.mJb2Kc0r1oB7yWq4sZ0eD9xVh3tLnA6uPfRkGiE8ScQ/pdf" ,
"payUrl" : "https://api.flux.example/public/invoices/NDIuNTAxMi5wYXkuMTc5NjExNjQwMA.Q2pXv8nTz1sLcR0aB4yHk7uWm3eJd6gFoNiV5qYtKxE/pay" ,
"expiresAt" : "2026-12-01T10:00:00.5236411Z"
}5 possible errors Status Code When 404not_foundUnknown id, or an id that belongs to another organization. 503links_not_configuredThe server has no link-signing key configured. 401unauthorizedNo API key, an unknown key or a revoked key. 403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }. 429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.
List payment attempts# GET /api/v1/invoices/{id}/paymentspayments:read
The invoice's online payment attempts, as an array. An attempt that didn't succeed may still be in progress: wait for the webhook.
Path parameters Field Description id integer required The invoice's id.
Request cURL Node.js Python PHP C# Go
Copy curl "$FLUX_BASE_URL/api/v1/invoices/5012/payments" \
-H "Authorization: ApiKey $FLUX_API_KEY"// Node.js 18+ (built-in fetch). Save as an .mjs file.
const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/invoices/5012/payments`, {
headers: {
Authorization: `ApiKey ${process.env.FLUX_API_KEY}`,
},
});
console.log(response.status, await response.json());import os
import requests
response = requests.get(
f"{os.environ['FLUX_BASE_URL']}/api/v1/invoices/5012/payments",
headers={"Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}"},
timeout=30,
)
print(response.status_code, response.json())<?php
$ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/invoices/5012/payments');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: ApiKey ' . getenv('FLUX_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers;
var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL");
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/api/v1/invoices/5012/payments");
using var response = await http.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", os.Getenv("FLUX_BASE_URL")+"/api/v1/invoices/5012/payments", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(out))
}Responses 200 OK[
{
"reference" : "T12-MAX-2026-0042-K3P9QZ_1" ,
"channel" : "paystack" ,
"succeeded" : false ,
"attemptedAt" : "2026-11-02T08:15:00"
}
]4 possible errors Status Code When 404not_foundUnknown id, or an id that belongs to another organization. 401unauthorizedNo API key, an unknown key or a revoked key. 403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }. 429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.
Start a checkout# POST /api/v1/invoices/{id}/checkoutpayments:write
Starts an online payment with Paystack and returns the checkout URL. Redirect the payer there. The redirect back from Paystack isn't proof of payment: wait for invoice.paid. See Payments .
Path parameters Field Description id integer required The invoice's id.
Request cURL Node.js Python PHP C# Go
Copy curl -X POST "$FLUX_BASE_URL/api/v1/invoices/5012/checkout" \
-H "Authorization: ApiKey $FLUX_API_KEY"// Node.js 18+ (built-in fetch). Save as an .mjs file.
const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/invoices/5012/checkout`, {
method: "POST",
headers: {
Authorization: `ApiKey ${process.env.FLUX_API_KEY}`,
},
});
console.log(response.status, await response.json());import os
import requests
response = requests.post(
f"{os.environ['FLUX_BASE_URL']}/api/v1/invoices/5012/checkout",
headers={"Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}"},
timeout=30,
)
print(response.status_code, response.json())<?php
$ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/invoices/5012/checkout');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: ApiKey ' . getenv('FLUX_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers;
var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL");
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/api/v1/invoices/5012/checkout");
using var response = await http.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("POST", os.Getenv("FLUX_BASE_URL")+"/api/v1/invoices/5012/checkout", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(out))
}Responses 200 OK{
"checkoutUrl" : "https://checkout.paystack.com/7xk2m9q4fz0b1ac"
}9 possible errors Status Code When 404not_foundUnknown id, or an id that belongs to another organization. 409invoice_voidedCheckout: the invoice is voided. 409already_paidCheckout: the invoice is already paid. 409not_onlineCheckout: the invoice wasn't approved for online payment. 409payment_not_availableCheckout: online payment can't start, for example because the organization's payment settings aren't complete. The message explains. 502gateway_errorPaystack didn't start the payment. details holds the gateway's response. 401unauthorizedNo API key, an unknown key or a revoked key. 403insufficient_scopeThe key lacks the scope. details names it: { "required": "invoices:write" }. 429rate_limitedToo many requests for this key. Wait for the Retry-After seconds.