Invoices API
Create an invoice in BusinessDash from any external system — a time-tracker, a custom ordering flow, a back-office script. One authenticated POST creates the invoice, resolves (or creates) the bill-to contact, and derives the totals from the line items server-side.
Endpoint
POST https://www.businessdash.us/api/package/v1/invoices
Authorization: Bearer bd_…
Content-Type: application/json
Authentication
Bearer a secret API key (it starts with bd_) that holds the
invoices:write scope. This is an operator-grade write scope (it maps to
the invoices.manage permission), so it can only live on a secret key — never
a publishable browser token (those start with pk_ and are rejected for
write scopes).
Key formats: secret keys are
bd_…; publishable (browser) tokens arepk_…. There is nosk_format — pass thebd_…value straight through in theAuthorizationheader. Don't add a client-side prefix check beyond, at most, rejectingpk_….
Where to get the key: in the dashboard go to Site Builder → Developer
tab (direct link: /dashboard/site-builder?tab=developer, or search
"API keys" in the top search bar). Create a key in the Secret keys
section (not Publishable tokens), check the invoices:write scope,
and copy the bd_… secret — it's shown once.
Secret keys need no "Allowed origin" — that field belongs only to
publishable (pk_…) browser tokens, which are origin-locked because they ship
in page JavaScript. A server, script, or native/desktop app (which sends no
browser Origin) authenticates with the secret alone, so there's nothing to
enter there.
If the key is missing, invalid, or lacks the scope you get 401/403; a
malformed body is 400; a semantic failure (unknown contactId, no line
items) is 422.
Request body
{
// Bill-to — provide ONE of these:
"contactId": "…uuid…", // an existing contact, OR
"customer": { "email": "ari@acme.com", "name": "Ari Hecht" },
// resolved by email; created if new
"invoiceNumber": "INV-000123", // optional — auto INV-###### if omitted
"issueDate": "2026-08-09", // optional — defaults to today
"dueDate": "2026-09-08", // optional — defaults to issueDate
"taxRate": 8.875, // optional — percent
"notes": "Thanks for your business!", // shown ON the invoice
"internalNotes": "Net-30 per MSA", // NEVER shown to the customer
"paymentNotes": "Wire to acct …1234",
"lineItems": [
{
"description": "Project management — sprint 12",
"quantity": 3.5, // optional — default 1 (hours or count)
"unitPrice": 45.00, // DOLLARS per unit
"serviceDate": "2026-08-07" // optional
}
]
}
Field reference
| Field | Type | Required | Notes |
|---|---|---|---|
contactId | uuid | one of these two | Existing contact in your org. |
customer.email | string | one of these two | Matched to an existing contact by email; a new contact is created if none exists. |
customer.name | string | no | Used only when a contact is created. |
invoiceNumber | string | no | Falls back to the org's INV-###### sequence. |
issueDate | date | no | YYYY-MM-DD or ISO. Defaults to today (UTC). |
dueDate | date | no | Defaults to issueDate. |
taxRate | number | no | Percentage, e.g. 8.875. |
notes | string | no | Rendered on the invoice document. |
internalNotes | string | no | Private — never on the PDF, portal, or emails. |
paymentNotes | string | no | Rendered on the invoice. |
lineItems[].description | string | yes | The line's visible name. |
lineItems[].quantity | number | no | Default 1. Hours for a time line, count otherwise. |
lineItems[].unitPrice | number | yes | Dollars per unit. |
lineItems[].serviceDate | date | no | When the work was performed. |
The invoice is created as a draft (status: "draft"), unsent
(noSend: true). Money is stored internally in integer cents; you send and
read dollars. Totals — subtotal, tax, and the amount due — are computed from
the line items on the server, so a caller can neither skip nor skew them.
Response
{
"invoice": {
"id": "…uuid…",
"invoiceNumber": "INV-000123",
"contactId": "…uuid…",
"status": "draft",
"totalAmountCents": 16537
}
}
Example
curl -X POST https://www.businessdash.us/api/package/v1/invoices \
-H "Authorization: Bearer bd_xxx" \
-H "Content-Type: application/json" \
-d '{
"customer": { "email": "ari@acme.com", "name": "Ari Hecht" },
"dueDate": "2026-09-08",
"taxRate": 8.875,
"lineItems": [
{ "description": "Consulting", "quantity": 3.5, "unitPrice": 45.00, "serviceDate": "2026-08-07" },
{ "description": "Materials passthrough", "unitPrice": 120.00 }
]
}'
Notes & limits
- Up to 200 line items per request.
- Re-posting the same
invoiceNumberis rejected if it's already in use — use your own idempotency (uniqueinvoiceNumberper source record) to make retries safe. - The created invoice appears immediately in CRM → Invoices, editable in the Invoice Builder, and can be sent, paid, and PDF-exported like any other.