URL class: APBill — /Windward/WebAPI/APBill/...

Source of truth: ServerMethodsAPBill.pas, CM_APBill.pas, AO_APBill.pas.

The legacy equivalent is POST /TServerMethodsWebAPI/Insert_AP_Bill — see The Legacy TServerMethodsWebAPI Surface.


Operations

VerbPathDelphi methodEnvelope
GET/APBill/APBill_HandshakeAPBill_Handshakead hoc
GET/APBill/GetAPBillRecordCountGetAPBillRecordCountad hoc
GET/APBill/APBills/{Unique}APBillsAPIResponse + APBill
POST/APBill/addAPBillsupdateaddAPBillsAPIResponse + APBill

POST /APBill/addAPBills

This endpoint behaves differently from every other write endpoint in the API, in three ways that matter a great deal.

1. It is insert-only

// Update is not currently supported. Editing a Bill from an external system
// is not something we will likely support.
if (aActionParams.Operation = prmInsert) then

Only the insert branch exists. If FindRecordMatch resolves your payload to an existing bill — which it does when you send a Unique that exists — AddUpdateRecord does nothing at all: no write, no error, and ActionSuccess is left at its initialised false with an empty ActionResult.

Never send a Unique on an AP bill. Send 0 or omit it. AP bills cannot be edited through this API; correct them in System Five.

2. It pre-validates the whole batch, and drops what fails

Unlike the other domains, AP bills are validated before the upsert pipeline runs. Each bill's header is put through five checks in order; a bill only enters the write list if all five pass.

The problem is that the pass/fail flag is a single shared response object reused for every bill in the array:

  • Bill 1 fails validation -> not added to the write list, and the failure message is written to APIResponse.Response.
  • Bill 2 then passes validation -> the shared flag is reset to success and the failure message from bill 1 is overwritten.
  • The batch proceeds, writing bill 2 only. Bill 1 is silently dropped and produces no ActionResult entry at all.

Submit AP bills one per request. With a single bill in the array the shared-state behaviour is harmless and you get a clear pass/fail. With multiple bills you can lose a bill without any indication in the response. The record array length will be shorter than the array you sent — compare counts if you must batch.

3. It opens a System Five transaction

ProcessHeaderInfo begins a Tranlog transaction (BeginTransaction2) before writing. This is the only write path in the API that does so, and it means the header-plus-lines write for a single bill is atomic in System Five's own transaction log — unlike, say, an invoice.


Request shape

{
  "ConnectionInfo": { "TerminalNumber": 1 },
  "APBills": [
    {
      "HeaderInfo": {
        "Number":     "SUPP-INV-8891",
        "Pay":        4471,
        "Supplier":   4471,
        "Date":       "2026-09-01T00:00:00",
        "Total":      1250.00,
        "Department": 1,
        "PO":         "PO-2211",
        "Description":"September stock",
        "DueDate":    "2026-10-01T00:00:00"
      },
      "BillLines": [
        { "LedgerAccount": "5010.000", "Amount": 1000.00, "DbCd": "D",
          "Description": "Goods", "Supplier": 4471, "Department": 1 },
        { "LedgerAccountName": "FREIGHT", "Amount": 250.00, "DbCd": "D",
          "Description": "Freight", "Supplier": 4471, "Department": 1 }
      ],
      "BillCommentLines": [
        { "CommentLineIndex": 0, "CommentLineData": "Received in full" }
      ]
    }
  ]
}

Required header fields

Assembled dynamically per request:

FieldAlways requiredNotes
NumberyesThe supplier's bill/invoice number
PayyesPayee account unique
DateyesBill date, ISO 8601
SupplieryesSupplier account unique
Departmentonly when the dataset is departmentalisedAdded to the required list when the dataset reports more than zero departments

A missing required field fails the bill with:

Required parameter(s), Number, Date, not found

(The list is comma-separated with a trailing comma before not found.)

The five validations, in order

Each stops the bill at the first failure.

1. Required parameters

As above.

