Skip to main content

Invoices & Write-Offs API

The Invoices API allows you to retrieve invoices and write-offs within your Drum account. Invoices represent client billing documents, while write-offs track revenue adjustments. This API provides read-only access to invoice data.

Written by Ben Walker

Authentication

All API requests require authentication using a Bearer token. Include your API token in the Authorization header of each request:

Authorization: Bearer YOUR_API_TOKEN

For detailed information about authentication, token generation, and permissions, see the API Authentication documentation.

All endpoints are scoped to a specific account. Only invoices belonging to the authenticated user's account will be accessible.

The Invoice Object

An invoice object represents a client billing document within Drum. Invoices can be regular invoices or write-offs (distinguished by the write_off boolean field). The API provides separate endpoints for each type.

Core Attributes

Attribute

Type

Description

id

integer

Unique identifier for the invoice.

invoice_number

string | null

Invoice number/reference. May be null for draft invoices or Drum-only invoices.

description

text | null

Optional detailed description.

reference

string | null

Additional reference number or text.

Financial Data

Attribute

Type

Description

total

decimal

Total invoice amount including tax.

total_excluding_tax

decimal

Invoice subtotal before tax.

amount_paid

decimal

Amount that has been paid.

currency

string

Three-letter ISO currency code (e.g., "AUD", "USD", "GBP").

tax_rule

string

Tax calculation method: "exclusive", "inclusive", or "no_tax".

Status

Attribute

Type

Description

status

string

Invoice status in Drum: "draft", "awaiting_approval", or "approved".

third_party_status

string | null

Integration status with Xero/QuickBooks: "draft", "submitted", "authorised", "sent", "partial", "paid", "deleted", or "voided".

third_party_service

string | null

Integration service: "xero_aus" or "quickbooks".

integration_link

string | null

Direct link to the invoice in Xero or QuickBooks (only if integrated).

Dates

Attribute

Type

Description

issued_date

date | null

Date the invoice was issued (YYYY-MM-DD).

due_date

date | null

Payment due date (YYYY-MM-DD).

paid_date

datetime | null

Date/time the invoice was marked as paid.

approved_at

datetime | null

Date/time the invoice was approved.

sent_at

datetime | null

Date/time the invoice was sent to the client.

Flags

Attribute

Type

Description

sent

boolean

Whether the invoice has been sent to the client.

write_off

boolean

Whether this is a write-off (revenue adjustment) vs. a regular invoice.

Retention (if applicable)

Attribute

Type

Description

retention_release

boolean

Whether this invoice includes a retention release.

retention_release_amount

decimal

Amount of retention being released (only if retention_release is true).

Associations

Attribute

Type

Description

project

object

Project this invoice belongs to, with id, name, project_number, and project_type.

approved_by

object | null

User who approved the invoice, with id and name.

created_by

object | null

Account user who created the invoice, with id and name.

progress_claim_id

integer | null

ID of the progress claim this invoice was generated from (if applicable).

sales_tax_rate

object | null

Sales tax rate for US QuickBooks invoices, with id, name, and tax_percent.

Line Items

Attribute

Type

Description

line_items

array

Array of line item objects. See Line Item Object below.

Line Item Object

A line item represents an individual billable item on an invoice.

Attributes

Attribute

Type

Description

id

integer

Unique identifier for the line item.

name

string

Line item name/description.

description

text | null

Detailed description.

position

integer

Sort order within the invoice (1-indexed).

quantity

decimal | null

Quantity (may be null for description-only lines).

price

decimal | null

Unit price (may be null).

total

decimal

Total line amount including tax.

total_excluding_tax

decimal

Line subtotal before tax.

currency

string

Currency code.

tax_rate

object | null

Tax rate applied, with id, name, and tax_percent.


List all invoices

Returns a paginated list of regular invoices (excludes write-offs) for the authenticated account. Results can be filtered using query parameters.

Endpoint

GET /api/v1/invoices

Path Parameters

Parameter

Type

Description

account_id

integer

The ID of the account to retrieve invoices from. Must be an account the authenticated user has access to.

Query Parameters

Parameter

Type

Description

project_id

integer

Filter by project ID. Only invoices for this project will be returned.

status

string

