API ReferenceQuick start
Quick start
The whole API follows one pattern, so seeing it once tells you almost everything.
1
Get an API key
Go to Settings → API keys in your Biflus account and generate one. See Authentication.
2
Make a request
Send it as a bearer token. Here's listing your clients:
curl https://api.biflus.com/v1/clients \ -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests
resp = requests.get(
"https://api.biflus.com/v1/clients",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/clients", {
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/clients");
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/clients", 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/clients"))
.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/clients")
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)3
Read the response
Every list comes back the same shape: a data array plus a pagination object.
{ "data": [ { "_id": "client_a8f291", "company_name": "Acme Corp", "type": "Company" /* … */ } ], "pagination": { "count": 1, "remaining": 0, "next_cursor": null } }
Every other resource works the same way:
GET to list, GET .../:id
to fetch one, POST to create,
PATCH to update,
DELETE to remove. If you know one, you know them all.
Best practices
- Build against
sk_test_first. Test and live keys hit completely separate data, so there's no risk of a bad request touching real invoices while you're still wiring things up. - Log the
errorfield on every non-2xx response while you integrate — it's written to be read directly, not just for us. - Cache
valid_optionsfrom a 400 once (icons, units) instead of hardcoding the list — it's returned fresh from the same source of truth the app uses.
Common mistakes
- Don't guess at field names. Every resource page lists exactly which fields are writable — sending an unlisted field is silently ignored, not rejected, which is easy to mistake for a bug on our end.
- Don't poll for invoice status changes. Use a webhook instead — it's less code and you'll hear about a payment the moment it happens, not up to a minute later.
- Don't skip the
pagination.next_cursorcheck. Assuming a list response is complete because it "looked like everything" is the most common cause of missing records in an integration.