Invoices and write-offs
Invoices and write-offs are read only through the API, apart from allocating an existing Xero invoice to a project. A write-off is stored as an invoice with write_off: true and has its own endpoints.
Money on these endpoints is a decimal in the invoice’s currency, not cents. Totals are given both with and without tax.
Endpoints
| Method and path | Does |
|---|---|
GET /api/v1/invoices |
Lists invoices (write-offs are left out) |
GET /api/v1/invoices/:id |
Reads one invoice |
GET /api/v1/write_offs |
Lists write-offs |
GET /api/v1/write_offs/:id |
Reads one write-off |
POST /api/v1/projects/:project_id/invoices/allocate |
Imports an existing Xero sales invoice into a project |
List invoices or write-offs
Newest issued date first, paginated (see Pagination). Each row includes its line items.
| Parameter | Meaning |
|---|---|
project_id |
Invoices of one project |
status |
draft, awaiting_approval or approved |
start_date, end_date |
Issued on or after, or on or before, this date |
curl -H "Authorization: Bearer $DRUM_API_TOKEN" \
"https://app.getdrum.com/api/v1/invoices?project_id=20488&status=approved"
The invoice object
{
"id": 5678,
"invoice_number": "INV-2026-001",
"description": "September progress claim",
"reference": "Progress Claim #1",
"total": 5500.0,
"total_excluding_tax": 5000.0,
"amount_paid": 0.0,
"currency": "AUD",
"tax_rule": "exclusive",
"status": "approved",
"third_party_status": "authorised",
"third_party_service": "xero_aus",
"integration_link": "https://go.xero.com/...",
"issued_date": "2026-09-30",
"due_date": "2026-10-30",
"paid_date": null,
"approved_at": "2026-09-30T04:10:00.000Z",
"sent_at": null,
"sent": false,
"write_off": false,
"project": {"id": 20488, "name": "Riverside fit-out", "project_number": "2026-088", "project_type": "project"},
"approved_by": {"id": 1204, "name": "Sam Lee"},
"created_by": {"id": 1204, "name": "Sam Lee"},
"line_items": [
{
"id": 90011,
"name": "Design development",
"position": 1,
"quantity": "1.0",
"price": 5000.0,
"total": 5500.0,
"total_excluding_tax": 5000.0,
"currency": "AUD",
"tax_rate": {"id": 12, "name": "GST on Income", "tax_percent": 10.0}
}
]
}
| Field | Meaning |
|---|---|
status |
The status in Drum: draft, awaiting_approval or approved |
tax_rule |
exclusive, inclusive or no_tax |
third_party_status |
The status in the accounting system: draft, submitted, authorised, sent, partial, paid, deleted or voided, or null |
third_party_service |
xero_aus, quickbooks or myob, or null |
integration_link |
A link to the invoice in the accounting system. Present only when the invoice has been sent there |
sent, sent_at |
Whether, and when, the invoice was sent |
write_off |
true on a write-off |
retention_release, retention_release_amount |
Present only on an invoice that releases retention |
progress_claim_id |
Present when the invoice was raised from a progress claim |
sales_tax_rate |
{id, name, tax_percent}. Present only on invoices that use a sales tax rate (QuickBooks in the United States) |
project, approved_by, created_by |
Present when set |
line_items |
Ordered by position. price is left out when the line has none. tax_rate is present when the line has one |
Write-offs use the same shape.
Allocate a Xero invoice
POST /api/v1/projects/:project_id/invoices/allocate finds a sales invoice in the account’s connected Xero organisation and records it against the project. It does not change the invoice in Xero. Xero is the only accounting system supported.
Send one of these, flat or under allocation:
| Field | Meaning |
|---|---|
xero_invoice_id |
The invoice id in Xero |
invoice_number |
The invoice number in Xero |
Returns the invoice in the shape above: 201 when it is newly allocated, 200 when it was already on this project.
| Status | Body | When |
|---|---|---|
403 |
{"error": "Forbidden"} |
The token’s user cannot create invoices on this project |
404 |
{"error": "Project not found"} |
The project does not exist in the account, or is an opportunity |
404 |
{"error": ...} |
The invoice was not found in Xero |
409 |
{"error": ..., "existing_project_id": 123} |
The invoice is already allocated to another project |
422 |
{"error": ...} or {"errors": [...]} |
The request or the invoice cannot be used |
429 |
{"error": "Too many Xero requests. Retry later."} |
Xero’s rate limit. Wait for Retry-After seconds |
502 |
{"error": "Unable to allocate Xero invoice. Please try again later."} |
Xero could not be reached or returned an error |
Permissions
| Endpoint | Needs |
|---|---|
GET /invoices |
api_read and read_invoices |
GET /invoices/:id |
api_read, and read_invoices or a project role on the invoice’s project that can view invoices |
GET /write_offs, GET /write_offs/:id |
api_read and read_write_offs |
POST .../invoices/allocate |
api_create, and create_invoices or a project role on the project that can create invoices |
Errors on reads
| Status | Body | When |
|---|---|---|
403 |
{"error": "Forbidden"} |
The token’s user lacks the permission for the list |
404 |
Empty | The invoice or write-off does not exist in the account, or is hidden from the token’s user |