Filter by invoice status. One of: "draft", "awaiting_approval", or "approved".

start_date

date

Filter by issued date (inclusive). Format: YYYY-MM-DD. Only invoices with issued_date >= start_date will be returned.

end_date

date

Filter by issued date (inclusive). Format: YYYY-MM-DD. Only invoices with issued_date <= end_date will be returned.

page

integer

Page number for pagination. Defaults to 1.

items

integer

Number of items per page. Defaults to 20.

Example Request

curl https://app.getdrum.com/api/v1/invoices \
-H "Authorization: Bearer YOUR_API_TOKEN"

Example Request with Filters

curl "https://app.getdrum.com/api/v1/invoices?project_id=42&status=approved&start_date=2025-01-01&end_date=2025-12-31" \
-H "Authorization: Bearer YOUR_API_TOKEN"

Response

Returns an array of invoice objects with nested line items. Invoices are ordered by issued date (most recent first).

[
{
"id": 1234,
"invoice_number": "INV-001",
"description": "Website redesign services - January 2025",
"reference": "PO-12345",
"total": 11000.00,
"total_excluding_tax": 10000.00,
"amount_paid": 11000.00,
"currency": "AUD",
"tax_rule": "exclusive",
"status": "approved",
"third_party_status": "paid",
"third_party_service": "xero_aus",
"integration_link": "https://go.xero.com/app/ABC123/invoicing/view/xyz789",
"issued_date": "2025-01-31",
"due_date": "2025-02-28",
"paid_date": "2025-02-15T10:30:00Z",
"approved_at": "2025-01-31T09:00:00Z",
"sent_at": "2025-01-31T10:00:00Z",
"sent": true,
"write_off": false,
"project": {
"id": 42,
"name": "Website Redesign for Acme Corp",
"project_number": "PRJ-001",
"project_type": "project"
},
"approved_by": {
"id": 5,
"name": "Jane Smith"
},
"created_by": {
"id": 10,
"name": "John Doe"
},
"line_items": [
{
"id": 5001,
"name": "Design Services",
"description": "UI/UX design and prototyping",
"position": 1,
"quantity": 40.0,
"price": 150.00,
"total": 6600.00,
"total_excluding_tax": 6000.00,
"currency": "AUD",
"tax_rate": {
"id": 1,
"name": "GST 10%",
"tax_percent": 10.0
}
},
{
"id": 5002,
"name": "Development Services",
"description": "Front-end development",
"position": 2,
"quantity": 40.0,
"price": 100.00,
"total": 4400.00,
"total_excluding_tax": 4000.00,
"currency": "AUD",
"tax_rate": {
"id": 1,
"name": "GST 10%",
"tax_percent": 10.0
}
}
]
}
]

Retrieve an invoice

Retrieves the details of a specific invoice by ID.

Endpoint

GET /api/v1/invoices/:id

Path Parameters

Parameter

Type

Description

account_id

integer

The ID of the account the invoice belongs to.

id

integer

The ID of the invoice to retrieve.

Example Request

curl https://app.getdrum.com/api/v1/invoices/1234 \
-H "Authorization: Bearer YOUR_API_TOKEN"

Response

Returns a single invoice object with nested line items (same structure as index endpoint).


Allocate Xero invoice to a project

Creates a new Drum invoice on a project by importing an existing Xero sales invoice. This endpoint is Xero-only in v1. It does not link an existing unlinked Drum invoice, and it does not sync changes back out to Xero.

If the Xero invoice is already allocated to the same Drum project, the endpoint returns the existing Drum invoice idempotently with `200 OK`. If it is already allocated to another project, the endpoint returns `409 Conflict`.

For best reliability, prefer xero_invoice_id when you have it. Requests by invoice number require Drum to search Xero first and then fetch the resolved invoice, so they can use two Xero API calls. Requests by Xero invoice ID normally use one Xero API call, and idempotent retries for an invoice already allocated to the same project return from Drum without calling Xero.

Endpoint

POST /api/v1/projects/:project_id/invoices/allocate

Required Permissions

The API token must be account-scoped and the authenticated account user must have:

Permission

Description

API - Create records

Required for all POST API requests.

Create Invoices

