← All articles

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:

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