Bills
Bills represent purchase transactions from vendors in Crane Ledger's accounts payable system. They track expenses, vendor payments, and automatically create corresponding accounting transactions. Bills are the counterpart to invoices for managing business expenses and purchases.
The Bill Object
/organizations/:organization_id/billsA bill represents a purchase transaction from a vendor.
{
"id": "bil_xxxxxxxxxxxxxxxx",
"object": "bill",
"organization_id": "org_xxxxxxxxxxxxxxxx",
"bill_number": "BILL-2024-001",
"order_number": "PO-12345",
"status": "draft",
"contact_id": "con_xxxxxxxxxxxxxxxx",
"contact_name": "Office Supplies Co",
"contact_email": "billing@officesupplies.com",
"contact_tax_number": "US987654321",
"currency_id": "CUR_USD",
"currency_rate": "1.0000",
"billed_at": "2024-01-10",
"due_at": "2024-02-09",
"amount": "1250.00",
"category_id": "cat_xxxxxxxxxxxxxxxx",
"notes": "Monthly office supplies order",
"parent_id": null,
"created_by": "usr_xxxxxxxxxxxxxxxx",
"created_at": "2024-01-10T14:30:00Z",
"updated_at": "2024-01-10T14:30:00Z",
"metadata": {
"payment_terms": "net_30",
"po_number": "PO-2024-001"
}
}
Attributes
| Attribute | Type | Description |
|---|---|---|
id | string | Unique identifier with bil_ prefix |
bill_number | string | Sequential bill number |
status | enum | Current status (draft, received, partial, paid, cancelled, overdue) |
contact_id | string | Vendor contact |
amount | string | Total bill amount before tax |
billed_at | date | Bill date |
due_at | date | Payment due date |
currency_id | string | Bill currency |
category_id | string | Bill category |
notes | string | Bill notes |
order_number | string | Purchase order reference |
Create Bill
/organizations/:organization_id/billsCreates a new purchase bill with line items, taxes, and vendor details.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organization_id | string | Yes | The ID of the organization |
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
bill_number | string | Yes | Unique bill number |
contact_id | string | Yes | Vendor contact ID |
billed_at | date | Yes | Bill date (YYYY-MM-DD) |
due_at | date | Yes | Payment due date (YYYY-MM-DD) |
category_id | string | No | Bill category |
notes | string | No | Bill notes |
order_number | string | No | Purchase order reference |
items | array | Yes | Line items array |
Line Items Structure
Each item in the items array:
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Item name/description |
sku | string | No | Stock keeping unit |
quantity | number | Yes | Quantity (decimal) |
price | number | Yes | Unit price |
tax_id | string | No | Tax rate to apply |
Response
{
"request_id": "req_yyyyyyyyyyyyyyyy",
"success": true,
"data": {
"status": "queued",
"estimated_completion": "2024-01-10T14:30:10Z",
"bill_preview": {
"number": "BILL-2024-001",
"total_amount": "1250.00",
"total_tax": "250.00",
"grand_total": "1500.00"
}
},
"error": null,
"duration_ms": 0,
"credit_cost": 5
}
List Bills
/organizations/:organization_id/billsReturns a list of bills for the organization with optional filtering.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organization_id | string | Yes | The organization ID |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | enum | Filter by status (draft, received, paid, etc.) |
contact_id | string | Filter by vendor contact |
limit | integer | Number of results (default: 20, max: 100) |
offset | integer | Pagination offset |
Response
{
"object": "list",
"data": [
{
"id": "bil_xxxxxxxxxxxxxxxx",
"bill_number": "BILL-2024-001",
"status": "paid",
"contact_name": "Office Supplies Co.",
"amount": "500.00",
"issued_at": "2024-01-10",
"due_at": "2024-02-10",
"created_at": "2024-01-10T09:15:00Z"
}
],
"has_more": false,
"total_count": 1
}
Get Bill
/organizations/:organization_id/bills/:idRetrieves the details of a specific bill including line items and taxes.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organization_id | string | Yes | The organization ID |
id | string | Yes | The bill ID |
Response
Returns the complete bill object with line items, taxes, and payment history.
Update Bill
/organizations/:organization_id/bills/:idUpdates an existing bill. Only draft bills can be modified.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organization_id | string | Yes | The organization ID |
id | string | Yes | The bill ID |
Request Body
Same structure as Create Bill, but all fields are optional.
Delete Bill
/organizations/:organization_id/bills/:idDeletes a draft bill. Received or paid bills cannot be deleted.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organization_id | string | Yes | The organization ID |
id | string | Yes | The bill ID |
Receive Bill
/organizations/:organization_id/bills/:id/receiveMarks a bill as received and updates its status.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organization_id | string | Yes | The organization ID |
id | string | Yes | The bill ID |
Record Bill Payment
/organizations/:organization_id/bills/:id/paymentsRecords a payment for a bill, creating the corresponding accounting transaction.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organization_id | string | Yes | The organization ID |
id | string | Yes | The bill ID |
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | string | Yes | Payment amount |
payment_date | date | Yes | Date of payment |
payment_method | string | No | Payment method (check, wire, etc.) |
reference | string | No | Payment reference number |
Get Bill History
/organizations/:organization_id/bills/:id/historyReturns the status change history for a bill.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organization_id | string | Yes | The organization ID |
id | string | Yes | The bill ID |
Response
[
{
"status": "draft",
"changed_at": "2024-01-10T09:15:00Z",
"changed_by": "user_xxx"
},
{
"status": "received",
"changed_at": "2024-01-12T11:30:00Z",
"changed_by": "user_xxx"
},
{
"status": "paid",
"changed_at": "2024-01-15T14:45:00Z",
"changed_by": "user_xxx"
}
]
Bill Lifecycle
Status Flow
draft → received → partial → paid
↓ ↓ ↓ ↓
cancelled overdue
Status Descriptions
| Status | Description | Actions Available |
|---|---|---|
draft | Bill created but not received | Edit, receive, delete |
received | Bill received from vendor | Record payment, cancel |
partial | Partial payment made | Record additional payments, cancel |
paid | Fully paid | Generate receipt, create debit note |
cancelled | Bill cancelled | Reactivate (if draft), create debit note |
overdue | Past due date | Send reminders, record payment, dispute |
Expense Categories
Common Bill Categories
{
"operating_expenses": [
{
"name": "Office Supplies",
"category_id": "cat_office_supplies",
"typical_vendors": ["Office Depot", "Staples", "Amazon Business"]
},
{
"name": "Professional Services",
"category_id": "cat_professional_services",
"typical_vendors": ["Law Firms", "Consultants", "Accountants"]
},
{
"name": "Marketing & Advertising",
"category_id": "cat_marketing",
"typical_vendors": ["Google Ads", "Facebook", "PR Agencies"]
},
{
"name": "Travel & Entertainment",
"category_id": "cat_travel",
"typical_vendors": ["Airlines", "Hotels", "Uber", "Restaurants"]
},
{
"name": "Utilities",
"category_id": "cat_utilities",
"typical_vendors": ["Electric Company", "Internet Provider", "Phone Company"]
}
],
"cost_of_goods_sold": [
{
"name": "Inventory Purchases",
"category_id": "cat_inventory",
"typical_vendors": ["Wholesalers", "Manufacturers", "Distributors"]
}
],
"capital_expenditures": [
{
"name": "Equipment & Furniture",
"category_id": "cat_equipment",
"typical_vendors": ["Office Furniture Stores", "Computer Dealers"]
},
{
"name": "Software & Licenses",
"category_id": "cat_software_expenses",
"typical_vendors": ["Software Companies", "SaaS Providers"]
}
]
}
Three-Way Matching
Three-way matching ensures accurate bill processing by comparing purchase orders, receipts, and invoices.
Matching Process
// Three-way matching validation
function validateThreeWayMatch(purchaseOrder, goodsReceipt, vendorBill) {
const matches = {
quantity: false,
price: false,
total: false
};
// 1. Quantity Match
matches.quantity = purchaseOrder.items.every((poItem, index) => {
const receiptItem = goodsReceipt.items[index];
const billItem = vendorBill.items[index];
return poItem.quantity === receiptItem.quantity &&
receiptItem.quantity === billItem.quantity;
});
// 2. Price Match
matches.price = purchaseOrder.items.every((poItem, index) => {
const billItem = vendorBill.items[index];
return poItem.unitPrice === billItem.unitPrice;
});
// 3. Total Match
const poTotal = calculateTotal(purchaseOrder.items);
const billTotal = calculateTotal(vendorBill.items);
matches.total = Math.abs(poTotal - billTotal) < 0.01;
return {
matches,
allMatch: matches.quantity && matches.price && matches.total,
discrepancies: {
quantity: !matches.quantity,
price: !matches.price,
total: !matches.total
}
};
}
// Usage
const match = validateThreeWayMatch(po, receipt, bill);
if (!match.allMatch) {
console.log("Three-way match failed:", match.discrepancies);
// Handle discrepancies (approval workflow, vendor notification, etc.)
}
Approval Workflows
def process_bill_approval(bill, match_result):
"""Process bill through approval workflow based on matching results"""
if match_result['allMatch']:
# Auto-approve perfectly matched bills
return approve_bill(bill, "auto_approved")
# Manual approval required for discrepancies
if match_result['discrepancies']['total']:
# Large total discrepancy - escalate to finance manager
return escalate_to_finance_manager(bill, match_result)
elif match_result['discrepancies']['price']:
# Price discrepancy - requires department head approval
return require_department_approval(bill, match_result)
elif match_result['discrepancies']['quantity']:
# Quantity discrepancy - may be acceptable with explanation
return require_explanation(bill, match_result)
return bill
Recurring Bills
Setting Up Recurring Expenses
{
"recurring_bill_template": {
"contact_id": "con_vendor_utilities",
"category_id": "cat_utilities",
"items": [
{
"name": "Electricity",
"quantity": 1,
"price": 450.00,
"tax_id": "tax_vat_5"
},
{
"name": "Internet",
"quantity": 1,
"price": 89.99,
"tax_id": "tax_vat_20"
}
],
"notes": "Monthly utility bill"
},
"schedule": {
"frequency": "monthly",
"start_date": "2024-02-01",
"auto_receive": true,
"payment_method": "bank_transfer"
}
}
Recurring Bill Management
// Update recurring bill amounts
async function updateRecurringBill(billId, newAmounts) {
// Future API - update recurring template
const response = await fetch(`/organizations/${orgId}/bills/${billId}/recurring`, {
method: 'PUT',
headers: { ...authHeaders, 'Content-Type': 'application/json' },
body: JSON.stringify({
items: newAmounts,
effective_date: '2024-03-01'
})
});
return response.json();
}
// Pause recurring bill (e.g., during office closure)
async function pauseRecurringBill(billId, resumeDate) {
const response = await fetch(`/organizations/${orgId}/bills/${billId}/recurring/pause`, {
method: 'POST',
headers: { ...authHeaders, 'Content-Type': 'application/json' },
body: JSON.stringify({
resume_date: resumeDate,
reason: 'Office renovation'
})
});
return response.json();
}
Vendor Management
Preferred Vendors
{
"preferred_vendors": [
{
"contact_id": "con_vendor_office_depot",
"vendor_name": "Office Depot",
"categories": ["office_supplies", "furniture"],
"payment_terms": "net_30",
"discount_rate": "5",
"contract_expiry": "2024-12-31",
"performance_rating": "A"
},
{
"contact_id": "con_vendor_staples",
"vendor_name": "Staples",
"categories": ["office_supplies"],
"payment_terms": "net_15",
"discount_rate": "3",
"contract_expiry": "2024-06-30",
"performance_rating": "B"
}
]
}
Vendor Performance Tracking
def calculate_vendor_metrics(vendor_id, period_months=12):
"""Calculate vendor performance metrics"""
bills = get_bills_for_vendor(vendor_id, period_months)
payments = get_payments_for_vendor(vendor_id, period_months)
metrics = {
"total_billed": sum(bill['total'] for bill in bills),
"total_paid": sum(payment['amount'] for payment in payments),
"average_payment_time": calculate_average_payment_time(bills, payments),
"on_time_payment_rate": calculate_on_time_rate(bills, payments),
"invoice_accuracy": calculate_accuracy_rate(bills),
"dispute_rate": calculate_dispute_rate(bills)
}
# Assign performance rating
if metrics['on_time_payment_rate'] > 0.95 and metrics['invoice_accuracy'] > 0.98:
metrics['rating'] = 'A'
elif metrics['on_time_payment_rate'] > 0.90 and metrics['invoice_accuracy'] > 0.95:
metrics['rating'] = 'B'
else:
metrics['rating'] = 'C'
return metrics
Bill Payment Strategies
Payment Terms Optimization
// Calculate optimal payment dates
function optimizePaymentDates(bills, cashFlow) {
const paymentSchedule = [];
// Sort bills by due date
bills.sort((a, b) => new Date(a.due_at) - new Date(b.due_at));
bills.forEach(bill => {
const dueDate = new Date(bill.due_at);
const paymentTerms = bill.payment_terms || 'net_30';
// Calculate optimal payment date based on cash flow and terms
let optimalPaymentDate = new Date(dueDate);
if (cashFlow.isConstrained(dueDate)) {
// Delay payment within terms if cash flow is tight
optimalPaymentDate = new Date(Math.min(
dueDate.getTime() + (paymentTerms.days * 24 * 60 * 60 * 1000),
dueDate.getTime() + (30 * 24 * 60 * 60 * 1000) // Max 30 days
));
} else {
// Pay early for discounts
optimalPaymentDate = new Date(dueDate.getTime() - (2 * 24 * 60 * 60 * 1000));
}
paymentSchedule.push({
bill_id: bill.id,
vendor: bill.contact_name,
amount: bill.total,
due_date: dueDate,
optimal_payment_date: optimalPaymentDate,
savings: calculateEarlyPaymentDiscount(bill)
});
});
return paymentSchedule;
}
Cash Flow Management
def optimize_bill_payment_schedule(bills, available_cash, payment_priorities):
"""Optimize bill payment schedule based on cash flow and priorities"""
# Sort bills by priority and due date
prioritized_bills = sort_bills_by_priority(bills, payment_priorities)
payment_schedule = []
remaining_cash = available_cash
for bill in prioritized_bills:
if bill['total'] <= remaining_cash:
# Can pay in full
payment_schedule.append({
'bill_id': bill['id'],
'amount': bill['total'],
'payment_date': bill['due_at'],
'status': 'scheduled'
})
remaining_cash -= bill['total']
elif remaining_cash > 0:
# Can pay partial amount
payment_schedule.append({
'bill_id': bill['id'],
'amount': remaining_cash,
'payment_date': bill['due_at'],
'status': 'partial'
})
remaining_cash = 0
else:
# Cannot pay - mark as pending
payment_schedule.append({
'bill_id': bill['id'],
'amount': 0,
'payment_date': None,
'status': 'pending'
})
return payment_schedule
Bill Best Practices
Vendor Relationship Management
- Preferred Vendor Programs: Negotiate better terms with high-performing vendors
- Payment Terms: Optimize payment schedules for cash flow
- Volume Discounts: Consolidate purchases for better pricing
- Performance Tracking: Monitor vendor reliability and quality
Approval Workflows
- Amount-based Approvals: Different approval levels for different bill amounts
- Department Approvals: Route bills to appropriate department heads
- Three-way Matching: Automate approval for matched bills
- Exception Handling: Manual review for discrepancies
Tax Compliance
- Input Tax Credits: Track recoverable taxes for VAT/GST systems
- Withholding Taxes: Handle contractor payments correctly
- Tax Reporting: Maintain proper documentation for tax authorities
Bill Processing Timeline
Process bills promptly to maintain good vendor relationships and take advantage of early payment discounts. Delayed processing can impact cash flow forecasting and vendor terms.
Need help?
Create a free account to access our support portal. Once signed in, use the Support tab in your dashboard to submit a support ticket — our team typically responds within 24 hours.
- ✨ For LLMs/AI assistants: Read our structured API reference