Skip to content
Distributor onboarding

ERP to Salesforce

ERP → Salesforce

These are the calls ERP sends. Salesforce is the server. They run inside POST /webhook/onboarding, before ERP returns the codes to Salesforce. The sample company is Sharma Distributors. The body Salesforce sent in is on Salesforce → ERP.

Auth is OAuth client credentials. Every URL below is on the Salesforce instance:

https://{instance}/services/data/v67.0

Order Method and path What it is
1 PATCH /sobjects/Account/001000000000001AAA Turn the Temp Account into a Sold To and write DIST001
2 POST /sobjects/Account Create the Bill To BILL001 under that Sold To

If the server has no Salesforce URL and no token, these calls are not made. The local Sold To and Bill To still exist, and the onboarding response still returns the codes.

These fields are not driven by ERP_Payload_Mapping. They are the body of WriteBackOnboarding.


PATCH /sobjects/Account/{id}

Upgrade the Temp Account to a Sold To. Salesforce is still waiting on POST /webhook/onboarding while this PATCH runs.

PATCH /services/data/v67.0/sobjects/Account/001000000000001AAA
Authorization: Bearer {access token}
Content-Type: application/json

Caller: httpSalesforceClient.WriteBackOnboarding. 001000000000001AAA is onboarding_requests.salesforce_record_id. If that id is blank, the PATCH is skipped and the onboarding HTTP response is still 200.

Pre-check

Before the PATCH, ERP runs:

GET /services/data/v67.0/query?q=SELECT Id FROM Account WHERE Sold_To_Code__c = 'DIST001' LIMIT 1

If that query returns an Id whose first 15 characters differ from 001000000000001AAA, the upgrade stops. The error stored on the onboarding row is:

sold to code DIST001 already exists on Salesforce Account {otherId} (cannot upgrade Temp 001000000000001AAA); assign a free code or clear the conflicting Account

An empty query result, or a hit on the Temp Account itself, is allowed. The same 15-character comparison treats a 15-character Id and an 18-character Id as the same record.

What ERP sends

RecordTypeId is the org's Sold To record type, looked up at runtime by developer name Sold_To. The value below is a placeholder. Bank fields are omitted when empty, which is the live case, because bank details never arrived on the push. ERP_Record_ID__c on this call is ERP's own row id. Salesforce replaces it with DIST001 when it reads the onboarding response. BillingCountry does not come from the onboarding JSON. The PATCH always sends India.

