When to use this endpoint

Use this for a CRM2-style header before creating its lines. Use PurchaseOrder_new for the smaller legacy field set; use createFromJob or createFromRequisitionOrder for those multi-record workflows.

Authentication and permissions

Authenticate with signed API-key fields or a logged-in CRM session. Session requests use dispatcher CSRF protection when configured. API keys use their owner’s permissions.

Requires explicit View on Purchase Order or Purchase Orders, plus Add on either page. Non-administrators with legacy Purchase Orders visibility disabled (crm_access bit 256) are denied.

Parameters

ParameterTypeRequiredDescription
supplierID / supplierInteger / native picker objectYes on creationAvailable, active, non-held supplier ID, or native supplier={id:42}. The supplier must be visible through its own page/record scope.
formIDInteger-compatibleNoDefault 0 on creation. A nonzero value must identify an active type-5 Purchase Order form.
referenceTextNoAt most 32 Unicode characters in mapped validation; omitted creation uses the database default. Unlike the legacy new route, no "no reference" fallback is added.
poNumInteger-compatibleNoDisplayed order number; distinct from row ID. Creation selects the next number under the owner lock when omitted, or rejects a supplied nonpositive/used number.
target / targetType / targetIDNative picker / integer-compatibleNotarget={type:4,id:87}, or separate targetType/targetID. Link types: 0 No Link, 1 Contract, 2 Customer, 3 Quote, 4 Job, 5 Department, 6 Location, 7 Warehouse. Types 0/7 use ID 0. This header link does not itself allocate stock.
statusInteger-compatibleNoReceived status: 0 Open (creation default), 1 Part Received, 2 Complete, 3 Over-received, 4 Cancelled. This is separate from approval, sent history and metaStatus.
deliveryDateISO dateNoDelivery date, YYYY-MM-DD. Mapped create/update validates SQL-date input; checked updateField additionally applies its strict real-date check.
deliveryAddress / attention / instructionsTextNoDelivery address, contact/attention and supplier instructions. Omission on update leaves existing values.
deliveryNet / deliveryTaxNumeric-compatibleNoCarriage net and explicit tax amounts. Supplying a delivery tax code does not calculate deliveryTax on the server.
deliveryTaxcodeID / deliveryTaxcodeText / native pickerNoStored carriage tax code, or picker {id:"T1"}. Do not substitute a percentage for the explicit deliveryTax amount.
orderNet / orderTaxNumeric-compatibleNoMapped order amounts. Recalculation replaces persisted header totals with stored line totals plus carriage; this is not a way to recalculate line amounts.
taxInc / rowData / roIDInteger-compatible / text / integer-compatibleNoTax-inclusion flag, legacy row payload, and linked Requisition Order ID. roID alone does not convert a requisition or move its allocations.
customValues / meta / invoiceReceivedJSON-compatible valuesNoStored custom values, workflow metadata and invoice marker. Creation defaults customValues to {}. Existing invoice endpoints are preferable for maintaining the invoice marker.
metaLink / metaStatus / metaOwing / metaPaymentList / metaOrderFullyReceivedDateMapped metadata valuesNoPaths within meta: link, status, owing, paymentList and orderFullyReceivedDate. The last path is a date; the others use mapped text/JSON conversion.
cDate / lastEdited / createdByID / createdBy / dateApproved / dateSentDate-time / integer / pickerLegacy controlsServer-owned metadata. Creation uses the current actor and server times; a createdBy picker may only select that actor. Changed creation/edit/approval/sent metadata is rejected; use the dedicated workflow actions.

Example request

$result = FM_api('PurchaseOrder_createData', [
	'supplier'=>['id'=>'42'],
	'formID'=>'5',
	'reference'=>'PO-2026-1042',
	'deliveryNet'=>'10',
	'deliveryTax'=>'2',
]);

Example response

{
  "ok": 1,
  "id": 1042
}

Errors and unusual responses

ResponseCondition
{"error":"not logged in"}No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs.
{"error":"missing or invalid parameter: supplierID"}Missing or invalid supplier selector.
{"error":"supplier does not exist or is not available"}The supplier is hidden, missing, inactive or held.

Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.

Behaviour and edge cases

  • Requires an available, visible, non-held supplier. The form defaults to 0; a nonzero form must be active and type 5. Targets are validated for their own View permission and record scope; No Link/Warehouse use ID 0 and are unavailable to scoped roles that cannot view such orders.
  • Creation stamps the current actor and server creation/edit times. Supplied approval/sent/creation/edit timestamps and a different creator are rejected. The layout-createdBy picker may identify only the current actor.
  • The next displayed number is selected under the same tenant-owner lock as insertion. An explicitly supplied poNum must be positive and unused. The returned id is the inserted header ID, not poNum.
  • Unknown mapped keys are ignored. The mapper enforces reference length and typed values. Persisted totals are recalculated from line totals plus carriage; an empty header therefore contains carriage only. Layout formulas are not executed by this route.
  • Repeated successful calls create separate headers; there is no client idempotency key. A grouped/layout caller can include header and line creation in its owning transaction.

Side effects

  • Writes use the tenant-owner lock and a transaction or caller savepoint. A rejected operation rolls back its local writes and checked history. Earlier separate API requests remain committed.
  • Edit events, notifications and form follow-ups run after the owning commit. A saved/no-replay warning identifies committed data with a failed follow-up; inspect the saved state rather than repeating creation or sending.
  • Inserts the header, writes generic field and checked creation history, recalculates totals and queues the create notification. Direct creation does not add lines, receipts or stock containers.

See it in Fieldmotion

CRM1

No direct CRM1 caller was found. CRM1 uses PurchaseOrder_new and the legacy PurchaseOrderEntry_new/save line editor instead.

CRM2

In CRM2 Purchase Orders, click Create Purchase Order, choose the supplier/form/reference, and inspect Layout_setValue when saving. Its purchaseOrder mapping calls PurchaseOrder_createData internally; a nested call need not appear separately in the browser. The popup uses poDefaultForm when configured, or the only available form, and checks stock/supplier compatibility in the client.

Related endpoints