Skip to content

Bills

Bills represent amounts owed to vendors. They follow a lifecycle: draftposted → optionally void.

Only draft bills can be modified. Posted bills are immutable.

GET /api/books/bills

Scope: read

ParameterTypeDescription
statusstringFilter: draft, posted, void
contact_idintegerFilter by vendor contact
fromdateIssue date from (inclusive)
todateIssue date to (inclusive)
needs_completionbooleanOnly draft bills missing line accounts
limitintegerRecords per page (default 25, max 100)
afterstringPagination cursor
{
"bills": [
{
"id": 1,
"bill_number": "BILL-00001",
"status": "draft",
"contact_id": 42,
"contact_name": "Acme Materials",
"issue_date": "2026-03-01",
"due_date": "2026-04-01",
"currency": "USD",
"subtotal_cents": 109500,
"tax_cents": 0,
"total_cents": 109500,
"amount_paid_cents": 0,
"balance_due_cents": 109500,
"posted_at": null,
"voided_at": null,
"notes": null,
"line_count": 1,
"has_missing_accounts": false,
"attachment_count": 1,
"created_at": "2026-03-01T12:00:00Z",
"updated_at": "2026-03-01T12:00:00Z"
}
]
}

GET /api/books/bills/:id

Scope: read

Returns the bill with line items, payment applications, and debit note applications:

{
"bill": {
"id": 1,
"bill_number": "BILL-00001",
"status": "draft",
"contact_id": 42,
"contact_name": "Acme Materials",
"issue_date": "2026-03-01",
"due_date": "2026-04-01",
"currency": "USD",
"subtotal_cents": 109500,
"tax_cents": 0,
"total_cents": 109500,
"amount_paid_cents": 0,
"balance_due_cents": 109500,
"posted_at": null,
"voided_at": null,
"notes": null,
"line_count": 1,
"has_missing_accounts": false,
"attachment_count": 1,
"invoice_document_id": 5,
"bill_lines": [
{
"id": 10,
"position": 0,
"account_id": 301,
"description": "Excavator rental — March 2026",
"quantity": 1.0,
"unit_price_cents": 109500,
"amount_cents": 109500
}
],
"bill_applications": [],
"debit_applications": [],
"created_at": "2026-03-01T12:00:00Z",
"updated_at": "2026-03-01T12:00:00Z"
}
}
FieldTypeDescription
idintegerUnique identifier
bill_numberstringAuto-generated bill number
statusstringdraft, posted, or void
contact_idintegerVendor contact ID
contact_namestringVendor name
issue_datedateBill date
due_datedatePayment due date
currencystring3-letter currency code
subtotal_centsintegerSubtotal before tax
tax_centsintegerTax amount
total_centsintegerTotal after tax
amount_paid_centsintegerAmount paid via vendor payments
balance_due_centsintegerRemaining balance
posted_attimestampWhen the bill was posted (null if draft)
voided_attimestampWhen the bill was voided (null if not voided)
invoice_document_idintegerLinked invoice document (if created from inbox)
notesstringFree-text notes

POST /api/books/bills

Scope: write

{
"bill": {
"contact_id": 42,
"issue_date": "2026-03-12",
"due_date": "2026-04-12",
"currency": "USD",
"tax_cents": 0,
"notes": "March equipment rental",
"bill_lines_attributes": [
{
"description": "Excavator rental — March 2026",
"quantity": 1,
"unit_price_cents": 109500,
"account_id": 301,
"position": 0
}
]
}
}
FieldTypeRequiredDescription
contact_idintegeryesVendor contact ID
issue_datedateyesBill date
due_datedateyesPayment due date
currencystringnoDefaults to entity currency
tax_centsintegernoTax amount
notesstringnoNotes
bill_lines_attributesarraynoLine items (see below)
FieldTypeRequiredDescription
descriptionstringyesLine description
quantitynumberyesQuantity (supports decimals)
unit_price_centsintegeryesUnit price in cents
account_idintegernoExpense account ID
positionintegernoDisplay order

Returns: 201 Created

Totals are automatically calculated from line items.


PATCH /api/books/bills/:id

Scope: write

Only draft bills can be updated. Returns 409 Conflict if the bill is posted or voided.

Same parameters as Create. Only provided fields are updated. To manage line items, include bill_lines_attributes with id for existing lines or _destroy: true to remove.


POST /api/books/bills/:id/post_bill

Scope: write

Transitions the bill from draft to posted. Creates the corresponding journal entry in the general ledger (debit expense account, credit accounts payable).

Returns 422 if:

  • Bill is not in draft status
  • Issue date falls in a closed period
  • Line items are missing expense accounts

POST /api/books/bills/:id/void

Scope: write

Voids a posted bill and reverses its journal entry. The bill must have no payment or debit note applications — unapply those first.

Returns 409 Conflict if the bill is not posted or has applications.


DELETE /api/books/bills/:id

Scope: write

Permanently deletes a draft bill. Returns 409 Conflict if the bill is not in draft status.

Returns: 200 OK with { "success": true }


POST /api/books/bills/bulk_assign_account

Scope: write

Assigns an expense account to all lines missing one across multiple draft bills. Useful for batch processing inbox-converted bills.

{
"bill_ids": [1, 2, 3],
"account_id": 301
}

Returns: { "updated": 3 }