◷ In Development

BizBooks Pro API & Webhooks

Read and write your accounting data over REST, and receive signed events when things change. This documents the interface as it is being built — see the availability note below before you plan around it.

Availability: the API and webhooks are implemented and under active testing, but are not yet switched on in a released build. This page is published so you can review the design and tell us if it will not work for you. Want a heads-up the day it ships? Submit the form on the Integrations page.

On this page

Authentication

Every request is authenticated with an API key sent as a bearer token. Keys are created inside the app under Settings → Integrations → API Keys by an admin or manager.

curl https://your-install.local:3000/api/customers \
  -H "Authorization: Bearer bb_live_7fa39c21e4b85d06f1c2a930"

An X-API-Key header is accepted as an alternative if bearer tokens are awkward in your client.

A key is bound to one company. Multi-company installs issue a separate key per company, and the key decides which books it touches. There is no company header — sending one that disagrees with the key returns 403.

The full key is shown exactly once, at creation. We store only a SHA-256 hash plus a short display prefix, so we cannot recover it for you. Lost it? Revoke that key and issue another.

Because BizBooks Pro is desktop software, the base URL is your own installation, not a service we host. On the same machine that is http://localhost:3000. Reaching it from elsewhere on your network is a decision you make deliberately — see the security note at the end.

Scopes

Each key carries scopes. Grant the narrowest that does the job.

ScopeGrants
readGET on every supported resource.
writeEverything read allows, plus POST and PUT.

A key without the required scope gets 403 with a message naming the scope it needed.

Endpoints

API keys can only reach the endpoints below. This is an explicit allowlist, not a filter — an endpoint existing elsewhere in the app does not make it reachable with a key, and adding one to the public surface is a deliberate decision on our side.

MethodPathScope
GET/api/customersread
GET/api/customers/:idread
POST/api/customerswrite
PUT/api/customers/:idwrite
GET/api/vendorsread
POST/api/vendorswrite
GET/api/invoicesread
GET/api/invoices/:idread
POST/api/invoiceswrite
PUT/api/invoices/:idwrite
GET/api/billsread
POST/api/billswrite
GET/api/accountsread
GET/api/itemsread
GET/api/estimatesread
GET/api/transactionsread
POST/api/transactionswrite

Creating a customer

curl -X POST http://localhost:3000/api/customers \
  -H "Authorization: Bearer bb_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Riverside Dental",
    "email": "ap@riversidedental.example",
    "phone": "555-0142",
    "city": "Boise",
    "state": "ID"
  }'

Creating an invoice

Invoices post to the general ledger exactly as they would if you had keyed them in, including tax and multi-currency handling. A draft invoice does not post until it is issued.

curl -X POST http://localhost:3000/api/invoices \
  -H "Authorization: Bearer bb_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": 42,
    "invoice_date": "2026-08-04",
    "due_date": "2026-09-03",
    "status": "sent",
    "items": [
      { "description": "Consulting - August", "quantity": 12, "unit_price": 145.00 }
    ]
  }'

Request conventions

Errors

Errors use standard status codes with a JSON body carrying an error string written for a human reading a log.

StatusMeaning
400The request was malformed or failed validation.
401Key missing, invalid, revoked or expired.
403Key is valid but lacks the scope, or the endpoint is not on the allowlist.
404No such record in this company.
429Rate limited. Back off and retry.
500Our fault. Safe to retry an idempotent request.

The default rate limit is 100 requests per 15 minutes per IP, matching the rest of the application.

Webhooks

Rather than polling, register an endpoint and we will POST to it when something happens. Add endpoints under Settings → Integrations → Webhooks.

EventFires when
invoice.createdAn invoice is created, by any route.
invoice.updatedAn existing invoice is modified.
invoice.paidAn invoice is settled in full.
customer.createdA customer record is added.
customer.updatedA customer record changes.
vendor.createdA vendor record is added.
bill.createdA bill is entered.
payment.receivedA customer payment is recorded.
transaction.createdA journal entry is posted.

Subscribe to * to receive everything, including events added later.

POST /your-endpoint
X-BizBooks-Event: invoice.created
X-BizBooks-Event-Id: evt_9c1f04ab77e2
X-BizBooks-Timestamp: 1786000000
X-BizBooks-Signature: 4f1c...<64 hex chars>

{
  "id": "evt_9c1f04ab77e2",
  "type": "invoice.created",
  "created_at": "2026-08-04T15:12:09.441Z",
  "data": {
    "id": 1180,
    "invoice_number": "INV-1042",
    "customer_id": 42,
    "total_amount": "1740.00",
    "status": "sent"
  }
}

Verifying signatures

Every delivery is signed with the secret shown once when you created the endpoint. Verify it before trusting the payload — otherwise anyone who learns your URL can post fake accounting events at you.

Sign the string {timestamp}.{raw body} with HMAC-SHA256 and compare to the header:

const crypto = require('crypto');

function verify(req, rawBody, secret) {
  const timestamp = req.headers['x-bizbooks-timestamp'];
  const signature = req.headers['x-bizbooks-signature'];

  // Reject anything older than five minutes to blunt replay attacks.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature, 'utf8'),
    Buffer.from(expected, 'utf8')
  );
}

Sign the raw body, not a re-serialized object. Parsing the JSON and stringifying it again reorders keys and changes whitespace, and the signature will never match. Capture the body as a string first.

Delivery & retries

Zapier & Make

Both are being built on this API. Triggers will cover new invoice, invoice paid, new customer and new bill; actions will cover creating invoices and customers and recording payments. If you would rather not wait, the API and webhooks above give you everything those apps use.

A note on exposure

BizBooks Pro runs on your hardware, so an API key is only useful to something that can reach your machine. That is a genuine security advantage — there is no public endpoint for an attacker to find. It also means that if you want an outside service to call in, opening that path is your decision, and you should treat an API key with the same care as the login it acts on behalf of.

Questions, or something here that will not work for your use case? Tell us — this is being finalized now, which is exactly when feedback is worth most.