FOSSBilling provides a REST API for integrating external systems and building custom functionality on top of the platform.
API Endpoints
Section titled “API Endpoints”Three main entrypoints:
| Endpoint | Access Level | Purpose |
|---|---|---|
/api/admin/* | Admin only | System management |
/api/client/* | Authenticated clients | Client services |
/api/guest/* | Public | No authentication needed |
Request Format
Section titled “Request Format”- Method: Core API routes generally accept GET and POST. Guest client login/signup and staff login require POST. Use POST for mutations and requests with a JSON body or form data.
- Format: JSON responses. POST requests may send JSON or form data.
- Naming: Lowercase with underscores (
change_password) - Nulls: Blank fields are included as
null, not omitted - Dates: ISO 8601 format
Authentication
Section titled “Authentication”Use HTTP Basic Auth with base64 encoding:
- Username:
adminorclient(depending on endpoint) - Password: Your API key (found in client profile or admin dashboard)
Authorization: Basic base64_encode('admin:YOUR_API_KEY')Most HTTP clients (curl, Postman) handle the encoding automatically.
Browser requests from an active client or admin session use the session cookie instead. Those requests must include a valid CSRF token.
CSRF Tokens
Section titled “CSRF Tokens”CSRF protection applies to session-authenticated client and admin browser API calls. It also applies to guest client login/signup, staff login, and cart mutations. External client and admin calls authenticated with API keys do not require CSRF tokens.
For browser calls, FOSSBilling accepts the token in any of these places:
CSRFTokenin the JSON bodyCSRFTokenin form data (query parameters are also accepted except for guest authentication)X-CSRF-Tokenrequest header
The token must match the server-side session token. For session-authenticated client and admin calls, a nonempty CSRF cookie must match it too. The bundled JavaScript API wrapper reads the fossbilling_csrf cookie and adds the token automatically to same-origin requests. Sending a cookie without a token in the request is insufficient.
Guest Authentication and Cart Calls
Section titled “Guest Authentication and Cart Calls”Load a FOSSBilling page first to establish the session, then retain the session cookie and submit its token. These guest endpoints require a session-bound CSRF token:
client/login,client/create, andstaff/login: POST only, with the token in the JSON/form body orX-CSRF-Tokenheader.cart/add_item,cart/remove_item,cart/apply_promo,cart/remove_promo,cart/reset, andcart/set_currency: GET or POST, with the token in the request. Prefer POST for mutations.
These checks apply even when api.CSRFPrevention is disabled. A supplied Origin must match the configured FOSSBilling origin, and Sec-Fetch-Site: cross-site is rejected. Serve custom browser forms from the same origin. Server-side guest integrations must retain the session and submit its token too.
Other guest calls are not subject to this authentication/cart CSRF check. See the migration guide for migration details.
Use Twig helpers for theme and module templates. This way, you don't need to manually attach CSRF tokens to the requests:
<form action="{{ 'profile/update'|api_url }}" {{ fb_api_form({ message: 'Saved'|trans }) }}> <input type="text" name="first_name"> <button type="submit">{{ 'Save'|trans }}</button></form>For raw fetch() requests, send the token yourself:
const token = document.cookie.match(/fossbilling_csrf=([^;]*)/)?.[1] || '';
fetch('/api/client/profile/update', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': decodeURIComponent(token), }, body: JSON.stringify({ first_name: 'Jane' }),});Example Requests
Section titled “Example Requests”curl -X POST "https://example.com/api/admin/staff/create" \ -H "Authorization: Basic $(echo -n 'admin:YOUR_API_KEY' | base64)" \ -H "Content-Type: application/json" \ -d '{ "email": "hello@fossbilling.org", "password": "Testing123+", "name": "John Doe", "admin_group_id": 1, "status": "active" }'import requestsfrom requests.auth import HTTPBasicAuth
url = "https://example.com/api/admin/staff/create"api_key = "YOUR_API_KEY"
payload = { "email": "hello@fossbilling.org", "password": "Testing123+", "name": "John Doe", "admin_group_id": 1, "status": "active"}
auth = HTTPBasicAuth('admin', api_key)headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, auth=auth)print(response.json())Run admin API calls from trusted server-side code only. Never expose an admin API key in browser JavaScript.
const url = "https://example.com/api/admin/staff/create";const apiKey = "YOUR_API_KEY";
const payload = { email: "hello@fossbilling.org", password: "Testing123+", name: "John Doe", admin_group_id: 1, status: "active"};
const headers = new Headers();headers.set("Content-Type", "application/json");headers.set("Authorization", "Basic " + Buffer.from("admin:" + apiKey).toString("base64"));
fetch(url, { method: "POST", headers: headers, body: JSON.stringify(payload)}).then(response => response.json()).then(data => console.log(data)).catch(error => console.error("Error:", error));Response Format
Section titled “Response Format”Success:
{ "result": { ... }, "error": null}Error:
{ "result": null, "error": { "message": "Error description", "code": 123 }}HTTP Status and Error Handling
Section titled “HTTP Status and Error Handling”Error codes in the HTTP range 400–599 use that HTTP status, except application codes 413, 436, 437, and 498, which use HTTP 400. The original code stays in error.code. Application codes 201–206, 1002, and 1004 use HTTP 401; 701 and 879 use 400; and 740 uses 404. Other application errors can still return HTTP 200.
Read the JSON body on non-200 responses and check both the HTTP status and the error field. For example:
const response = await fetch('/api/guest/currency/get_pairs', { headers: { Accept: 'application/json' },});const data = await response.json();
if (data.error) { throw new Error(`${data.error.message} (code ${data.error.code})`);}if (!response.ok) { throw new Error(`HTTP ${response.status}`);}console.log(data.result);For HTTP libraries that throw on non-2xx responses, read the JSON body from the exception's response. Handle non-JSON proxy or web-server failures separately.
Common Patterns
Section titled “Common Patterns”Endpoint Structure
Section titled “Endpoint Structure”/api/{role}/{module}/{action}Examples:
/api/admin/client/get_list/api/client/profile/get/api/guest/system/periods
Pagination
Section titled “Pagination”List endpoints typically accept:
page— Page number (starting at 1)per_page— Items per page
Paginated results use the FOSSBilling\Paginator class.