Required to create invoices for the project. Project-level role-based "create invoices" permission also applies.

Account administrators bypass explicit API and invoice permission checks.

Path Parameters

Parameter

Type

Description

project_id

integer

The project to allocate the Xero invoice to. Trying to allocate an invoice to an opportunity will return 404 Not Found

Request Body

Provide xero_invoice_id, invoice_number, or both. When both are provided, they must identify the same Xero sales invoice. Parameters may be sent as top-level JSON fields or nested under an allocation object.

Field

Type

Description

xero_invoice_id

string

Xero unique InvoiceID for the sales invoice.

invoice_number

string

Xero invoice number. Drum resolves this by exact Xero sales invoice number before importing. If more than one Xero sales invoice matches, provide xero_invoice_id.

Example Request

curl https://app.getdrum.com/api/v1/projects/42/invoices/allocate \
-X POST \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"xero_invoice_id":"11111111-2222-3333-4444-555555555555"}'

Example Request by Invoice Number

curl https://app.getdrum.com/api/v1/projects/42/invoices/allocate \
-X POST \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"invoice_number":"INV-0042"}'

Example Nested Request Body

curl https://app.drumhq.com/api/v1/projects/42/invoices/allocate \
-X POST \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"allocation":{"xero_invoice_id":"11111111-2222-3333-4444-555555555555"}}'

Success Response

Returns the standard invoice show object, including line items.

Status

Description

201 Created

A new Drum invoice was created and synced from Xero.

200 OK

The Xero invoice was already allocated to this project; the existing Drum invoice was returned.

{
"id": 1234,
"invoice_number": "INV-0042",
"reference": "PO-12345",
"total": 11000.00,
"total_excluding_tax": 10000.00,
"amount_paid": 0.00,
"currency": "AUD",
"tax_rule": "exclusive",
"status": "approved",
"third_party_status": "authorised",
"third_party_service": "xero_aus",
"integration_link": "https://go.xero.com/app/ABC123/invoicing/view/11111111-2222-3333-4444-555555555555",
"issued_date": "2025-01-31",
"due_date": "2025-02-28",
"sent": false,
"write_off": false,
"project": {
"id": 42,
"name": "Website Redesign for Acme Corp",
"project_number": "PRJ-001",
"project_type": "project"
},
"line_items": []
}

Allocation Error Responses

Status

Description

401

Missing/invalid token, or token is not scoped to an account.

403

Missing API create or invoice creation permission.

404

Project not found, project is an opportunity, or the Xero invoice could not be found.

409

Xero invoice is already allocated to another Drum invoice, blocked by a discarded Drum invoice, or conflicts with an existing non-Xero Drum invoice external ID.

423

Too many Xero requests for the connected Xero tenant. Retry after the number of seconds in the Retry-After header.

422

Missing identifiers, mismatched identifiers, ambiguous invoice number, no Xero connection, wrong Xero invoice type, or voided/deleted Xero invoice.

502

Xero API or network error while resolving or importing the invoice.

Conflict responses include the project already holding the allocation:

{
"error": "This Xero invoice is already allocated to another Drum invoice.",
"existing_project_id": 99
}

Rate-limited responses include retry headers:

Header

Description

Retry-After

Seconds to wait before retrying.

RateLimit-Limit

Limit that was exceeded, when Drum's Xero limiter has this value.

RateLimit-Remaining

Always 0 on this endpoint's 429 responses.

RateLimit-Reset

Unix timestamp for when Drum's Xero limiter resets, when available.

{
"error": "Too many Xero requests. Retry later."
}

List all write-offs

Returns a paginated list of write-offs for the authenticated account. Write-offs are revenue adjustments tracked separately from regular invoices.

Endpoint

GET /api/v1/write_offs

Path Parameters

Parameter

Type

Description

account_id

integer

The ID of the account to retrieve write-offs from.

Query Parameters

Same as the invoices endpoint: project_id, status, start_date, end_date, page, items.

Example Request

curl https://app.getdrum.com/api/v1/write_offs \
-H "Authorization: Bearer YOUR_API_TOKEN"

Response

Returns an array of write-off objects. Write-offs use the same structure as invoices but have "write_off": true.


Retrieve a write-off

