API reference
Signed links
Signed links are public URLs to an invoice PDF, a receipt PDF or an invoice's payment page. They need no API key, so you can show them to your customers on a billing page or in your own emails.
Creating links#
| Endpoint | Returns | Scope |
|---|---|---|
POST /api/v1/invoices/{id}/links | pdfUrl and payUrl | invoices:read |
POST /api/v1/receipts/{invoiceId}/links | pdfUrl (the receipt) | invoices:read |
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))
}The links object#
Public, signed URLs you can show to end customers.
| Field | Description |
|---|---|
| pdfUrlstring | Opens the PDF. No API key needed. |
| payUrlstring · nullable | Redirects to Paystack checkout. null when the invoice is paid or voided, and always on receipt links. |
| expiresAttimestamp (UTC) | When the links stop working. UTC, with Z. |
{
"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"
}What the links open#
| Link | Opens |
|---|---|
/public/invoices/{token}/pdf | The invoice PDF. |
/public/receipts/{token}/pdf | The receipt PDF. |
/public/invoices/{token}/pay | Redirects to Paystack checkout. If the invoice is already paid, voided or not set up for online payment, it shows a short message instead. |
- A token works only for its own kind of document: a PDF link can't be used to pay.
- An invalid or expired link returns
404with “This link is invalid or has expired.” - PDF responses are sent with
Cache-Control: private, no-storeandX-Robots-Tag: noindex.
Expiry#
Links last 30 days by default (a server setting), shown in expiresAt. After that they stop working. Ask for fresh links when you show them, such as when your billing page loads, instead of storing them. Creating links is cheap and doesn't change anything.
Security#
- A link's token is signed with HMAC-SHA256 and names one organization, one invoice, one kind of document and an expiry. It can't be guessed or altered.
- Anyone who has a link can open it until it expires. Show links only to the customer they belong to, and send them only over HTTPS.
- If the server has no link-signing key configured, both endpoints return
503 links_not_configured.