Purchase Orders
Overview
SoftCo supports two invoicing models. For non-PO invoices — expenses, ad-hoc services, or spend that does not require a formal commitment — invoices are coded and approved directly within SoftCo without any upstream purchase order. For PO-based invoices — where your organisation's procurement policy requires a purchase order before a supplier can invoice — SoftCo needs visibility of those POs from your ERP in order to validate and match against them. This API serves that second scenario.
The Purchase Orders API lets your ERP push purchase order data directly into SoftCo's AP platform, making each PO available for automated invoice matching. Once a PO is received, SoftCo can compare incoming supplier invoices against the committed quantities, unit prices, and GL coding on the order — reducing manual coding effort and accelerating invoice processing. Organisations using three-way matching can additionally pair this with the Goods Received API, requiring a confirmed receipt before an invoice can be approved.
A single call to POST /purchase-orders/v1 submits one or more complete purchase orders. Each order includes a header with order details and supplier information, together with one or more line items carrying the pricing, quantities, and GL coding that SoftCo needs for matching. SoftCo uses the po_number as the primary reference when matching supplier invoices.
Where Purchase Orders fit in the AP workflow
SoftCo's AP platform is the system of record for invoice processing. When a supplier invoice arrives that references a purchase order, SoftCo looks up the PO your ERP has already sent via this API and compares the invoice against it — validating the supplier, quantities, and unit prices. Invoices that match within tolerance can be auto-approved; those outside tolerance are routed for review. For organisations that also use non-PO invoicing, both flows coexist: PO-referenced invoices are matched automatically, while non-PO invoices follow a direct coding and approval workflow. The richer the PO data you send, the more effectively SoftCo can automate the matching process.
The Purchase Orders API uses a two-level payload structure. Header fields carry PO-level information — the order number, dates, supplier, totals, and delivery addresses. Line item fields (nested in the rows array) carry line-level detail — quantities, pricing, GL codes, and accounting objects. Both levels are submitted together in a single request.
When to call this endpoint
po_number. Use revision_number to indicate the amendment version.
Resubmitting a PO that SoftCo has already received refreshes the record without creating a duplicate. This makes it straightforward to resend after a correction or to replay a batch.
Quick Start
The minimum viable PO request — a single order with two line items. Copy this, swap in your credentials and real PO data, and send. The required fields at header level are record_id, po_number, supplier_number, and po_date. Each line item in the rows array requires row_id, line_number, quantity, uom, unit_price, and receipting_required.
POST /purchase-orders/v1 x-api-key: your-api-key-here Content-Type: application/Json Client_Id: your-client-id Client_Secret: your-client-secret { "objects": { "object": { "record": [ { "organization": "main", "record_id": "1", "po_number": "PO-2024-00891", "po_date": "2024-03-15", "supplier_number": "SUP-001234", "supplier_name": "Acme Office Supplies Ltd", "currency": "EUR", "po_net_total": 1250.00, "po_gross_total": 1537.50, "rows": [ { "row_id": "1", "line_number": 1, "line_description": "A4 Copy Paper — 5 Ream Box", "quantity": 10, "uom": "BOX", "unit_price": 62.50, "row_net_total": 625.00, "receipting_required": "true" "gl_code": "6100" }, { "row_id": "2", "line_number": 2, "line_description": "Printer Ink Cartridge — Black", "quantity": 5, "uom": "EA", "unit_price": 125.00, "row_net_total": 625.00, "receipting_required": "true" "gl_code": "6100" } ] } ] } } }
A successful response looks like this:
{ "status": "SUCCESS", "total": 1, "succeeded": 1, "failed": 0, "errors": [], "message": "All records processed successfully." }
A 202 Accepted means your purchase order has been validated and queued. The integration layer picks it up, transforms it into SoftCo's internal format, and delivers it for AP processing. The PO will be available for invoice matching once delivered. If it does not appear within a few minutes, see the Troubleshooting section.
How It Works
Every request follows the same pipeline. Your system sends once — validation, transformation, and delivery to SoftCo are handled automatically.
Layer
Queue
AP
POST /purchase-orders/v1 with your credentials and one or more complete PO records — each containing a header and at least one line item in the rows array.401 Unauthorized before your payload is processed.rows array. If anything fails, you get a 400 Bad Request with a field-level error message. Nothing is queued until validation passes.202 Accepted immediately — no further action is needed from your side.po_number and made available for invoice matching. If SoftCo is temporarily unavailable, the queue holds the record and retries delivery automatically.202 Accepted does not mean the PO is available in SoftCo yet — it means your request has been received, validated, and queued successfully. Delivery to SoftCo AP is asynchronous and typically completes within minutes. Resending a PO is always safe; SoftCo will refresh the record using the po_number as the key. If a PO does not appear after a reasonable period, contact your SoftCo Customer Success representative.
Rate limits are configured per customer during onboarding. If you receive a 429 Too Many Requests response, reduce your request rate and retry after a short delay. For your specific rate limit thresholds, contact your SoftCo Customer Success representative.
Authentication
All requests require an API key and OAuth 2.0 credentials. Your credentials are provided during onboarding — keep them secure and never expose them in client-side code or version control.
Requests with a missing or invalid API key are rejected by the gateway immediately and never reach the integration layer.
Required Headers
| Header | Value | Notes |
|---|---|---|
| x-api-key | Your assigned API key | Provided at onboarding. Required on every request. |
| Client_Id | OAuth 2.0 Client ID | Provided at onboarding. |
| Client_Secret | OAuth 2.0 Client Secret | Treat as a password — do not share or log. |
| Access_Token_URL | OAuth 2.0 token endpoint | Provided at onboarding. |
| Content-Type | application/Json | Must be present on every POST request. |
Contact your SoftCo Customer Success representative immediately to rotate your API key. Do not attempt to use the same credentials across multiple customer environments.
Response Codes
| Code | Status | Meaning | What to do |
|---|---|---|---|
| 202 | Accepted | Payload validated and queued. Delivery to SoftCo is in progress. | Nothing — your purchase order is on its way. |
| 207 | Multi-Status | Batch partially accepted. Valid records were queued; one or more failed validation. Check the errors array for details on failing records. | Inspect the errors array by record index, correct the failing records, and resubmit only those that failed. |
| 400 | Bad Request | Payload failed schema validation. A field-level error is in the response body. | Read the message and errors fields, fix the payload, retry. See Troubleshooting. |
| 401 | Unauthorized | API key missing or invalid. | Check your x-api-key header value. |
| 403 | Forbidden | Valid API key but no permission for this endpoint. | Contact your SoftCo Customer Success representative. |
| 404 | Not Found | Endpoint path is incorrect. | Verify the URL — check for typos or trailing slashes. |
| 408 | Request Timeout | Request took too long to process. | Retry once after a short delay. |
| 429 | Too Many Requests | Rate limit exceeded. | Slow your request rate and retry after the delay indicated. |
| 500 | Internal Server Error | Unexpected error on our side. | Retry once. If it persists, raise a ticket with the requestId. |
| 503 | Service Unavailable | Service temporarily unavailable. | Retry after a short delay. |
POST /purchase-orders/v1
Submit one or more purchase orders to SoftCo. Each record contains a PO header with supplier and address information, and a rows array with one or more line items. The payload is validated and queued for delivery to SoftCo AP, where the PO becomes available for invoice matching by po_number.
Request Headers
| Header | Value |
|---|---|
| x-api-key | Your assigned API key. |
| Content-Type | application/Json |
| Client_Id | Your OAuth 2.0 Client ID. |
| Client_Secret | Your OAuth 2.0 Client Secret. |
| Access_Token_URL | Your OAuth 2.0 token endpoint URL. |
Request Body
The Purchase Orders API uses the standard SoftCo iPaaS envelope: objects → object → record. For all PO requests, record is a JSON array. Each record carries PO header fields and a nested rows array of line item objects.
Single purchase order
Batch (multiple purchase orders)
Field Reference
The Purchase Orders API carries 61 fields across two levels: PO header fields that apply to the entire order, and line item fields nested within the rows array. The tables below cover each group in turn.
PO Header Fields
These fields sit directly within each record object and describe the purchase order as a whole — the order reference, dates, totals, and parties involved.
| Field | Type | Required | Description |
|---|---|---|---|
| organization | string | Recommended |
The SoftCo organisation entity this PO belongs to. If omitted, the integration defaults this value to "main".
Defaults to
"main" if omitted. |
| system | string | Optional | Identifier for the source system submitting the PO — for example the name or instance of your ERP. Used for traceability and audit purposes. |
| record_id | string | Required | Your system's internal reference for this record submission. Useful for correlating API submissions back to records in your ERP for troubleshooting. |
| po_number | string | Required |
The purchase order number from your ERP. This is the primary key SoftCo uses to match incoming supplier invoices to a purchase commitment. Must be unique per order.
e.g. "PO-2024-00891"
|
| po_type | string | Optional |
Indicates whether the purchase order is being created, updated, or closed. Accepted values are:
NEWUPDATECLOSE
Caution : Once a purchase order is marked as closed, it cannot be reopened for invoicing.
|
| revision_number | string | Optional | The amendment version of this PO. Increment each time the PO is revised and resent to SoftCo. Helps SoftCo track which version of a PO is current.
e.g. "0""1""2"
|
| creation_date | string | Optional | The date the PO was originally created in your ERP. Format: YYYY-MM-DD.
e.g. "2024-03-10"
|
| po_date | string | Required | The effective date of the purchase order. This is the date the order was raised or approved and is used by SoftCo for period-based matching and reporting. Format: YYYY-MM-DD.
e.g. "2024-03-15"
|
| currency | string | Recommended | The currency of the purchase order using ISO 4217 three-letter currency codes. Required for accurate invoice matching when your organisation operates across multiple currencies.
"EUR""USD""GBP"
|
| buyer | string | Optional | The name or identifier of the employee or user who raised the purchase order. Used for ownership and approval tracking in SoftCo.
e.g. "Jane Murphy"
|
| approver | string | Optional | The name or identifier of the person who approved the purchase order in your ERP. Carried through to SoftCo for audit trail and invoice routing purposes.
e.g. "Tom Walsh"
|
| alternative_recipient | string | Optional | An alternative contact or recipient for the order — for example, a project manager or department head who should be notified about delivery or invoice queries. |
| po_net_total | number (decimal) | Recommended | The total net value of the purchase order before tax. Must be provided as a numeric value with up to 2 decimal places, without any thousand separators such as commas. Should equal the sum of all row_net_total line values.
e.g. 1250.00
|
| po_tax_total | number (decimal) | Optional | The total tax amount on the purchase order. Must be provided as a numeric value with up to 2 decimal places, without any thousand separators such as commas. Derived from line-level tax codes but can be supplied at header level for reconciliation.
e.g. 287.50
|
| po_gross_total | number (decimal) | Recommended | The total gross value of the purchase order including tax. Must be provided as a numeric value with up to 2 decimal places, without any thousand separators such as commas. Should equal po_net_total plus po_tax_total.
e.g. 1537.50
|
Supplier Fields
Supplier fields identify the vendor associated with this purchase order. supplier_number is the key reference SoftCo uses to link the PO to a vendor record. The remaining supplier fields carry address and identification detail for the supplier as recorded in your ERP at time of order.
| Field | Type | Required | Description |
|---|---|---|---|
| supplier_number | string | Required |
The vendor ID from your ERP — the primary identifier SoftCo uses to link this PO to a supplier record. Should match the supplier_number sent via the Vendor Data API.
e.g. "SUP-001234"
|
| supplier_site_code | string | Optional | The site or location code for this supplier in your ERP, where a supplier has multiple sites. Used to disambiguate supplier addresses and payment terms.
e.g. "DUBLIN-01"
|
| supplier_name | string | Optional | The supplier's trading name as recorded in your ERP at the time the PO was raised. Carried through for display and reference in SoftCo.
e.g. "Acme Office Supplies Ltd"
|
| supplier_address1 | string | Optional | First line of the supplier's address. |
| supplier_address2 | string | Optional | Second line of the supplier's address. |
| supplier_city | string | Optional | The city of the supplier's address. |
| supplier_postcode | string | Optional | The postcode or ZIP code of the supplier's address. |
| supplier_state | string | Optional | The state, province, or county of the supplier's address. |
| supplier_country | string | Optional | The country of the supplier's address. Recommended if your organisation works with international suppliers.
e.g. "Ireland""United Kingdom"
|
Address Fields
Two address groups are supported: the invoice-to address is where the supplier should direct their invoice; the deliver-to address is where the ordered goods or services are to be received. Both groups are optional but recommended where your organisation uses centralised invoicing or multiple delivery locations.
| Field | Type | Required | Description |
|---|---|---|---|
| Invoice-To Address | |||
| invoice_to_name | string | Optional | The name of the entity or department to whom the supplier should address their invoice. |
| invoice_to_address1 | string | Optional | First line of the invoice-to address. |
| invoice_to_address2 | string | Optional | Second line of the invoice-to address. |
| invoice_to_city | string | Optional | City of the invoice-to address. |
| invoice_to_postcode | string | Optional | Postcode or ZIP of the invoice-to address. |
| invoice_to_state | string | Optional | State, province, or county of the invoice-to address. |
| invoice_to_country | string | Optional | Country of the invoice-to address. |
| Deliver-To Address | |||
| deliver_to_name | string | Optional | The name of the person, department, or site receiving the goods or services. |
| deliver_to_address1 | string | Optional | First line of the delivery address. |
| deliver_to_address2 | string | Optional | Second line of the delivery address. |
| deliver_to_city | string | Optional | City of the delivery address. |
| deliver_to_postcode | string | Optional | Postcode or ZIP of the delivery address. |
| deliver_to_state | string | Optional | State, province, or county of the delivery address. |
| deliver_to_country | string | Optional | Country of the delivery address. |
Line Item Fields (rows array)
Line item fields are nested within the rows array inside each PO record. Each element of the rows array represents one purchase order line — with its own quantity, unit price, GL coding, and optional accounting object splits. Every PO record must contain at least one row.
| Field | Type | Required | Description |
|---|---|---|---|
| Line Identification | |||
| row_id | string | Required | A unique identifier for this line within the PO, typically the line's sequence number or internal row key from your ERP. Used to distinguish lines when multiple rows are present.
e.g. "1""2"
|
| line_number | number (integer) | Required | The line number as it appears on the purchase order — the reference a supplier would see on the printed or emailed PO. Used by SoftCo for invoice line matching.
e.g. 1210
|
| item_number | string | Optional | Your ERP's internal item or product code for the goods or service on this line. Where the item is managed as a catalogue item in your ERP, provide this code for richer matching.
e.g. "ITEM-00452"
|
| line_description | string | Recommended | A human-readable description of the goods or service being ordered on this line. This is the description that appears on the PO and is used by SoftCo for display and matching context.
e.g. "A4 Copy Paper — 5 Ream Box"
|
| supplier_item_number | string | Optional | The supplier's own product reference or catalogue number for the item on this line. Useful for cross-referencing supplier invoices that cite the supplier's part number rather than yours. |
| Quantity & Pricing | |||
| quantity | number (decimal) | Required | The quantity ordered on this line. SoftCo uses this for quantity-based invoice matching — checking that the invoiced quantity does not exceed the ordered quantity. Must be provided as a numeric value with up to 6 decimal places, without any thousand separators such as commas.
e.g. 102.550.750
|
| uom | string | Required | The unit of measure for the quantity on this line. Should match the unit of measure used on the supplier's invoice to enable accurate matching.
"EA""BOX""KG""HR""DAY"
|
| unit_price | number (decimal) | Required | The agreed unit price for one unit of the item on this line. SoftCo compares this against the invoiced unit price during matching. Whether this is a net or gross price is indicated by unit_price_is_net. Must be provided as a numeric value with up to 2 decimal places, without any thousand separators such as commas.
e.g. 62.50125.00
|
| unit_price_is_net | string | Optional | Indicates whether the unit_price is a net (pre-tax) price. Send "true" if the unit price excludes tax, "false" if it is a gross price inclusive of tax.
"true""false"
|
| row_net_total | number (decimal) | Recommended | The total net value for this line (quantity × unit price before tax). Must be provided as a numeric value with up to 2 decimal places, without any thousand separators such as commas. Should equal quantity × unit_price when unit_price_is_net is true.
e.g. 625.00
|
| row_gross_total | number (decimal) | Optional | The total gross value for this line including tax. Must be provided as a numeric value with up to 2 decimal places, without any thousand separators such as commas.
e.g. 768.75
|
| Receipting & Matching Controls | |||
| receipting_required | string | Required | Controls whether a goods receipt or service confirmation is required before SoftCo will approve an invoice for this line. Send "true" to enable three-way matching (PO + GRN + invoice). Send "false" for two-way matching (PO + invoice only).
"true""false"
|
| exact_GRN_level_match | string | Optional | When receipting is required, this flag controls whether SoftCo requires an exact match between the goods receipt quantity and the invoiced quantity. Send "true" to enforce exact matching; "false" to allow tolerance-based matching.
"true""false"
|
| by_pass_confirmation | string | Optional | When set to "true", instructs SoftCo to bypass the normal confirmation or approval step for this line. Use only where the line has already been approved upstream and confirmation in SoftCo is not required.
"true""false"
|
| GL & Tax Coding | |||
| gl_code | string | Recommended | The general ledger account code for this line. Should match a GL code that has been synchronised to SoftCo via the GL Account Codes API. Providing this at PO level pre-populates the coding on matched invoices, reducing manual coding effort.
e.g. "6100""7200"
|
| tax_code | string | Recommended | The tax code applicable to this line — for example the VAT rate or tax treatment. Should correspond to a tax code configured in SoftCo.
e.g. "T1""T0""T9"
|
| Accounting Objects (accounting_object_1 – accounting_object_9) | |||
| accounting_object_1 through accounting_object_9 |
string | Optional |
Up to nine accounting dimension values — cost centres, departments, projects, or any other segmentation dimensions configured in SoftCo. These correspond directly to the accounting object dimensions in the Accounting Objects API. Only populate the dimensions your organisation uses; unused fields can be omitted or left empty.
e.g. accounting_object_1: "CC-MKTG"accounting_object_2: "PRJ-0042"
|
Providing gl_code, tax_code, and accounting object values at line level means SoftCo can automatically pre-populate the coding on matched invoices — eliminating manual coding and accelerating the approval workflow for your AP team.
Example Requests
Single purchase order with two lines
POST /purchase-orders/v1 x-api-key: your-api-key-here Content-Type: application/Json Client_Id: your-client-id Client_Secret: your-client-secret { "objects": { "object": { "record": [ { "organization": "main", "system": "NetSuite", "record_id": "5", "po_number": "PO-2024-00891", "po_type": "NEW", "po_date": "2024-03-15", "currency": "EUR", "buyer": "Jane Murphy", "approver": "Tom Walsh", "supplier_number": "SUP-001234", "supplier_name": "Acme Office Supplies Ltd", "supplier_address1": "14 Parkway Business Centre", "supplier_city": "Dublin", "supplier_postcode": "D12 XF70", "supplier_country": "Ireland", "deliver_to_name": "Goods Inward — Building A", "deliver_to_address1": "1 Corporate Park", "deliver_to_city": "Dublin", "deliver_to_country": "Ireland", "po_net_total": 1250.00, "po_tax_total": 287.50, "po_gross_total": 1537.50, "rows": [ { "row_id": "1", "line_number": 1, "line_description": "A4 Copy Paper — 5 Ream Box", "quantity": 10, "uom": "BOX", "unit_price": 62.50, "unit_price_is_net": "true", "row_net_total": 625.00, "receipting_required": "true", "gl_code": "6100", "tax_code": "T1", "accounting_object_1": "CC-ADMIN" }, { "row_id": "2", "line_number": 2, "line_description": "Printer Ink Cartridge — Black", "quantity": 5, "uom": "EA", "unit_price": 125.00, "unit_price_is_net": "true", "row_net_total": 625.00, "receipting_required": "true", "gl_code": "6100", "tax_code": "T1", "accounting_object_1": "CC-ADMIN" } ] } ] } } }
Batch — two purchase orders in one request
POST /purchase-orders/v1 x-api-key: your-api-key-here Content-Type: application/Json Client_Id: your-client-id Client_Secret: your-client-secret { "objects": { "object": { "record": [ { "record_id": "5", "po_number": "PO-2024-00891", "po_date": "2024-03-15", "supplier_number": "SUP-001234", "currency": "EUR", "po_net_total": 1250.00, "po_gross_total": 1537.50, "rows": [ { "row_id": "1", "line_number": 1, "quantity": 10, "uom": "BOX", "unit_price": 62.50, "receipting_required": "true", "gl_code": "6100" } ] }, { "record_id": "6", "po_number": "PO-2024-00892", "po_date": "2024-03-15", "supplier_number": "SUP-000876", "currency": "EUR", "po_net_total": 3400.00, "po_gross_total": 4182.00, "rows": [ { "row_id": "1", "line_number": 1, "line_description": "IT Consulting Services", "quantity": 8, "uom": "DAY", "unit_price": 425.00, "receipting_required": "true", "gl_code": "7200", "accounting_object_1": "PRJ-0042" } ] } ] } } }
Validation
Every request is validated against the purchase order schema before any processing takes place. If validation fails, the entire request is rejected immediately — nothing is queued or forwarded to SoftCo. You receive a clear error identifying the field or structural issue.
What is checked
| Check | Detail |
|---|---|
| Envelope structure | The payload must use the PO envelope: objects as a JSON object, each containing a record array. Using the master data envelope (objects as an object with a record key) will fail validation. |
| Required header fields | record_id, po_number, po_date, and supplier_number must be present in every record. |
| rows array presence | Every PO record must contain a rows array with at least one line item object. |
| Required line fields | Every row must contain row_id, line_number, quantity, uom, unit_priceand receipting_required. |
| Field types | All fields must be in line with the field type. Sending an incorrect data type for any field will fail validation. |
| Non-empty required values | Required fields must not be empty strings. |
Validation error response
When validation fails, the API returns a 400 Bad Request with a JSON body identifying the failing field or structure:
{ "status": "FAILED", "total": 1, "succeeded": 0, "failed": 1, "errors": [{ "index": 0, "error": "required key [po_number] not found" }], "message": "1 of 1 records failed validation." }
The errors array identifies the exact field or path that failed, and the message field provides a summary. Fix the identified issue and resubmit.
Troubleshooting
The most common error scenarios and how to resolve them.
errors array will identify the missing field — for example required key [po_number] not found.po_number, po_date, and supplier_number are present in every record object. None can be omitted or null.rows array with at least one line item object. Sending a record without rows, or with an empty rows: [], will fail validation.rows array with at least one complete line item in every PO record.errors array will reference the failing field — for example a missing quantity, uom, or unit_price.row_id, line_number, quantity, uom, and unit_price. None can be omitted or null.Resubmitting a PO with the same po_number is safe — SoftCo will update the existing record with the values in the new request. This makes it straightforward to resend after a correction or amendment without needing to delete the original first.
Example Responses
Success — 202 Accepted
{ "status": "SUCCESS", "total": 1, "succeeded": 1, "failed": 0, "errors": [], "message": "All records processed successfully." }
Partial success — 207 Multi-Status
{ "status": "PARTIAL", "total": 3, "succeeded": 2, "failed": 1, "errors": [{ "index": 1, "error": "required key [supplier_number] not found" }], "message": "2 of 3 records processed successfully. 1 record failed validation." }
Validation failure — 400 Bad Request
{ "status": "FAILED", "total": 1, "succeeded": 0, "failed": 1, "errors": [{ "index": 0, "error": "required key [supplier_number] not found" }], "message": "1 of 1 records failed validation." }
Authentication failure — 401 Unauthorized
{ "status": "FAILED", "message": "Unauthorized" }
What's Next
Purchase Orders is one of two inbound transaction APIs in the SoftCo iPaaS. The Goods Received API completes the three-way matching picture by delivering item receipt data — enabling SoftCo to automatically validate invoices against both the PO and the confirmed delivery.