Each event below shows when we send it, the fields in its standard body and an example. Every delivery also carries the headers listed on the Overview.
A sales order was created
sales_order.created
Sent when Syncor creates the sales order for one of your purchase requests. Identify the order by purchaseRequest.number and salesOrder.number.
This is the standard (canonical) JSON body. If your subscription uses a mapping profile, the body follows that profile instead, as JSON or CSV. Use Preview on the profile in the developer portal to see it.
Fields
| Field | Type | Always present | Description |
|---|---|---|---|
event.id |
string | Yes | Event ID |
event.type |
string | Yes | Event Type |
event.version |
string | Yes | Event Version |
event.occurredAt |
string | Yes | Occurred At |
event.correlationId |
string | No, may be null | Correlation ID |
salesOrder.number |
string | Yes | Sales Order Number |
salesOrder.createdAt |
string | No, may be null | Sales Order Created At |
salesOrder.currency |
string | No, may be null | Currency |
salesOrder.totalAmount |
string | No, may be null | Total Amount |
salesOrder.markupTotalAmount |
string | No, may be null | Markup Total Amount |
purchaseRequest.id |
string | Yes | Purchase Request ID |
purchaseRequest.number |
string | Yes | Purchase Request Number |
purchaseRequest.salesOrigin |
string | No, may be null | Sales Origin |
purchaseRequest.requiredByDate |
string | No, may be null | Required By Date |
purchaseRequest.scheduledDeliveryDate |
string | No, may be null | Scheduled Delivery Date |
purchaseRequest.notes |
string | No, may be null | Notes |
purchaseRequest.chargeableToClient |
boolean | No, may be null | Chargeable To Client |
customer.name |
string | No, may be null | Customer Name |
customer.accountNumber |
string | Yes | Customer Account Number |
site.name |
string | No, may be null | Site Name |
site.code |
string | No, may be null | Site Code |
customerDepartment.code |
string | No, may be null | Customer Department Code |
customerDepartment.name |
string | No, may be null | Customer Department Name |
contract.name |
string | No, may be null | Contract Name |
contract.code |
string | No, may be null | Contract Code |
contract.address |
string | No, may be null | Contract Address |
requester.name |
string | No, may be null | Requester Name |
requester.firstName |
string | No, may be null | Requester First Name |
requester.lastName |
string | No, may be null | Requester Last Name |
requester.email |
string | No, may be null | Requester Email |
requester.phone |
string | No, may be null | Requester Phone |
requester.employeeCode |
string | No, may be null | Requester Employee Code |
deliveryLocation.name |
string | No, may be null | Delivery Location Name |
deliveryLocation.code |
string | No, may be null | Delivery Location Code |
deliveryLocation.address |
string | No, may be null | Delivery Location Address |
deliveryLocation.latitude |
string | No, may be null | Delivery Location Latitude |
deliveryLocation.longitude |
string | No, may be null | Delivery Location Longitude |
referenceFields |
array | No, may be null | Reference Fields |
lines[].lineNumber |
integer | Yes | Line Number |
lines[].salesOrderLineNumber |
string | No, may be null | Sales Order Line Number |
lines[].sku |
string | No, may be null | SKU |
lines[].itemNumber |
string | No, may be null | Item Number |
lines[].masterProductSku |
string | No, may be null | Master Product SKU |
lines[].variantNumber |
string | No, may be null | Variant Number |
lines[].customerProductNumber |
string | No, may be null | Customer Product Number |
lines[].customerProductName |
string | No, may be null | Customer Product Name |
lines[].customerProductDescription |
string | No, may be null | Customer Product Description |
lines[].name |
string | No, may be null | Line Name |
lines[].brand |
string | No, may be null | Brand |
lines[].description |
string | No, may be null | Line Description |
lines[].quantity |
string | Yes | Quantity |
lines[].uom |
string | No, may be null | UOM |
lines[].uomBase |
string | No, may be null | Base UOM |
lines[].uomQuantity |
string | No, may be null | UOM Quantity |
lines[].unitPrice |
string | No, may be null | Unit Price |
lines[].lineAmount |
string | No, may be null | Line Amount |
lines[].markupPercentage |
string | No, may be null | Markup Percentage |
lines[].markupUnitPrice |
string | No, may be null | Markup Unit Price |
lines[].markupLineAmount |
string | No, may be null | Markup Line Amount |
lines[].customerVatPercentage |
string | No, may be null | Customer VAT Percentage |
lines[].glCodeValue |
string | No, may be null | GL Code |
lines[].glCodeName |
string | No, may be null | GL Code Name |
lines[].defaultCategory |
string | No, may be null | Default Category |
lines[].referenceFields |
array | No, may be null | Line Reference Fields |
Example
{
"event": {
"id": "evt_01JTEST",
"type": "sales_order.created",
"version": "1.0",
"occurredAt": "2026-05-14T08:00:00Z",
"correlationId": "evt_syncor_123"
},
"salesOrder": {
"number": "SO-100123",
"createdAt": "2026-05-14T08:00:00Z",
"currency": "DKK",
"totalAmount": "1234.56",
"markupTotalAmount": "1400.00"
},
"purchaseRequest": {
"id": "12345",
"number": "PR-100045",
"salesOrigin": "SYNCOR_PR",
"requiredByDate": "2026-05-20",
"scheduledDeliveryDate": "2026-05-21",
"notes": "Customer note",
"chargeableToClient": true
},
"customer": {
"name": "Example Customer",
"accountNumber": "CUST-001"
},
"site": {
"name": "Site A",
"code": "SITE-A"
},
"customerDepartment": {
"code": "OPS",
"name": "Operations"
},
"contract": {
"name": "Main Facilities Contract",
"code": "MFC-001",
"address": "Somewhere 123"
},
"requester": {
"name": "Jane Doe",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"phone": "+4512345678",
"employeeCode": "EMP-123"
},
"deliveryLocation": {
"name": "Warehouse 1",
"code": "WH-1",
"address": "Somewhere 123",
"latitude": "55.6761",
"longitude": "12.5683"
},
"lines": [
{
"lineNumber": 1,
"salesOrderLineNumber": "1.000000",
"sku": "ITEM-001",
"itemNumber": "ITEM-001",
"masterProductSku": null,
"variantNumber": null,
"customerProductNumber": "CUST-SKU-001",
"customerProductName": "Customer Product Name",
"customerProductDescription": "Customer product description",
"name": "Product Name",
"brand": "Brand A",
"description": "Product Description",
"quantity": "10",
"uom": "PCS",
"uomBase": "PCS",
"uomQuantity": "1",
"unitPrice": "12.00",
"lineAmount": "120.00",
"markupPercentage": "15",
"markupUnitPrice": "13.80",
"markupLineAmount": "138.00",
"customerVatPercentage": "25",
"glCodeValue": "60009020",
"glCodeName": "Branch Supplies - Refreshment",
"defaultCategory": "Branch Supplies",
"referenceFields": [
{
"key": "Asset Tag",
"label": "Asset Tag",
"value": "ASSET-1",
"type": "string"
}
]
}
],
"referenceFields": [
{
"key": "Cost Center",
"label": "Cost Center",
"value": "CC-100",
"type": "string"
}
]
}
A delivery note is available
delivery_note.available
Sent when a delivery note for one of your orders is recorded, including proof-of-delivery details where available. The same delivery note can be sent again if it changes; it keeps the same event ID.
This is the standard (canonical) JSON body. If your subscription uses a mapping profile, the body follows that profile instead, as JSON or CSV. Use Preview on the profile in the developer portal to see it.
Fields
| Field | Type | Always present | Description |
|---|---|---|---|
event.id |
string | Yes | Event ID |
event.type |
string | Yes | Event Type |
event.version |
string | Yes | Event Version |
event.occurredAt |
string | Yes | Occurred At |
deliveryNote.number |
string | Yes | Delivery Note Number |
deliveryNote.deliveryDate |
string | No, may be null | Delivery Date |
deliveryNote.salesOrderStatus |
string | No, may be null | Sales Order Status |
deliveryNote.hasEpod |
boolean | No, may be null | Has ePOD |
deliveryNote.epodReportAvailable |
boolean | No, may be null | ePOD Report Available |
deliveryNote.epodSignatureCount |
integer | No, may be null | ePOD Signature Count |
deliveryNote.epodPhotoCount |
integer | No, may be null | ePOD Photo Count |
deliveryNote.epodFile.fileName |
string | No, may be null | ePOD File Name |
deliveryNote.epodFile.mimeType |
string | No, may be null | ePOD File MIME Type |
deliveryNote.epodFile.byteSize |
integer | No, may be null | ePOD File Size (bytes) |
deliveryNote.epodFile.sha256 |
string | No, may be null | ePOD File SHA-256 |
deliveryNote.epodFile.contentAvailable |
boolean | No, may be null | ePOD File Content Available |
deliveryNote.epodFile.contentBase64 |
string | No, may be null | ePOD File Content (base64) |
salesOrder.number |
string | Yes | Sales Order Number |
purchaseRequest.number |
string | No, may be null | Purchase Request Number |
customer.accountNumber |
string | Yes | Customer Account Number |
customer.name |
string | No, may be null | Customer Name |
lines[].lineNumber |
integer | Yes | Line Number |
lines[].productNumber |
string | No, may be null | Product Number |
lines[].orderedQuantity |
number | No, may be null | Ordered Quantity |
lines[].deliveredQuantity |
number | No, may be null | Delivered Quantity |
lines[].confirmedDeliveredQuantity |
number | No, may be null | Confirmed Delivered Quantity |
lines[].unitOfMeasure |
string | No, may be null | Unit of Measure |
lines[].eligibleForInvoice |
boolean | No, may be null | Eligible for Invoice |
Example
{
"event": {
"id": "evt_01JDNTEST",
"type": "delivery_note.available",
"version": "1.0",
"occurredAt": "2026-05-26T12:00:00Z"
},
"deliveryNote": {
"number": "DN-100123",
"deliveryDate": "2026-05-26",
"salesOrderStatus": "Delivered",
"hasEpod": true,
"epodReportAvailable": true,
"epodSignatureCount": 1,
"epodPhotoCount": 3
},
"salesOrder": {
"number": "SO-100123"
},
"purchaseRequest": {
"number": "PR-100123"
},
"customer": {
"accountNumber": "CUST-001",
"name": "Example Customer"
},
"lines": [
{
"lineNumber": 1,
"productNumber": "SKU-001",
"orderedQuantity": 10,
"deliveredQuantity": 10,
"confirmedDeliveredQuantity": 10,
"unitOfMeasure": "EA",
"eligibleForInvoice": true
}
]
}
An invoice is available
invoice.available
Sent when an invoice for one of your orders is recorded. The same invoice can be sent again if it changes; it keeps the same event ID.
This is the standard (canonical) JSON body. If your subscription uses a mapping profile, the body follows that profile instead, as JSON or CSV. Use Preview on the profile in the developer portal to see it.
Fields
| Field | Type | Always present | Description |
|---|---|---|---|
event.id |
string | Yes | Event ID |
event.type |
string | Yes | Event Type |
event.version |
string | Yes | Event Version |
event.occurredAt |
string | Yes | Occurred At |
invoice.number |
string | Yes | Invoice Number |
invoice.invoiceDate |
string | No, may be null | Invoice Date |
invoice.totalAmount |
number | No, may be null | Total Amount |
invoice.taxAmount |
number | No, may be null | Tax Amount |
invoice.currencyCode |
string | No, may be null | Currency Code |
invoice.syncedToSyncorAt |
string | No, may be null | Synced to Syncor At |
salesOrder.number |
string | Yes | Sales Order Number |
purchaseRequest.number |
string | No, may be null | Purchase Request Number |
customer.accountNumber |
string | Yes | Customer Account Number |
customer.name |
string | No, may be null | Customer Name |
lines[].lineNumber |
integer | Yes | Line Number |
lines[].productNumber |
string | No, may be null | Product Number |
lines[].productName |
string | No, may be null | Product Name |
lines[].invoicedQuantity |
number | No, may be null | Invoiced Quantity |
lines[].lineAmount |
number | No, may be null | Line Amount |
lines[].salesPrice |
number | No, may be null | Sales Price |
lines[].unitOfMeasure |
string | No, may be null | Unit of Measure |
Example
{
"event": {
"id": "evt_01JINVTEST",
"type": "invoice.available",
"version": "1.0",
"occurredAt": "2026-05-26T12:00:00Z"
},
"invoice": {
"number": "INV-100123",
"invoiceDate": "2026-05-26",
"totalAmount": 1234.56,
"taxAmount": 184.32,
"currencyCode": "SAR",
"syncedToSyncorAt": null
},
"salesOrder": {
"number": "SO-100123"
},
"purchaseRequest": {
"number": "PR-100123"
},
"customer": {
"accountNumber": "CUST-001",
"name": "Example Customer"
},
"lines": [
{
"lineNumber": 1,
"productNumber": "SKU-001",
"productName": "Product Name",
"invoicedQuantity": 10,
"lineAmount": 1000,
"salesPrice": 100,
"unitOfMeasure": "EA"
}
]
}
Catalog changes are available
catalog.changes.available
A notification only: new catalog changes can be read from GET /jsonapi/v1/catalog/changes, starting from the cursor you last processed. The links point to the relevant endpoints.
This is the standard (canonical) JSON body. If your subscription uses a mapping profile, the body follows that profile instead, as JSON or CSV. Use Preview on the profile in the developer portal to see it.
Fields
| Field | Type | Always present | Description |
|---|---|---|---|
event.id |
string | Yes | Event ID |
event.name |
string | Yes | Event Name |
event.version |
string | Yes | Event Version |
event.occurredAt |
string | Yes | Occurred At |
catalog.projectionRunId |
string | Yes | Catalog Projection Run ID |
catalog.latestCursor |
integer | Yes | Latest Catalog Change Cursor |
catalog.productsUpserted |
integer | Yes | Products Upserted |
catalog.productsRemoved |
integer | Yes | Products Removed |
catalog.categoriesChanged |
integer | Yes | Categories Changed |
catalog.attributesChanged |
integer | Yes | Attributes Changed |
links.changes |
string | Yes | Catalog Changes Feed |
links.products |
string | Yes | Catalog Products Feed |
links.categories |
string | Yes | Catalog Categories Feed |
links.attributes |
string | Yes | Catalog Attributes Feed |
Example
{
"event": {
"id": "evt_01JCATALOGAVAILABLE",
"name": "catalog.changes.available",
"version": "1.0",
"occurredAt": "2026-08-20T12:00:00Z"
},
"catalog": {
"projectionRunId": "01JCATALOGRUN000000000001",
"latestCursor": 423,
"productsUpserted": 2,
"productsRemoved": 1,
"categoriesChanged": 1,
"attributesChanged": 1
},
"links": {
"changes": "https://integration.example.test/jsonapi/v1/catalog/changes?since=420",
"products": "https://integration.example.test/jsonapi/v1/catalog/products",
"categories": "https://integration.example.test/jsonapi/v1/catalog/categories",
"attributes": "https://integration.example.test/jsonapi/v1/catalog/attributes"
}
}
A batch of catalog changes
catalog.changes.batch
The catalog changes themselves, split into numbered parts. Parts are sent strictly in order, and a part is not sent until the previous one was delivered. Each part is sent byte-for-byte the same on every retry; check it against X-Syncor-Payload-SHA256.
Catalog batches always use this standard JSON body. A canonical mapping profile may only wrap it in a property.
Extra headers
| Header | Meaning |
|---|---|
X-Syncor-Batch-Id |
The batch this part belongs to. |
X-Syncor-Batch-Part |
This part's number, starting at 1. |
X-Syncor-Batch-Parts |
How many parts the batch has. |
X-Syncor-Cursor-From |
The catalog change cursor this part starts after. |
X-Syncor-Cursor-To |
The last catalog change cursor in this part. |
X-Syncor-Payload-SHA256 |
The hex SHA-256 of the body, the same on every retry. |
Fields
| Field | Type | Always present | Description |
|---|---|---|---|
event.id |
string | Yes | Event ID |
event.name |
string | Yes | Event Name |
event.version |
string | Yes | Event Version |
event.occurredAt |
string | Yes | Occurred At |
customer.accountNumber |
string | Yes | Customer Account Number |
batch.id |
string | Yes | Batch ID |
batch.partNumber |
integer | Yes | Part Number |
batch.partCount |
integer | Yes | Part Count |
batch.isFirstPart |
boolean | Yes | First Part |
batch.isFinalPart |
boolean | Yes | Final Part |
batch.fromCursor |
integer | Yes | Batch From Cursor |
batch.toCursor |
integer | Yes | Batch To Cursor |
batch.partFromCursor |
integer | Yes | Part From Cursor |
batch.partToCursor |
integer | Yes | Part To Cursor |
batch.totalChangeCount |
integer | Yes | Total Change Count |
batch.partChangeCount |
integer | Yes | Part Change Count |
changes[].cursor |
integer | Yes | Change Cursor |
changes[].entityType |
string | Yes | Entity Type |
changes[].operation |
string | Yes | Operation |
changes[].changeType |
string | Yes | Change Type |
changes[].entityId |
string | Yes | Entity ID |
changes[].data |
object | Yes | Customer Catalog Entity |
links.changes |
string | Yes | Catalog Changes Feed |
Example
{
"event": {
"id": "evt_01JCATALOGDELTA",
"name": "catalog.changes.batch",
"version": "1.0",
"occurredAt": "2026-08-19T12:00:00Z"
},
"customer": {
"accountNumber": "CUST-001"
},
"batch": {
"id": "01JCATALOGBATCH0000000001",
"partNumber": 1,
"partCount": 2,
"isFirstPart": true,
"isFinalPart": false,
"fromCursor": 420,
"toCursor": 423,
"partFromCursor": 421,
"partToCursor": 422,
"totalChangeCount": 3,
"partChangeCount": 2
},
"changes": [
{
"cursor": 421,
"entityType": "product",
"operation": "upserted",
"changeType": "added",
"entityId": "SKU-001",
"data": {
"type": "catalog-products",
"id": "SKU-001",
"attributes": {
"sku": "SKU-001",
"name": "Safety Helmet",
"isActive": true
}
}
},
{
"cursor": 422,
"entityType": "product",
"operation": "upserted",
"changeType": "updated",
"entityId": "SKU-002",
"data": {
"type": "catalog-products",
"id": "SKU-002",
"attributes": {
"sku": "SKU-002",
"name": "Protective Gloves",
"isActive": true
}
}
}
],
"links": {
"changes": "/jsonapi/v1/catalog/changes"
}
}