{
  "RecordTypeId": "012000000000001AAA",
  "Sold_To_Code__c": "DIST001",
  "Name": "Sharma Distributors",
  "GST_Registration_Number__c": "29ABCDE1234F1Z5",
  "PAN_Number__c": "ABCDE1234F",
  "BillingStreet": "21 Residency Road",
  "BillingCity": "Bengaluru",
  "BillingState": "Karnataka",
  "BillingPostalCode": "560025",
  "BillingCountry": "India",
  "Phone": "9900112233",
  "Contact_Name__c": "Meena Rao",
  "Contact_Phone__c": "9900112233",
  "Email__c": "meena.sharma@example.com",
  "Payment_Terms__c": "Net 30",
  "Territory__r": {
    "Territory_Code__c": "TER-KA-BLR-01"
  },
  "Account_Status__c": "Active",
  "ERP_Record_ID__c": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
JSON field Type Required Source in ERP Salesforce field
RecordTypeId id yes record type cache, developer name Sold_To RecordTypeId
Sold_To_Code__c string yes DIST001 Sold_To_Code__c
Name string no prospectName Name
GST_Registration_Number__c string no gstRegistrationNumber GST_Registration_Number__c
PAN_Number__c string no panNumber PAN_Number__c
Bank_Account_Name__c string no bank name, omitted when blank Bank_Account_Name__c
Bank_Account_Number__c string no bank number, omitted when blank Bank_Account_Number__c
Bank_IFSC_Code__c string no IFSC, omitted when blank Bank_IFSC_Code__c
BillingStreet string no billingAddress.street BillingStreet
BillingCity string no billingAddress.city BillingCity
BillingState string no normalized state name BillingState
BillingPostalCode string no billingAddress.pincode BillingPostalCode
BillingCountry string yes on this call always the literal India BillingCountry
Phone string no contactPhone Phone
Contact_Phone__c string no same phone as Phone Contact_Phone__c
Contact_Name__c string no contactName Contact_Name__c
Email__c string no contactEmail Email__c
Payment_Terms__c string no Net 30 when the Sold To value is blank. It was not in the Salesforce request Payment_Terms__c
Territory__r.Territory_Code__c string no territoryCode. The whole Territory__r object is omitted when the code is blank Territory__c, resolved by external id
Account_Status__c string yes Active Account_Status__c
ERP_Record_ID__c string no Sold To primary key. Falls back to DIST001 only when that id is blank ERP_Record_ID__c

What Salesforce sends back

HTTP 204. The body is empty.

That save changes the record type from Temp to Sold To on an existing Account. SoldTo_AfterSave calls Account_AfterSave_Shared. The subflow sees prior record type Temp and current record type Sold To, and queues the record-id callback with accountType = SoldTo and erpCode = DIST001. The callback is skipped when Bypass_Automation__c.Is_Bypassed__c is true.

A 204 clears errorMessage on onboarding row ONB-2026-0001. The Sold To row in ERP still has an empty salesforceRecordId. The callback fills it. The outbound integration log reference is flowB writeback sfId=001000000000001AAA soldTo=DIST001.

System Record State
Salesforce 001000000000001AAA Record type Sold To. Sold_To_Code__c = DIST001. ERP_Record_ID__c is still the UUID until the onboarding HTTP response is applied
ERP Sold To DIST001 Unchanged by this PATCH. salesforceRecordId empty
ERP Onboarding ONB-2026-0001 errorMessage empty

Errors

A non-success from the query or the PATCH is returned to writeBackOnboarding, stored in onboarding_requests.errorMessage, and logged. processOnboardingReceive ignores that error. Salesforce still receives HTTP 200 and the codes. The Account can still be record type Temp. Sold_To_Code__c stays blank, because only this PATCH writes it.

Salesforce REST errors use the platform body, for example a duplicate Sold_To_Code__c or a validation rule on GST, PAN, or billing address. ERP does not rewrite that body. It wraps the message:

upgrade temp account to sold to: {Salesforce error}

If the Bill To step has not run yet, a PATCH failure means no Bill To Account is created either. WriteBackOnboarding creates Bill Tos only after the PATCH returns. See Failures and retry.


POST /sobjects/Account

Create the Bill To. This runs only after the Sold To PATCH succeeded, and still before the 200 response. Caller: upsertBillToDuringOnboarding.

POST /services/data/v67.0/sobjects/Account
Authorization: Bearer {access token}
Content-Type: application/json
Sforce-Duplicate-Rule-Header: allowSave=true

The duplicate-rule header lets the Bill To save when it shares the Sold To GSTIN and address and the org's duplicate rule is set to allow the save.

One Bill To is created on the automatic path. Its name is {prospect name} - Billing. A manual respond that passes several codes names the later ones {prospect name} - Billing 2, and so on.

Pre-check

GET /services/data/v67.0/query?q=SELECT Id FROM Account WHERE Bill_To_Code__c = 'BILL001' LIMIT 1

An empty result means create. A hit means update that Account instead, with the body in Update, if BILL001 already exists.

What ERP sends

The parent is the Sold To code, not a Salesforce Id. ERP does not have the Sold To Id yet. The callback has not run. GST is copied from the Sold To, not from a separate field on the original push. RecordTypeId is the org's Bill_To record type. ERP_Record_ID__c is the ERP Bill To primary key.

{
  "RecordTypeId": "012000000000002AAA",
  "Name": "Sharma Distributors - Billing",
  "Bill_To_Code__c": "BILL001",
  "Parent": {
    "Sold_To_Code__c": "DIST001"
  },
  "GST_Registration_Number__c": "29ABCDE1234F1Z5",
  "BillingStreet": "21 Residency Road",
  "BillingCity": "Bengaluru",
  "BillingState": "Karnataka",
  "BillingPostalCode": "560025",
  "BillingCountry": "India",
  "Account_Status__c": "Active",
  "ERP_Sync_Status__c": "Synced",
  "ERP_Last_Sync_Timestamp__c": "2026-11-05T12:00:00Z",
  "ERP_Record_ID__c": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
JSON field Type Required Source in ERP Salesforce field
RecordTypeId id yes record type cache, developer name Bill_To RecordTypeId
Name string yes Sharma Distributors - Billing Name
Bill_To_Code__c string yes BILL001, allocated from that Bill To name Bill_To_Code__c
Parent.Sold_To_Code__c string yes, when ParentId is unknown parent Sold To code ParentId, resolved by Sold_To_Code__c
ParentId id used instead of Parent once the parent Salesforce Id is known not set on this first create ParentId
GST_Registration_Number__c string yes Sold To GSTIN GST_Registration_Number__c
BillingStreet string yes same address as the Sold To BillingStreet
BillingCity string yes same BillingCity
BillingState string yes normalized state BillingState
BillingPostalCode string yes pincode BillingPostalCode
BillingCountry string yes literal India BillingCountry
Account_Status__c string yes Active Account_Status__c
ERP_Sync_Status__c string yes literal Synced ERP_Sync_Status__c
ERP_Last_Sync_Timestamp__c string no UTC time of the call, RFC3339 ERP_Last_Sync_Timestamp__c
ERP_Record_ID__c string no Bill To primary key. Falls back to BILL001 when that id is blank ERP_Record_ID__c

What Salesforce sends back

HTTP 201 Created

{
  "id": "001000000000002AAA",
  "success": true,
  "errors": []
}

HTTP 200 with the same shape is also accepted. An empty id is a failure.

The insert is a new Bill To. BillTo_AfterSave calls Account_AfterSave_Shared, which queues a record-id callback with accountType = BillTo and erpCode = BILL001, unless automation bypass is on.

ERP does not write 001000000000002AAA onto the local Bill To from this response. The callback does that. The id is checked only to confirm the create returned one. The Salesforce caller of /webhook/onboarding is still waiting, and receives 200 only after this create returns.

System Record State
Salesforce 001000000000002AAA Record type Bill To, parent is the Account whose Sold_To_Code__c is DIST001, GSTIN copied from the Sold To, ERP_Sync_Status__c = Synced
ERP Bill To BILL001 Present. salesforceRecordId still empty

Update, if BILL001 already exists

A retry sends a PATCH instead of a second create.

PATCH /services/data/v67.0/sobjects/Account/001000000000002AAA
{
  "Name": "Sharma Distributors - Billing",
  "GST_Registration_Number__c": "29ABCDE1234F1Z5",
  "BillingStreet": "21 Residency Road",
  "BillingCity": "Bengaluru",
  "BillingState": "Karnataka",
  "BillingPostalCode": "560025",
  "BillingCountry": "India",
  "ERP_Sync_Status__c": "Synced",
  "ERP_Last_Sync_Timestamp__c": "2026-11-05T12:00:00Z"
}

Salesforce answers HTTP 204 with an empty body. Bill_To_Code__c and Parent are not sent again. This branch is the retry path, after a first attempt created the Bill To and a later write-back is repeated.

Errors

A query, create, or update failure is wrapped as:

create bill to BILL001 during onboarding write-back: {Salesforce error}

or update bill to BILL001 during onboarding write-back: ..., or pre-check bill to code BILL001 in Salesforce: ....

On the automatic receive, that message is stored on the onboarding row and the original Salesforce caller still gets HTTP 200 with soldToCode and billToCodes. The Sold To upgrade has already committed. A Bill To failure does not roll it back. The local Bill To row BILL001 exists either way. It was inserted before write-back started.