Create a customer invoice
Customer invoices in Sage 50 are created as sales journal entries through the Sales API. Use POST /api/sales to create an invoice with line items.
Prerequisites
Before creating an invoice, make sure you have:
- A valid API key configured on the server
- The Sage 50 company database open (use
GET /api/databaseto check status) - A valid
customerIdfromGET /api/customers
Authentication
Pass your API key in the x-api-key request header:
x-api-key: YOUR_API_KEY
Endpoint
POST /api/sales
Only customerId is required by the API. Most other fields are optional. Sage applies defaults from the customer, item, or company settings when you omit them.
What you actually need to send
You do not need to send payment terms, tax codes, GL accounts, invoice numbers, or currency fields unless you want to override Sage defaults.
| Field | Required? | What happens if omitted |
|---|---|---|
customerId |
Yes | Request is rejected |
lines |
Practically yes | Sage needs at least one line to post |
itemNumber |
One of itemNumber or accountNumber per line |
Use itemNumber for inventory items |
quantity |
Yes (per line) | Required for each line |
price |
Yes (per line) | Required for each line |
accountNumber |
No | Sage uses the revenue account configured on the item |
taxCode |
No | Sage uses the tax settings configured on the item |
termDiscNetDay |
No | Sage uses the payment terms configured on the customer |
invoiceNumber |
No | Sage assigns the next invoice number automatically |
date |
No | Sage uses the current date. The invoice date must fall within the company’s open fiscal period |
description |
No | Sage can use the item name, or the line can be description-only |
lineNumber |
No | Assigned automatically from the order of items in lines |
amount |
No | Ignored on create. Sage calculates it from quantity × price |
You do not need to set lineNumber on each line. The API numbers lines sequentially based on their position in the lines array (first item is line 1, second is line 2, and so on).
If you omit date, Sage uses the current date. Whether defaulted or sent explicitly, the invoice date must fall within the company’s open fiscal period.
Request body
Minimum example using an inventory item. Only customerId, quantity, price, and itemNumber are sent. Sage fills in the account, tax, and payment terms:
{
"customerId": 42,
"lines": [
{
"itemNumber": "WIDGET-01",
"quantity": 2,
"price": 50.0
}
]
}
For a service line without an inventory item, use accountNumber instead of itemNumber:
{
"customerId": 42,
"lines": [
{
"description": "Consulting services",
"quantity": 1,
"price": 100.0,
"accountNumber": "4000"
}
]
}
Empty and description-only lines
Sage invoices support blank spacer lines and informational lines that carry no amount. You can mix these with regular item lines in the same request.
An empty line is a spacer row with no item, quantity, or price. Pass an empty description:
{
"description": ""
}
A description-only line is useful for notes, project names, or section headers. Set description with text and clear taxCode and accountNumber so Sage does not apply item defaults:
{
"description": "Project: Example Site",
"taxCode": "",
"accountNumber": ""
}
Example invoice with a description header, a product line, and a blank line between sections:
{
"customerId": 42,
"lines": [
{
"description": "September delivery",
"taxCode": "",
"accountNumber": ""
},
{
"itemNumber": "WIDGET-01",
"quantity": 2,
"price": 50.0
},
{
"description": ""
},
{
"itemNumber": "WIDGET-02",
"quantity": 1,
"price": 25.0
}
]
}
cURL example
curl -X POST "/api/sales" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"customerId": 42,
"lines": [
{
"itemNumber": "WIDGET-01",
"quantity": 2,
"price": 50.0
}
]
}'
Success response
A successful request returns 201 Created with the created sales journal entry. Fields you did not send are populated by Sage:
{
"id": 456,
"customerId": 42,
"date": "2026-09-11T00:00:00",
"customerName": "Acme Corp",
"invoiceNumber": "INV-1001",
"termDiscNetDay": 30,
"totalAmount": 113.0,
"amountOwing": 113.0,
"paymentStatus": 0,
"dueDate": "2026-10-11T00:00:00",
"lines": [
{
"lineNumber": 1,
"itemNumber": "WIDGET-01",
"accountNumber": "4000",
"description": "Premium widget",
"quantity": 2.0,
"price": 50.0,
"amount": 100.0,
"taxCode": "HST"
}
]
}
paymentStatus values: 0 = unpaid, 1 = partial, 2 = paid. totalAmount includes tax calculated by Sage. termDiscNetDay, accountNumber, and taxCode in the response come from the customer and item defaults even though they were not in the request.
Error response
If the request is invalid or Sage rejects the entry, the API returns 400 Bad Request:
{
"title": "Validation failed",
"status": 400,
"detail": "Customer with id 42 was not found.",
"code": "CUSTOMER_NOT_FOUND",
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
Common causes include an invalid customerId, unknown itemNumber, or a missing line with quantity and price.
Next steps
- Browse the full API reference for all Sales endpoints
- Use
GET /api/sales/{saleId}to retrieve the invoice you just created - List invoices changed since a time with
GET /api/sales?modifiedSince=oractivitySince=. Each row includeslastModifiedAtandlastActivityAt. See Detect changes for incremental sync - Apply a customer payment with the customer receipt article