Items
Your catalog: the things you sell, used as invoice line items. An item's price and VAT are just defaults — any invoice line can override them per line.
Fields you can set
These are the fields you can send on create and update. An item also carries read-only fields the system fills in.
namestringRequireddefault_pricenumberRequired**Unless no_price_yet: true is sent instead.
description, skustringunitenumHour, Day, Piece, Session, Project, Kilogram, Liter, … (21 total)
iconenumDefaults to "boxes". 54 valid values (rocket, dog, gauge, …).
categorystring (id)A category id from Categories.
vat_ratenumber (%)e.g. 19. Resolved to (or creates) a matching tax rate for your company.
active, track_stock_quantitybooleanstock_quantitynumberSetting this on create also sets the item's starting stock snapshot; on update it only updates the current count.
Read-only fields
Returned on every item but managed by the system or derived from the fields above — you can't set them directly.
_idstring (id)The item's public id, e.g. item_2e88ac.
companystring (id)Your company — scoping is automatic.
default_vat_ratestring (id)The tax-rate record your vat_rate resolved to. Send vat_rate to change it.
display_vat_percentage, display_unitstringPreformatted strings the app renders, derived from the fields above.
Initial_stock_qtynumberThe starting stock snapshot, captured once from the first stock_quantity you send.
flaggedbooleanSet by the system's content check; you can't write it.
Created Date, Modified DatedateISO timestamps maintained automatically.
- Send a plain percentage to
vat_rate(e.g.19) — don't try to look up or pass a tax-rate id yourself, it's resolved for you. - Use
no_price_yet: truefor items you're still pricing rather than sending a placeholder0, which reads as "this item is free" everywhere else it's used. - If a 400 comes back on
iconorunit, readvalid_optionsfrom the response — it's the live list, safer than a hardcoded copy.
- Don't rely on delete to retire an item. Unlike clients and categories,
DELETEon an item is never blocked by usage — if you want it to disappear from new invoices but stay valid for reference, setactive: falseinstead. - Don't expect updating
stock_quantityto reset the starting snapshot.Initial_stock_qtyis only ever set on create; a laterstock_quantityupdate changes the current count, not the baseline. - Don't pass a
categoryid you haven't verified belongs to your company. It's checked as a real id shape, but not ownership at write time in the same pass — fetch it via Categories first if you're not sure.
List items
Same shape as every other list endpoint. See Pagination.
limitquery param, numberDefault 25, max 100.
cursorquery param, stringFrom the previous page's pagination.next_cursor.
curl https://api.biflus.com/v1/items?limit=2 \ -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests
resp = requests.get(
"https://api.biflus.com/v1/items",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
params={"limit": 2},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/items?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/items?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/items?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/items?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/items?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": "item_2e88ac", "name": "Consulting Hour", "default_price": 150 /* … */ }, { "_id": "item_71fc0b", "name": "Onboarding Kit", "default_price": 40 /* … */ } ], "pagination": { "count": 2, "remaining": 11, "next_cursor": "Mg==" } }
Get an item
idpath param, stringRequiredThe item's _id.
curl https://api.biflus.com/v1/items/item_2e88ac \ -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests
resp = requests.get(
"https://api.biflus.com/v1/items/item_2e88ac",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/items/item_2e88ac", {
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/items/item_2e88ac");
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/items/item_2e88ac", 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/items/item_2e88ac"))
.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/items/item_2e88ac")
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": "item_2e88ac", "name": "Consulting Hour", "default_price": 150, "unit": "Hour", "icon": "boxes", "active": true, "default_vat_rate": "vat_5c14e8" } }
{ "error": "item not found" }
Create an item
icon and unit are validated server-side against fixed enums — an invalid value gets a 400 back with the full valid_options list, not a silent fallback.
curl -X POST https://api.biflus.com/v1/items \
-H "Authorization: Bearer sk_test_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Consulting Hour", "default_price": 150, "unit": "Hour", "vat_rate": 19}'import requests
resp = requests.post(
"https://api.biflus.com/v1/items",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
json={"name": "Consulting Hour", "default_price": 150, "unit": "Hour", "vat_rate": 19},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/items", {
method: "POST",
headers: {
"Authorization": "Bearer sk_test_YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({ name: "Consulting Hour", default_price: 150, unit: "Hour", vat_rate: 19 })
});
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/items");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
request.Content = new StringContent("{"name": "Consulting Hour", "default_price": 150, "unit": "Hour", "vat_rate": 19}", 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(`{"name": "Consulting Hour", "default_price": 150, "unit": "Hour", "vat_rate": 19}`)
req, _ := http.NewRequest("POST", "https://api.biflus.com/v1/items", 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/items"))
.header("Authorization", "Bearer sk_test_YOUR_API_KEY")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("{"name": "Consulting Hour", "default_price": 150, "unit": "Hour", "vat_rate": 19}"))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); require 'net/http'
require 'json'
uri = URI("https://api.biflus.com/v1/items")
req = Net::HTTP::Post.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
req['Content-Type'] = 'application/json'
req.body = '{"name": "Consulting Hour", "default_price": 150, "unit": "Hour", "vat_rate": 19}'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){ "data": { "_id": "item_2e88ac", "name": "Consulting Hour", "default_price": 150, "unit": "Hour", "icon": "boxes", "default_vat_rate": "vat_5c14e8" } }
{ "error": "\"cyan\" is not a valid icon", "valid_options": [ "disc-3", "boxes", "rocket", … 51 more ] }
Update an item
vat_rate is a percentage in, a resolved default_vat_rate id out; it finds a matching tax rate for your company or creates one.
curl -X PATCH https://api.biflus.com/v1/items/item_2e88ac \
-H "Authorization: Bearer sk_test_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"default_price": 175, "vat_rate": 21}'import requests
resp = requests.patch(
"https://api.biflus.com/v1/items/item_2e88ac",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
json={"default_price": 175, "vat_rate": 21},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/items/item_2e88ac", {
method: "PATCH",
headers: {
"Authorization": "Bearer sk_test_YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({ default_price: 175, vat_rate: 21 })
});
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/items/item_2e88ac");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
request.Content = new StringContent("{"default_price": 175, "vat_rate": 21}", 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(`{"default_price": 175, "vat_rate": 21}`)
req, _ := http.NewRequest("PATCH", "https://api.biflus.com/v1/items/item_2e88ac", 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/items/item_2e88ac"))
.header("Authorization", "Bearer sk_test_YOUR_API_KEY")
.header("Content-Type", "application/json")
.method("PATCH", HttpRequest.BodyPublishers.ofString("{"default_price": 175, "vat_rate": 21}"))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); require 'net/http'
require 'json'
uri = URI("https://api.biflus.com/v1/items/item_2e88ac")
req = Net::HTTP::Patch.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
req['Content-Type'] = 'application/json'
req.body = '{"default_price": 175, "vat_rate": 21}'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){ "data": { "_id": "item_2e88ac", "name": "Consulting Hour", "default_price": 175, "default_vat_rate": "vat_d802f1" /* … unchanged fields */ } }
Delete an item
Unlike clients and categories, deleting an item is never blocked by usage — invoices already store a frozen copy of the line, not a live reference. If you want it gone from new invoices but still valid historically, prefer active: false.
curl -X DELETE https://api.biflus.com/v1/items/item_2e88ac \ -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests
resp = requests.delete(
"https://api.biflus.com/v1/items/item_2e88ac",
headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
)
print(resp.json())const res = await fetch("https://api.biflus.com/v1/items/item_2e88ac", {
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/items/item_2e88ac");
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/items/item_2e88ac", 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/items/item_2e88ac"))
.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/items/item_2e88ac")
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": "item_2e88ac" }