Retrieves the details of a specific write-off by ID.

Endpoint

GET /api/v1/write_offs/:id

Path Parameters

Parameter

Type

Description

account_id

integer

The ID of the account the write-off belongs to.

id

integer

The ID of the write-off to retrieve.

Example Request

curl https://app.getdrum.com/api/v1/write_offs/5678 \
-H "Authorization: Bearer YOUR_API_TOKEN"

Response

Returns a single write-off object with nested line items.


Error Responses

The Invoices API uses standard HTTP response codes to indicate success or failure.

HTTP Status Codes

Status Code

Description

200

Success - The request was successful.

401

Unauthorised - Missing, invalid, or expired API token.

403

Forbidden - The authenticated user lacks the required permissions (api_read and invoice viewing permissions).

404

Not Found - The requested account or invoice does not exist, or the authenticated user does not have access.

Error Response Format

{
"error": "Forbidden"
}

Common Error Scenarios

401 Unauthorised

Occurs when:

  • No Authorisation header is provided

  • The API token is invalid or malformed

  • The API token has expired

Solution: Verify your API token is valid and properly included in the Authorisation header.

403 Forbidden

Occurs when:

  • The authenticated user's role lacks the required API permissions

  • The user doesn't have permission to view invoices (based on InvoicePolicy)

  • The account user has been archived or deactivated

Solution: Contact your account administrator to verify your API permissions and invoice viewing permissions.

404 Not Found

Occurs when:

  • The specified account ID doesn't exist or you don't have access

  • The specified invoice ID doesn't exist within the account

  • The invoice exists but belongs to a project you don't have access to

Solution: Verify the account_id and invoice id are correct and that you have access to the account and the invoice's project.


Rate Limiting

Currently, rate limiting is not enforced on API requests. However, infrastructure is in place for future rate limiting:

  • General API: 300 requests per 5 minutes per IP address

  • Per endpoint throttling may be introduced as needed

We recommend implementing exponential backoff in your API client to handle potential future rate limits gracefully.


API Permissions

The Invoices API requires multiple levels of permissions:

Base API Permissions

Required based on HTTP method:

Method

Required Permission

GET

API Read

POST

API Create

Invoice Visibility Permissions

In addition to api_read, users need invoice viewing permissions based on the InvoicePolicy:

Permission

Description

View invoices

Access all invoices in the account

Create invoices

Create invoices against projects

Project-level permissions

Access invoices for specific projects based on project role permissions (View Invoices or Create Invoices)

Permission Hierarchy

  • Account administrators automatically have all API and invoice permissions

  • Users with read_invoices permission can access all invoices in the account

  • Users without read_invoices can only access invoices for projects where they have the appropriate project-level permissions

  • The API respects all Pundit policy scoping defined in InvoicePolicy


Notes

  • Read-Only: This API primarily provides read-only access to invoices and write-offs. Invoice updates are not supported and invoice creation is currently only supported via the Xero-only allocate invoice endpoint.

  • Multi-tenancy: All invoices are scoped to accounts. Users can only access invoices from accounts they belong to.

  • Authorisation: The API enforces permission-based access through InvoicePolicy. Users are restricted to invoices they have permission to view.

  • Soft Deletes: Deleted invoices are soft-deleted (discarded) rather than permanently removed. Discarded invoices are not returned by the API.

  • Money Format: All monetary amounts are returned in decimal format (e.g., 11000.00 for $11,000). The currency field indicates the currency code.

  • Date Format: Dates are returned in ISO 8601 format (YYYY-MM-DD). Date times include timezone information.

  • Write-Offs vs Invoices: Write-offs are tracked using the same Invoice model but with write_off: true. They are accessed through separate endpoints for clarity.

  • Line Items: Line items are always included in both index and show responses, ordered by position.

  • Integration Status: The third_party_status and third_party_service fields track synchronisation with Xero or QuickBooks.

  • Eager Loading: The API uses efficient eager loading to minimise database queries. Large result sets may still result in substantial response payloads.

  • Ordering: Invoices and write-offs are returned ordered by issued date (most recent first), with creation date as a secondary sort.

  • Pagination: Use the page and items query parameters to control pagination. Default is 20 items per page.

Did this answer your question?