B
Biflus Docs
biflus.com Get API key
API ReferenceCategories

Categories

Groups for organizing items. Purely organizational — a category has no effect on pricing, tax, or invoice totals.

Fields you can set

A category is deliberately small — three settable fields, two of which fill themselves in.

namestringRequired
colorstring

A color name. Auto-assigned from Blue, Amber, Violet, Green, Cyan, Pink, or Orange if omitted.

key_letterstring

The 2-letter chip shown in the app. Auto-derived from name (uppercased) if omitted.

Read-only fields

Returned on every category but set by the system.

_idstring (id)

The category's public id, e.g. cat_4b91fa. Pass it as an item's category.

companystring (id)

Your company — scoping is automatic.

Created Date, Modified Datedate

ISO timestamps maintained automatically.

Best practices
  • Leave key_letter unset the first time — the auto-derived initials usually look right, and you can always PATCH it later if two categories collide.
  • Create categories before the items that reference them — item.category needs a real id, so there's no "create both at once" shortcut.
  • Keep the list short and stable. Categories are meant to be a handful of durable groupings, not a tag per item — if you find yourself creating one per item, an item field like sku is probably the better fit.
Common mistakes
  • Don't delete a category before reassigning its items. Delete is blocked (409) while any item still references it — there's no cascade or force-delete option, by design.
  • Don't assume key_letter is unique. Nothing enforces it — two categories can end up with the same two-letter chip if you let both auto-derive from similar names.
  • Don't build a "category per client" or "category per invoice" pattern. Categories only ever attach to items — there's no client- or invoice-level equivalent.

List categories

GET/v1/categories 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/categories \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests

resp = requests.get(
    "https://api.biflus.com/v1/categories",
    headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
)
print(resp.json())
const res = await fetch("https://api.biflus.com/v1/categories", {
  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/categories");
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/categories", 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/categories"))
    .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/categories")
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": "cat_4b91fa",
      "name": "General Services",
      "key_letter": "GS",
      "color": "Violet"
    }
  ],
  "pagination": {
    "count": 1,
    "remaining": 0,
    "next_cursor": null
  }
}

Get a category

GET/v1/categories/:id available Fetch one category
idpath param, stringRequired

The category's _id.

curl https://api.biflus.com/v1/categories/cat_4b91fa \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests

resp = requests.get(
    "https://api.biflus.com/v1/categories/cat_4b91fa",
    headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
)
print(resp.json())
const res = await fetch("https://api.biflus.com/v1/categories/cat_4b91fa", {
  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/categories/cat_4b91fa");
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/categories/cat_4b91fa", 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/categories/cat_4b91fa"))
    .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/categories/cat_4b91fa")
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": "cat_4b91fa",
    "name": "General Services",
    "key_letter": "GS",
    "color": "Violet"
  }
}
{
  "error": "itemscategory not found"
}

Create a category

POST/v1/categories available Create a category

key_letter defaults to the first letters of each word in name ("General Services" → "GS"), or the first two letters of a single word.

curl -X POST https://api.biflus.com/v1/categories \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "General Services"}'
import requests

resp = requests.post(
    "https://api.biflus.com/v1/categories",
    headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
    json={"name": "General Services"},
)
print(resp.json())
const res = await fetch("https://api.biflus.com/v1/categories", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_test_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ name: "General Services" })
});
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/categories");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
request.Content = new StringContent("{"name": "General Services"}", 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": "General Services"}`)
	req, _ := http.NewRequest("POST", "https://api.biflus.com/v1/categories", 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/categories"))
    .header("Authorization", "Bearer sk_test_YOUR_API_KEY")
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("{"name": "General Services"}"))
    .build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
require 'net/http'
require 'json'

uri = URI("https://api.biflus.com/v1/categories")
req = Net::HTTP::Post.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
req['Content-Type'] = 'application/json'
req.body = '{"name": "General Services"}'

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
Response
{
  "data": {
    "_id": "cat_4b91fa",
    "name": "General Services",
    "key_letter": "GS",
    "color": "Violet"
  }
}

Update a category

PATCH/v1/categories/:id available Partial update

Only send the field(s) you're changing — name, color, and key_letter can each be updated independently.

curl -X PATCH https://api.biflus.com/v1/categories/cat_4b91fa \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Professional Services"}'
import requests

resp = requests.patch(
    "https://api.biflus.com/v1/categories/cat_4b91fa",
    headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
    json={"name": "Professional Services"},
)
print(resp.json())
const res = await fetch("https://api.biflus.com/v1/categories/cat_4b91fa", {
  method: "PATCH",
  headers: {
    "Authorization": "Bearer sk_test_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ name: "Professional Services" })
});
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/categories/cat_4b91fa");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sk_test_YOUR_API_KEY");
request.Content = new StringContent("{"name": "Professional Services"}", 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": "Professional Services"}`)
	req, _ := http.NewRequest("PATCH", "https://api.biflus.com/v1/categories/cat_4b91fa", 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/categories/cat_4b91fa"))
    .header("Authorization", "Bearer sk_test_YOUR_API_KEY")
    .header("Content-Type", "application/json")
    .method("PATCH", HttpRequest.BodyPublishers.ofString("{"name": "Professional Services"}"))
    .build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
require 'net/http'
require 'json'

uri = URI("https://api.biflus.com/v1/categories/cat_4b91fa")
req = Net::HTTP::Patch.new(uri)
req['Authorization'] = 'Bearer sk_test_YOUR_API_KEY'
req['Content-Type'] = 'application/json'
req.body = '{"name": "Professional Services"}'

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
Response
{
  "data": {
    "_id": "cat_4b91fa",
    "name": "Professional Services",
    "key_letter": "GS",
    "color": "Violet"
  }
}

Delete a category

DELETE/v1/categories/:id available Blocked if any item still uses this category

Reassign or delete the items using this category first.

curl -X DELETE https://api.biflus.com/v1/categories/cat_4b91fa \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY"
import requests

resp = requests.delete(
    "https://api.biflus.com/v1/categories/cat_4b91fa",
    headers={"Authorization": "Bearer sk_test_YOUR_API_KEY"},
)
print(resp.json())
const res = await fetch("https://api.biflus.com/v1/categories/cat_4b91fa", {
  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/categories/cat_4b91fa");
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/categories/cat_4b91fa", 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/categories/cat_4b91fa"))
    .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/categories/cat_4b91fa")
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": "cat_4b91fa"
}
{
  "error": "4 item(s) still use this category — reassign them to a different category first"
}
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.