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 |
approved_by | object | null | User who approved the invoice, with |
created_by | object | null | Account user who created the invoice, with |
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 |
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 |
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 |
end_date | date | Filter by issued date (inclusive). Format: YYYY-MM-DD. Only invoices with |
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 |
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 |
| integer | The project to allocate the Xero invoice to. Trying to allocate an invoice to an opportunity will return |
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 |
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 |
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 |
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 |
| Seconds to wait before retrying. |
| Limit that was exceeded, when Drum's Xero limiter has this value. |
| Always |
| 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
Authorisationheader is providedThe 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_invoicespermission can access all invoices in the accountUsers without
read_invoicescan only access invoices for projects where they have the appropriate project-level permissionsThe 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
currencyfield 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_statusandthird_party_servicefields 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
pageanditemsquery parameters to control pagination. Default is 20 items per page.
