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 are pk_…. There is no sk_ format — pass the bd_… value straight through in the Authorization header. Don't add a client-side prefix check beyond, at most, rejecting pk_….

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

FieldTypeRequiredNotes
contactIduuidone of these twoExisting contact in your org.
customer.emailstringone of these twoMatched to an existing contact by email; a new contact is created if none exists.
customer.namestringnoUsed only when a contact is created.
invoiceNumberstringnoFalls back to the org's INV-###### sequence.
issueDatedatenoYYYY-MM-DD or ISO. Defaults to today (UTC).
dueDatedatenoDefaults to issueDate.
taxRatenumbernoPercentage, e.g. 8.875.
notesstringnoRendered on the invoice document.
internalNotesstringnoPrivate — never on the PDF, portal, or emails.
paymentNotesstringnoRendered on the invoice.
lineItems[].descriptionstringyesThe line's visible name.
lineItems[].quantitynumbernoDefault 1. Hours for a time line, count otherwise.
lineItems[].unitPricenumberyesDollars per unit.
lineItems[].serviceDatedatenoWhen 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 invoiceNumber is rejected if it's already in use — use your own idempotency (unique invoiceNumber per 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.