Skip to content
Price

ERP to Salesforce

ERP → Salesforce

ERP is the client. Salesforce is the server. Auth is the same OAuth client credentials as onboarding, against /services/data/v67.0. If ERP has no instance URL and no token, the local row is still saved and these calls are not made.

POST /api/prices

TEA-500 must already exist. This is the first price for that product in INR, so nothing is closed.

{
  "price": {
    "productCode": "TEA-500",
    "batch": "B2026-11",
    "currency": "INR",
    "price": 240,
    "effectiveFrom": "2026-11-01T00:00:00Z"
  }
}

ERP builds skuBatchKey as TEA-500_B2026-11, saves the row with no end date, upserts Salesforce, then answers HTTP 201. On a successful push the body includes "skuBatchKey": "TEA-500_B2026-11", "syncStatus": "Synced", and "active": true. A failed push still returns 201, with "syncStatus": "Failed".

HTTP error message
400 invalid_json invalid request body
400 validation_error effective to must be blank when creating a price
400 validation_error sku batch key is required
400 validation_error product code is required
400 validation_error batch is required
400 validation_error price must be greater than 0
400 validation_error effective from is required
400 validation_error product not found
400 validation_error a price already exists for batch B2026-11 of this product; use a new batch number
400 validation_error effective from must be after the current active price effective from

PATCH Product_Price__c by key

The key is in the URL, not the body. Effective_To__c is omitted while the price is open.

PATCH /services/data/v67.0/sobjects/Product_Price__c/SKU_Batch_Key__c/TEA-500_B2026-11
Authorization: Bearer {access token}
Content-Type: application/json
{
  "Product_Master__r": { "Product_Code__c": "TEA-500" },
  "Batch_Number__c": "B2026-11",
  "Unit_Price__c": 240,
  "CurrencyIsoCode": "INR",
  "Effective_From__c": "2026-11-01"
}
Salesforce field Example Where ERP got it
SKU_Batch_Key__c TEA-500_B2026-11 the URL. External id. {productCode}_{batch}, upper case
Product_Master__r.Product_Code__c TEA-500 productCode
Batch_Number__c B2026-11 batch
Unit_Price__c 240 price
CurrencyIsoCode INR currency. A blank currency is sent as INR
Effective_From__c 2026-11-01 effectiveFrom, as a UTC date
Effective_To__c omitted effectiveTo. Omitted when the price is open

A first create is HTTP 201 with an id. ERP checks that the id is present and does not store it. An update of a key Salesforce already has is HTTP 204 with an empty body. ERP treats the empty body as a failure: price upsert: empty id (status 204). The local row becomes Failed. See Salesforce → ERP.

The log reference is skuBatchKey=TEA-500_B2026-11 productCode=TEA-500 batch=B2026-11 price=240.00 INR currentActive=true.

POST /api/prices/TEA-500_B2026-11/sync-to-salesforce sends the same PATCH for the open INR row. Add ?currency=INR to name the currency. Add ?id= to name one stored row. Success is HTTP 200:

{
  "erpCode": "TEA-500_B2026-11",
  "accountType": "Price",
  "status": "Synced"
}

A Salesforce error on this retry is HTTP 500 internal_error with message internal server error. A missing row is HTTP 404 not_found with message product price not found.

POST /api/prices/sync-unsynced runs the single PATCH for every price, ordered by key, including rows already marked Synced and rows that were closed. It does not send the Composite transition. A second pass hits the empty 204 and counts those rows as failed. The answer is HTTP 200, for example { "synced": 1, "failed": 0 }.

A new batch

B2026-12 starts on 1 December. The November price is still open, so ERP closes it and sends both rows in one Composite call.

{
  "price": {
    "productCode": "TEA-500",
    "batch": "B2026-12",
    "currency": "INR",
    "price": 255,
    "effectiveFrom": "2026-12-01T00:00:00Z"
  }
}

ERP answers HTTP 201 with the December price. skuBatchKey is TEA-500_B2026-12. The November row stays in ERP with effectiveTo 2026-11-30. Both rows share the sync result: Synced when the Composite succeeds, Failed when it does not.

The date written locally and the date sent to Salesforce are different. Locally the November price ends on 2026-11-30, the day before the new price starts, so only one price is active on a calendar day. The Composite body end-dates it on 2026-12-01, the new effective from.

POST /services/data/v67.0/composite
{
  "allOrNone": true,
  "compositeRequest": [
    {
      "method": "PATCH",
      "url": "/services/data/v67.0/sobjects/Product_Price__c/SKU_Batch_Key__c/TEA-500_B2026-11",
      "referenceId": "endDateOldBatch0",
      "body": { "Effective_To__c": "2026-12-01" }
    },
    {
      "method": "PATCH",
      "url": "/services/data/v67.0/sobjects/Product_Price__c/SKU_Batch_Key__c/TEA-500_B2026-12",
      "referenceId": "newBatch",
      "body": {
        "Product_Master__r": { "Product_Code__c": "TEA-500" },
        "Batch_Number__c": "B2026-12",
        "Unit_Price__c": 255,
        "CurrencyIsoCode": "INR",
        "Effective_From__c": "2026-12-01"
      }
    }
  ]
}

Every open price for TEA-500 in INR is an endDateOldBatch step, numbered from 0. The new price is always newBatch. allOrNone is true, so a failed step rolls the Composite back. A later single sync of the November row sends Effective_To__c 2026-11-30, the date stored locally.

PATCH /api/prices/TEA-500_B2026-12

The URL is the key. The body has no price wrapper. Product code, batch, and currency must stay the same. This updates the open row for that key and currency. ?id= selects one stored row instead.

{
  "productCode": "TEA-500",
  "batch": "B2026-12",
  "currency": "INR",
  "price": 260,
  "effectiveFrom": "2026-12-01T00:00:00Z"
}

ERP answers HTTP 200 and sends the single PATCH, with "Unit_Price__c": 260. Because Salesforce already has TEA-500_B2026-12, that PATCH is HTTP 204 with an empty body, and the local row becomes Failed.

HTTP error message
400 validation_error product code and batch cannot be changed; create a new price instead
400 validation_error currency cannot be changed; create a new price instead
400 validation_error effective to must be on or after effective from
404 not_found product price not found

Failed Integration Log

A failed price upsert or a failed Composite also posts Integration_Log__c. Log_Type__c is Inbound. Operation__c is Create on the first push and on a new batch, and Update on a PATCH. ERP_Code__c is the sku batch key of the new price.

POST /services/data/v67.0/sobjects/Integration_Log__c
{
  "Log_Type__c": "Inbound",
  "Operation__c": "Update",
  "Status__c": "Failed",
  "ERP_Code__c": "TEA-500_B2026-12",
  "Error_Message__c": "price upsert: empty id (status 204)",
  "Timestamp__c": "2026-11-06T05:30:00.000Z",
  "Payload_Reference__c": "skuBatchKey=TEA-500_B2026-12 productCode=TEA-500 batch=B2026-12 price=260.00 INR currentActive=false"
}