Invoices
Creating an invoice also generates and attaches a real PDF, the same rendering pipeline the main app and Flus AI use.
How invoice creation works
An invoice is never just a row you insert — creating one runs the same pipeline the main app and Flus AI use, in this order:
Each line is resolved
A line with item_id pulls its title, price, unit,
and VAT rate fresh from your catalog at the moment of creation — not
a cached copy. Anything you pass alongside it
(unit_price, vat_rate)
overrides just that one field for just that one line, without touching the
catalog item itself. A line with title instead
of item_id is a one-off: it's priced entirely
from what you send and is never saved anywhere else.
Totals are computed server-side
Subtotal, VAT amount, and total are calculated from the resolved lines — you never send a total yourself, and one sent in the request body would be ignored. This is also true on update: change the quantity on one line, and every total recalculates automatically.
A real invoice number is assigned
Pulled from your account's own numbering sequence and prefix (so
invoice_number in the response looks like your
real invoices, not a generic placeholder), then the counter is bumped for
next time.
A PDF is rendered and attached
The same rendering pipeline the main app uses. This step is deliberately
non-fatal: if PDF generation hiccups, the invoice record itself is still
created and fully valid — pdf_file_URL is
just null until a retry succeeds. A missing PDF
is never a reason the whole request fails.
client_idstring (id)RequireditemsarrayRequiredAt least one line. Each: item_id or title, plus optional quantity (default 1), unit_price, vat_rate.
issue_date, due_datedate stringDefault to today, and today + your account's default payment terms.
Understanding the status lifecycle
Every invoice is born Draft and moves through a strict,
one-way state machine — there is no endpoint that moves a status backward, by
design: once a client may have seen an invoice, silently rewriting it would be
the wrong behavior for real accounting.
Draftstarting stateThe only state where line items can still change. Editable via PATCH, deletable via DELETE. Nothing has been sent to the client yet.
Sentfrom Draft onlyLine items and totals are frozen from here on — PATCH now returns a 409. The invoice is considered delivered to the client.
PaidterminalFrom Draft or Sent. Also creates a real payment record and generates a receipt PDF (receipt_url in the response) automatically — there's no separate "create a receipt" call. Cannot be changed again afterward.
CanceledterminalFrom Draft or Sent, never from Paid — a paid invoice can't be canceled through this endpoint. Also final; nothing moves an invoice out of Canceled.
The invoice object
Every field a GET returns. Totals, the number, currency, and the printed notes are all set for you — see Create for the short list of what you actually send.
_idstring (id)The invoice's public id, e.g. inv_9a3d27.
invoice_numberstringYour real numbered reference, e.g. IPP/1028 — account prefix + sequence.
clientstring (id)The client this invoice bills, as a client_… id.
statusDraft | Sent | Paid | CanceledChange it via the status endpoint, never by writing this field.
issue_date, due_datedatecurrencystringFrom your account default, e.g. EUR.
subtotal, vat_amount, vat_percentage, totalnumberAll computed server-side from the resolved lines.
itemsarray of idsThe line items as li_… ids. Create and update also return a fully expanded line_items breakdown alongside the object.
pdf_file_URL, pdf_name, pdf_file_sizestringThe generated PDF. pdf_file_URL is null until rendering finishes.
payment_record, receipt_fileid · string (url)Appear once the invoice is Paid — the payment (pay_…) and its receipt PDF.
payment_token, stripe_payment_intent_id, payment_reference_numberstringUsed by the hosted pay / checkout flow; mostly empty until a payment is in progress.
how_to_pay_*, legal_notice_*, delivery_info_*, footer_note_*, notes_*string · booleanThe note blocks printed on the PDF, each a _content + _enabled pair copied from your account defaults at creation.
company, Created Date, Modified Dateid · date- For a quick one-off invoice, skip the catalog entirely: send
{"title": "...", "unit_price": ...}as a line — no item needs to exist first. - Set
payment_referencewhen marking Paid — it's your one chance to attach your own reconciliation id, since the status endpoint can't be called again once an invoice is Paid. - Subscribe to webhooks instead of polling
GET /v1/invoices/:idfor status changes — you'll hear about a payment the instant it happens.
- Don't mark Paid until you're sure. There's no "undo Paid" endpoint — it's a terminal state, and reversing it means manual cleanup on our end, not an API call on yours.
- Don't call
PATCHafter an invoice is Sent expecting a silent no-op. It returns a 409, not a partial success — checkstatusfirst, or just catch the 409. - Don't forget
PATCHreplaces the whole line-item list. Sending just the one line you're changing deletes every other line on the invoice; always send the full, current set.
List invoices
limitquery param, numberDefault 25, max 100.
cursorquery param, stringFrom the previous page's pagination.next_cursor.
curl https://api.biflus.com/v1/invoices?limit=2 \ -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests
resp = requests.get(
"https://api.biflus.com/v1/invoices",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
params={"limit": 2},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/invoices?limit=2", {
headers: { "Authorization": "Bearer sk_test_YOUR_API_KEY" }
});
const data = await res.json();using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.biflus.com/v1/invoices?limit=2");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
var response = await client.SendAsync(request);
var data = await response.Content.ReadAsStringAsync();package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.biflus.com/v1/invoices?limit=2", nil)
req.Header.Set("Authorization", "Bearer sk_test_YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(string(data))
}import java.net.URI;
import java.net.http.*;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.biflus.com/v1/invoices?limit=2"))
.header("Authorization", "Bearer sk_test_YOUR_API_KEY")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); require 'net/http'
require 'json'
uri = URI("https://api.biflus.com/v1/invoices?limit=2")
req = Net::HTTP::Get.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){ "data": [ { "_id": "inv_9a3d27", "invoice_number": "IPP/1028", "status": "Draft", "total": 350 /* … */ }, { "_id": "inv_b6f930", "invoice_number": "IPP/1030", "status": "Paid", "total": 300 /* … */ } ], "pagination": { "count": 2, "remaining": 28, "next_cursor": "Mg==" } }
Get an invoice
Unlike create/update, fetching one invoice doesn't also return a separate line_items array — the PDF is the source of truth for the itemized breakdown.
curl https://api.biflus.com/v1/invoices/inv_9a3d27 \ -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests
resp = requests.get(
"https://api.biflus.com/v1/invoices/inv_9a3d27",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/invoices/inv_9a3d27", {
headers: { "Authorization": "Bearer sk_test_YOUR_API_KEY" }
});
const data = await res.json();using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.biflus.com/v1/invoices/inv_9a3d27");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
var response = await client.SendAsync(request);
var data = await response.Content.ReadAsStringAsync();package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.biflus.com/v1/invoices/inv_9a3d27", nil)
req.Header.Set("Authorization", "Bearer sk_test_YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(string(data))
}import java.net.URI;
import java.net.http.*;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.biflus.com/v1/invoices/inv_9a3d27"))
.header("Authorization", "Bearer sk_test_YOUR_API_KEY")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); require 'net/http'
require 'json'
uri = URI("https://api.biflus.com/v1/invoices/inv_9a3d27")
req = Net::HTTP::Get.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){ "data": { "_id": "inv_9a3d27", "invoice_number": "IPP/1028", "status": "Draft", "client": "client_a8f291", "subtotal": 350, "vat_amount": 0, "total": 350, "currency": "EUR", "pdf_file_URL": "https://invoices.biflus.com/pdfs/…/IPP1028.pdf" } }
Create an invoice
Totals, VAT, and the invoice number are all computed server-side, then a real PDF is rendered and attached in the same request. line_items in the response shows the fully resolved breakdown, including which lines came from your catalog.
client_idbody, string (id)RequiredA client id (client_…). The client's raw internal id works too.
itemsbody, arrayRequiredAt least one line-item object (see below).
issue_datebody, dateDefaults to today. Any date string works, e.g. "2026-08-08".
due_datebody, dateDefaults to issue_date plus your account's payment terms (7 days if none is set).
Line item object — each entry in items
item_idstring (id)A catalog item (item_…). Its title, unit, price, and VAT rate are pulled fresh from your catalog. Send this or title.
titlestringMakes this a one-off line with no catalog item. Required when item_id is omitted.
quantitynumberDefaults to 1.
unit_pricenumberOverrides the price for this one line. A catalog line defaults to the item's default_price; a one-off defaults to 0.
vat_ratenumberPercentage, e.g. 21. A catalog line defaults to the item's own VAT rate; a one-off defaults to 0.
unitstringe.g. "Hour". A catalog line defaults to the item's unit.
Everything else is set for you and ignored if sent: invoice_number, subtotal, vat_amount, total, currency, and the starting Draft status. Currency, payment terms, and the notes printed on the PDF (how-to-pay, legal notice, footer, etc.) all come from your account defaults.
curl -X POST https://api.biflus.com/v1/invoices \
-H "Authorization: Bearer sk_test_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"client_id": "client_a8f291", "items": [{"item_id": "item_2e88ac", "quantity": 2}, {"title": "One-off setup fee", "unit_price": 50}]}'import requests
resp = requests.post(
"https://api.biflus.com/v1/invoices",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
json={
"client_id": "client_a8f291",
"items": [
{"item_id": "item_2e88ac", "quantity": 2},
{"title": "One-off setup fee", "unit_price": 50},
],
},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/invoices", {
method: "POST",
headers: {
"Authorization": "Bearer sk_test_YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
client_id: "client_a8f291",
items: [
{ item_id: "item_2e88ac", quantity: 2 },
{ title: "One-off setup fee", unit_price: 50 }
]
})
});
const data = await res.json();using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.biflus.com/v1/invoices");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
request.Content = new StringContent("{"client_id": "client_a8f291", "items": [{"item_id": "item_2e88ac", "quantity": 2}, {"title": "One-off setup fee", "unit_price": 50}]}", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var data = await response.Content.ReadAsStringAsync();package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"client_id": "client_a8f291", "items": [{"item_id": "item_2e88ac", "quantity": 2}, {"title": "One-off setup fee", "unit_price": 50}]}`)
req, _ := http.NewRequest("POST", "https://api.biflus.com/v1/invoices", body)
req.Header.Set("Authorization", "Bearer sk_test_YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(string(data))
}import java.net.URI;
import java.net.http.*;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.biflus.com/v1/invoices"))
.header("Authorization", "Bearer sk_test_YOUR_API_KEY")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("{"client_id": "client_a8f291", "items": [{"item_id": "item_2e88ac", "quantity": 2}, {"title": "One-off setup fee", "unit_price": 50}]}"))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); require 'net/http'
require 'json'
uri = URI("https://api.biflus.com/v1/invoices")
req = Net::HTTP::Post.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
req['Content-Type'] = 'application/json'
req.body = '{"client_id": "client_a8f291", "items": [{"item_id": "item_2e88ac", "quantity": 2}, {"title": "One-off setup fee", "unit_price": 50}]}'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){ "data": { "_id": "inv_9a3d27", "invoice_number": "IPP/1028", "client": "client_a8f291", "status": "Draft", "issue_date": "2026-08-08T00:00:00.000Z", "due_date": "2026-08-15T00:00:00.000Z", "currency": "EUR", "subtotal": 350, "vat_amount": 0, "vat_percentage": 0, "total": 350, "items": ["li_7f2a10", "li_3c9b84"], "pdf_file_URL": "https://invoices.biflus.com/pdfs/…/IPP1028.pdf", "pdf_name": "IPP1028.pdf", "pdf_file_size": "136 KB" /* + the note fields printed on the PDF (how_to_pay_content, …) */ }, "line_items": [ { "title": "Website design", "quantity": 2, "unit": "Hour", "unit_price": 150, "vat_rate": 0, "subtotal": 300, "vat_amount": 0, "line_total": 300, "custom": false }, { "title": "One-off setup fee", "quantity": 1, "unit": null, "unit_price": 50, "vat_rate": 0, "subtotal": 50, "vat_amount": 0, "line_total": 50, "custom": true } ] }
// client_id missing, or items empty / a line with neither item_id nor title { "error": "items is required — at least one line item, each with either item_id or title" }
// client_id (or an item_id) doesn't exist, or isn't yours { "error": "Client not found" }
// a line title was flagged by the content-moderation check { "error": "This content couldn't be accepted." }
Update an invoice
Pass the full new list of line items, not just the ones changing. Old lines are replaced, not merged, and the PDF is regenerated automatically.
curl -X PATCH https://api.biflus.com/v1/invoices/inv_9a3d27 \
-H "Authorization: Bearer sk_test_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"items": [{"item_id": "item_2e88ac", "quantity": 3}]}'import requests
resp = requests.patch(
"https://api.biflus.com/v1/invoices/inv_9a3d27",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
json={"items": [{"item_id": "item_2e88ac", "quantity": 3}]},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/invoices/inv_9a3d27", {
method: "PATCH",
headers: {
"Authorization": "Bearer sk_test_YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({ items: [{ item_id: "item_2e88ac", quantity: 3 }] })
});
const data = await res.json();using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Patch, "https://api.biflus.com/v1/invoices/inv_9a3d27");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
request.Content = new StringContent("{"items": [{"item_id": "item_2e88ac", "quantity": 3}]}", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var data = await response.Content.ReadAsStringAsync();package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"items": [{"item_id": "item_2e88ac", "quantity": 3}]}`)
req, _ := http.NewRequest("PATCH", "https://api.biflus.com/v1/invoices/inv_9a3d27", body)
req.Header.Set("Authorization", "Bearer sk_test_YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(string(data))
}import java.net.URI;
import java.net.http.*;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.biflus.com/v1/invoices/inv_9a3d27"))
.header("Authorization", "Bearer sk_test_YOUR_API_KEY")
.header("Content-Type", "application/json")
.method("PATCH", HttpRequest.BodyPublishers.ofString("{"items": [{"item_id": "item_2e88ac", "quantity": 3}]}"))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); require 'net/http'
require 'json'
uri = URI("https://api.biflus.com/v1/invoices/inv_9a3d27")
req = Net::HTTP::Patch.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
req['Content-Type'] = 'application/json'
req.body = '{"items": [{"item_id": "item_2e88ac", "quantity": 3}]}'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){ "data": { "total": 300, "pdf_file_URL": "https://invoices.biflus.com/pdfs/…/IPP1028.pdf" /* … */ }, "line_items": [ /* full resolved line breakdown, quantities recalculated */ ] }
{ "error": "This invoice is already Sent, not Draft — line items and totals can only be edited while still a draft" }
Delete an invoice
Also removes the invoice's line items, its PDF, and its event log: a real delete, not a soft one.
curl -X DELETE https://api.biflus.com/v1/invoices/inv_9a3d27 \ -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests
resp = requests.delete(
"https://api.biflus.com/v1/invoices/inv_9a3d27",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/invoices/inv_9a3d27", {
method: "DELETE",
headers: { "Authorization": "Bearer sk_test_YOUR_API_KEY" }
});
const data = await res.json();using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.biflus.com/v1/invoices/inv_9a3d27");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
var response = await client.SendAsync(request);
var data = await response.Content.ReadAsStringAsync();package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://api.biflus.com/v1/invoices/inv_9a3d27", nil)
req.Header.Set("Authorization", "Bearer sk_test_YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(string(data))
}import java.net.URI;
import java.net.http.*;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.biflus.com/v1/invoices/inv_9a3d27"))
.header("Authorization", "Bearer sk_test_YOUR_API_KEY")
.method("DELETE", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); require 'net/http'
require 'json'
uri = URI("https://api.biflus.com/v1/invoices/inv_9a3d27")
req = Net::HTTP::Delete.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){ "deleted": true, "id": "inv_9a3d27" }
{ "error": "This invoice is already Paid, not Draft — only a still-Draft invoice can be deleted" }
Change invoice status
Status only moves forward: once an invoice leaves Draft it can't go back, and Paid/Canceled are final. Marking Paid also creates a payment record and generates a receipt PDF automatically. payment_method, payment_reference, and payment_notes are all optional.
curl -X POST https://api.biflus.com/v1/invoices/inv_9a3d27/status \
-H "Authorization: Bearer sk_test_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "Paid", "payment_method": "Bank Transfer", "payment_reference": "TX-12345"}'import requests
resp = requests.post(
"https://api.biflus.com/v1/invoices/inv_9a3d27/status",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
json={"status": "Paid", "payment_method": "Bank Transfer", "payment_reference": "TX-12345"},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/invoices/inv_9a3d27/status", {
method: "POST",
headers: {
"Authorization": "Bearer sk_test_YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
status: "Paid",
payment_method: "Bank Transfer",
payment_reference: "TX-12345"
})
});
const data = await res.json();using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.biflus.com/v1/invoices/inv_9a3d27/status");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
request.Content = new StringContent("{"status": "Paid", "payment_method": "Bank Transfer", "payment_reference": "TX-12345"}", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var data = await response.Content.ReadAsStringAsync();package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"status": "Paid", "payment_method": "Bank Transfer", "payment_reference": "TX-12345"}`)
req, _ := http.NewRequest("POST", "https://api.biflus.com/v1/invoices/inv_9a3d27/status", body)
req.Header.Set("Authorization", "Bearer sk_test_YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(string(data))
}import java.net.URI;
import java.net.http.*;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.biflus.com/v1/invoices/inv_9a3d27/status"))
.header("Authorization", "Bearer sk_test_YOUR_API_KEY")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("{"status": "Paid", "payment_method": "Bank Transfer", "payment_reference": "TX-12345"}"))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); require 'net/http'
require 'json'
uri = URI("https://api.biflus.com/v1/invoices/inv_9a3d27/status")
req = Net::HTTP::Post.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
req['Content-Type'] = 'application/json'
req.body = '{"status": "Paid", "payment_method": "Bank Transfer", "payment_reference": "TX-12345"}'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){ "data": { "status": "Paid", "payment_record": "pay_ff2c19" /* … */ }, "receipt_url": "https://invoices.biflus.com/pdfs/…/REC-IPP1030.pdf" }
{ "error": "This invoice is already marked Paid and cannot be changed" }