2. Verify_Bill_Date — date and total

  • Date must be present: The required [Date] value was missing from the Parameters!
  • Total must be present: The required [Total] value was missing from the Parameters!
  • The date must be postable — System Five checks it against the open booking period / minimum book month rules. A closed or out-of-range period fails with: Post Bill Exception: {System Five's message}

This is the check that most often rejects a backdated bill. Bills cannot be posted into a closed accounting period through the API any more than they can in System Five.

3. Verify_Bill_Department

Behaviour depends on whether the dataset is departmentalised:

DatasetRuleFailure message
DepartmentalisedDepartment must be presentBill Department Missing. This departmentalized dataset requires a Department value!
Departmentalisedmust be > 0Invalid Bill Department. This departmentalized dataset requires a Department value greater than ZERO!
Departmentalisedmust not exceed the configured maximumInvalid Bill Department. Department parameter exceeds maximum department for this dataset.
Not departmentalisedmust be 0Invalid Bill Department. This dataset only accepts ZERO as a department value!

Note the asymmetry: on a non-departmentalised dataset, sending "Department": 1 is an error. Send 0, or omit it (it is not on the required list in that case).

4. Verify_Bill_Payee — payee and supplier must exist

  • Pay must be present: The required [Pay] value was missing from the Parameters!
  • Pay must resolve to a real account: Payee Unique not found in dataset [{value}]
  • Supplier must be present: The required [Supplier] value was missing from the Parameters!
  • Supplier must resolve: Supplier Unique not found in dataset [{value}]

Pay and Supplier are separate concepts — the supplier who issued the bill and the account the payment is made to. They are usually the same unique, but the API validates them independently and both must be sent.

5. Verify_Bill_Lines — every ledger account must exist

For each entry in BillLines, the ledger account is resolved from one of:

FieldResolution
LedgerAccountParsed directly as a System Five ledger number
LedgerAccountNameResolved via GetLedgerAccountByConst using the line's Supplier — a named constant such as the supplier's default expense account

Every resolved account must exist in the chart of accounts. Failures accumulate into one message listing the offending values:

Ledger Account(s) not found in dataset [5010.000,5099.000,]
Ledger Account Name(s) not found in dataset [FREIGHT,]

A line with neither field, or one that resolves to a blank ledger number, also fails.

Important: Validate your account mapping once at integration time. The API gives you the full list of unresolvable accounts in a single message, so a dry run against a test dataset is the cheapest way to find mapping gaps.

Write phase

Once a bill passes validation:

  1. A System Five transaction is opened.
  2. ProcessHeaderInfo inserts the bill header.
  3. If the header succeeded (headerError empty), ProcessLines posts each line's trandata against the bill.
  4. The bill is updated and the result recorded.

On failure the messages are concatenated with carriage returns:

An error occurred during the insert of the record
{header error}
{line error}

RecordDesc is "{Number} - {Description}". RecordId is the bill unique.

Comment lines

BillCommentLines is an array of { "CommentLineIndex": n, "CommentLineData": "..." }, written as comment records against the bill.

Query parameters

ParameterEffect
Fields=...Shape RecordResult; implies a detailed response
DetailedResponse=YPopulate RecordResult

GET /APBill/APBills/{Unique}

0 returns all bills; a positive value returns one.

Query parameterNotes
FieldsComma-separated field names
PageSize / PageNumberBoth or neither; PageNumber is 1-based

Note the Swagger declares the path parameter as Number (a string) while the Delphi signature takes an integer unique. Send the bill's numeric Unique, not its bill number.

The response array key is APBill. Each record contains HeaderInfo, BillLines, BillCommentLines, and the payee/supplier objects (BillPayeeObject, BillSupplierObject).


GET /APBill/GetAPBillRecordCount

{ "SystemFive API Record Count": "APBills", "Record Count": "3391" }

GET /APBill/APBill_Handshake

{ "System Five APBill API": "Handshake", "Version": "1.2.3.4" }

Field reference

HeaderInfo

FieldTypeRequiredNotes
NumberstringyesSupplier's bill number
PayintegeryesPayee account unique; must exist
SupplierintegeryesSupplier account unique; must exist
DateISO 8601yesMust be postable in an open period
Totalnumberyes (checked by Verify_Bill_Date)
DepartmentintegerconditionalRequired and > 0 on departmentalised datasets; must be 0 otherwise
POstringno
Descriptionstringno
DueDateISO 8601no
Foreignstringno
CurrencyCodeintegerno
BookMonthISO 8601no
Job, Exptno
Uniqueintegerdo not sendSending an existing unique makes the bill a silent no-op

BillLines[]

FieldTypeNotes
LedgerAccountstringLedger number, e.g. 5010.000. Must exist
LedgerAccountNamestringAlternative to LedgerAccount; resolved against the line's Supplier
Amountnumber
DbCdstringDebit/credit indicator
Descriptionstring
SupplierintegerUsed to resolve LedgerAccountName
Departmentinteger
CurrencyCode, WholeCurrencyCode, CurrencyDate, DataDate

BillCommentLines[]

FieldType
CommentLineIndexinteger
CommentLineDatastring

Read-only in responses

Paid, FullyPaid, FullyPaidDate, Completed, APStage, EnterBy, EnterDate, EnterTime, PaidByDepartment