B
Biflus Docs
biflus.com Get API key

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:

1

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.

2

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.

3

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.

4

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)Required
itemsarrayRequired

At least one line. Each: item_id or title, plus optional quantity (default 1), unit_price, vat_rate.

issue_date, due_datedate string

Default 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 state

The only state where line items can still change. Editable via PATCH, deletable via DELETE. Nothing has been sent to the client yet.

Sentfrom Draft only

Line items and totals are frozen from here on — PATCH now returns a 409. The invoice is considered delivered to the client.

Paidterminal

From 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.

Canceledterminal

From 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_numberstring

Your real numbered reference, e.g. IPP/1028 — account prefix + sequence.

clientstring (id)

The client this invoice bills, as a client_… id.

statusDraft | Sent | Paid | Canceled

Change it via the status endpoint, never by writing this field.

issue_date, due_datedate
currencystring

From your account default, e.g. EUR.

subtotal, vat_amount, vat_percentage, totalnumber

All computed server-side from the resolved lines.

itemsarray of ids

The 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_sizestring

The 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_numberstring

Used 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 · boolean

The note blocks printed on the PDF, each a _content + _enabled pair copied from your account defaults at creation.

company, Created Date, Modified Dateid · date
Best practices
  • 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_reference when 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/:id for status changes — you'll hear about a payment the instant it happens.
Common mistakes
  • 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 PATCH after an invoice is Sent expecting a silent no-op. It returns a 409, not a partial success — check status first, or just catch the 409.
  • Don't forget PATCH replaces 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

GET/v1/invoices available Paginated
limitquery param, number

Default 25, max 100.

cursorquery param, string

From 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)
Response
{
  "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

GET/v1/invoices/:id available Fetch one 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)
Response
{
  "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

POST/v1/invoices available Creates a Draft invoice + PDF

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)Required

A client id (client_…). The client's raw internal id works too.

itemsbody, arrayRequired

At least one line-item object (see below).

issue_datebody, date

Defaults to today. Any date string works, e.g. "2026-08-08".

due_datebody, date

Defaults 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.

titlestring

Makes this a one-off line with no catalog item. Required when item_id is omitted.

quantitynumber

Defaults to 1.

unit_pricenumber

Overrides the price for this one line. A catalog line defaults to the item's default_price; a one-off defaults to 0.

vat_ratenumber

Percentage, e.g. 21. A catalog line defaults to the item's own VAT rate; a one-off defaults to 0.

unitstring

e.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)
Response
{
  "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

PATCH/v1/invoices/:id available Edit line items — only while still Draft

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)
Response
{
  "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

DELETE/v1/invoices/:id available Only while still Draft

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)
Response
{
  "deleted": true,
  "id": "inv_9a3d27"
}
{
  "error": "This invoice is already Paid, not Draft — only a still-Draft invoice can be deleted"
}

Change invoice status

POST/v1/invoices/:id/status available Mark Sent / Paid / Canceled

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)
Response
{
  "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"
}
This page is updated as the API changes. If something here doesn't match what you're seeing, that's a bug worth flagging to the Biflus team.