Distributor onboarding¶
Distributor onboarding is the path that turns an approved prospect into a Sold To and a Bill To. Salesforce collects the prospect and runs the approval. ERP assigns the codes and is the system of record for the master afterwards.
Four records are involved.
| Record | Where | Role in this flow |
|---|---|---|
Distributor_Onboarding__c |
Salesforce | The prospect, the approval, and the link to the Temp Account |
| Account, record type Temp | Salesforce | The row Salesforce sends to ERP. It has no Sold To code yet |
| Sold To | ERP, then Salesforce | The distributor. ERP assigns Sold_To_Code__c and upgrades the Temp Account into this record type |
| Bill To | ERP, then Salesforce | The billing account under that Sold To. ERP creates it |
ERP owns the codes. Salesforce does not invent DIST001 or BILL001. It sends the prospect, waits for the codes, and stores the Sold To code that comes back.
The JSON is on two pages: Salesforce → ERP and ERP → Salesforce. What stays saved when a later call fails is on Failures and retry.
Sample distributor¶
Every example uses this prospect. Codes assume no earlier DIST or BILL sequence exists in ERP or in Salesforce.
| Item | Value |
|---|---|
| Prospect name | Sharma Distributors |
| Contact | Meena Rao |
| Phone | 9900112233 |
| meena.sharma@example.com | |
| GSTIN | 29ABCDE1234F1Z5 |
| PAN | ABCDE1234F |
| Street | 21 Residency Road |
| City | Bengaluru |
| State | Karnataka |
| Pincode | 560025 |
| Territory code | TER-KA-BLR-01 |
| Onboarding record Id | a0X000000000001AAA |
| Temp Account Id, later the Sold To Id | 001000000000001AAA |
| Bill To Account Id | 001000000000002AAA |
| Sold To code | DIST001 |
| Bill To code | BILL001 |
| Sold To row id | 7c9e6679-7425-40de-944b-e07fc1f90ae7 |
| Bill To row id | 3fa85f64-5717-4562-b3fc-2c963f66afa6 |
| Onboarding reference | ONB-2026-0001 |
DIST is the first four letters of the last significant word in "Sharma Distributors". BILL comes from the Bill To name "Sharma Distributors - Billing". The number is a three-digit sequence for that prefix. The onboarding reference is ONB-{year}-{count}, zero-padded to four digits. Each code is checked in Salesforce and skipped if that code is already on an Account. Up to 50 candidates are tried.
How the systems connect¶
Salesforce calls ERP with the Named Credential ERP_Named_Credential. ERP calls Salesforce with OAuth client credentials against REST API v67.0.
Salesforce calls ERP¶
ERP_Integration_Setting.Distributor_Sync is the onboarding push. ErpCallbackService is the record-id callback.
| Setting | Onboarding push | Record ID callback |
|---|---|---|
| Call | POST callout:ERP_Named_Credential/webhook/onboarding |
POST callout:ERP_Named_Credential/api/accounts/salesforce-callback |
| Header | Content-Type: application/json |
Content-Type: application/json |
| Timeout | 15000 ms | 15000 ms |
| Retries | Max_Retries__c = 0. One attempt |
3 retries after the first attempt (retry count 0 through 3) |
| Object | Account | Account |
| Sync status field | ERP_Sync_Status__c |
ERP_Sync_Status__c |
| Sync timestamp field | ERP_Last_Sync_Timestamp__c |
stamped on a successful callback |
| Log operation | Update | Create, or Retry when retryCount > 0 |
The Named Credential supplies the ERP base URL and the credential Salesforce attaches. The path above is appended to that base. A local ERP listens on http://localhost:8080, so a developer org typically points the Named Credential there.
ERP also accepts the same handlers on a second path:
| Live path Salesforce uses | Alias | Handler |
|---|---|---|
POST /webhook/onboarding |
POST /api/distributors/onboarding |
ReceiveOnboarding |
POST /api/accounts/salesforce-callback |
POST /webhook/salesforce-callback |
SalesforceCallback |
The alias under /webhook/salesforce-callback is the older path. New callback traffic uses /api/accounts/salesforce-callback.
ERP calls Salesforce¶
When an instance URL and a token provider (OAuth client id and secret, or a static access token) are set, ERP uses the HTTP client:
POST {instance}/services/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id={id}&client_secret={secret}
Account calls then go to /services/data/v67.0/sobjects/Account and /services/data/v67.0/sobjects/Account/{id}. Record type ids are resolved by developer name (Sold_To, Bill_To) and cached. They are not hardcoded.
If neither an instance URL nor a token provider is configured, NewSalesforceClient returns MockSalesforceClient. Local Sold To and Bill To rows are still created, and POST /webhook/onboarding still returns the codes. Nothing is written to a Salesforce org. The bodies on ERP → Salesforce are the HTTP client, which is the path a connected org runs.
Log types¶
The same hop is outbound for the caller and inbound for the receiver.
| Hop | Salesforce log type | ERP log type |
|---|---|---|
| Temp Account push | Outbound (Distributor_Sync) |
Inbound |
| Sold To upgrade and Bill To create | Inbound REST | Outbound |
| Record ID callback | Outbound (ErpCallbackService) |
Callback |
Sequence¶
Finance approval of Distributor_Onboarding__c is the start. The onboarding record must move to status Approved while Temp_Account__c is still blank. Distributor_Onboarding_After_Save_Flow then creates one Temp Account and stores its Id on the onboarding record. If that insert fails, the flow rolls the approval back and the onboarding record stays at KYC In Progress.
The Temp Account is filled from the onboarding record:
| Temp Account field | Copied from |
|---|---|
Name |
Prospect_Name__c |
GST_Registration_Number__c |
GST_Registration_Number__c |
PAN_Number__c |
PAN_Number__c |
BillingStreet, BillingCity, BillingPostalCode |
Address__Street__s, Address__City__s, Address__PostalCode__s |
BillingStateCode, BillingCountryCode |
Address__StateCode__s, Address__CountryCode__s |
Contact_Name__c |
Contact_Name__c |
Contact_Phone__c |
Mobile__c |
Email__c |
Email__c |
Territory__c |
Proposed_Territory__c |
Distributor_Onboarding__c |
the onboarding record Id |
Account_Status__c |
Active |
ERP_Sync_Status__c |
Pending |
| Record type | Temp |
Bank fields are not copied onto the Temp Account. The three bank payload mappings are also inactive, so bank details are not on the wire. The mapping rows are on Salesforce → ERP.
sequenceDiagram
participant SF as Salesforce
participant ERP
SF->>SF: Finance approval creates Temp Account
SF->>ERP: POST /webhook/onboarding
ERP->>ERP: Create Sold To and Bill To
ERP->>SF: PATCH Temp Account to Sold To
ERP->>SF: POST Bill To Account
ERP-->>SF: 200 with soldToCode and billToCodes
SF->>ERP: POST /api/accounts/salesforce-callback
- Temp Account insert. Id
001000000000001AAAnow exists. Record type is Temp. Sold To code is blank.ERP_Sync_Status__cis Pending. Temp_AfterSavesees a new Temp Account. The flow starts only for record type Temp. On insert (ISNEW()) it callsErpOutboundSyncInvocablewith integration nameDistributor_Syncand that Account Id. This is the onboarding POST. The olderAccount_AfterSaveflow is obsolete and does not run. Temp accounts do not enterAccount_AfterSave_Shared, so this insert does not send a record-id callback.- ERP validates and saves. It creates Sold To
DIST001and Bill ToBILL001in its own database, with onboarding referenceONB-2026-0001. The Salesforce Id is kept on the onboarding row. It is not yet stored on the Sold To. That happens on the callback. - ERP upgrades the Temp Account.
PATCH /services/data/v67.0/sobjects/Account/001000000000001AAAchanges the record type to Sold To and writesSold_To_Code__c = DIST001. See Sold To upgrade.SoldTo_AfterSavethen callsAccount_AfterSave_Shared, which queues the Sold To callback because the prior record type was Temp. - ERP creates the Bill To.
POST /services/data/v67.0/sobjects/Accountinserts record type Bill To, parented by Sold To codeDIST001. Salesforce returns001000000000002AAA. See Bill To create. That insert queues the Bill To callback. - ERP answers the original push. HTTP 200 with
soldToCode,billToCodes,recordTypeUpgrade,erpReference, andstatus. Salesforce writessoldToCodetoAccount.ERP_Record_ID__cand resolvesrecordTypeUpgrade(Sold To) to the Sold ToRecordTypeId. It setsERP_Sync_Status__cto Synced. The code write replaces the internal id that the PATCH had put inERP_Record_ID__c. The record type write is the same Sold To type the PATCH already set, when the PATCH succeeded. - Callbacks run. Each queued job posts the Account Id to
POST /api/accounts/salesforce-callback. ERP stores001000000000001AAAon Sold ToDIST001and copies it onto the Bill To as the parent Salesforce Id. It stores001000000000002AAAon Bill ToBILL001. See Record ID callback.
The PATCH and the Bill To insert happen inside the onboarding request, before step 6 returns. The callbacks are queued by those Salesforce saves and run after the request, as separate queueable jobs.
| After step | Known in Salesforce | Known in ERP |
|---|---|---|
| 1 | Temp Account Id, onboarding Id | nothing yet |
| 3 | unchanged | Sold To code, Bill To code, onboarding reference, Temp Account Id on the onboarding row |
| 4 | Temp Account is now Sold To, Sold_To_Code__c is set |
same, plus the PATCH succeeded |
| 5 | Bill To Id exists | Bill To still has no Salesforce Id until step 7 |
| 6 | ERP_Record_ID__c on the Sold To Account is DIST001, sync status Synced |
unchanged |
| 7 | callback sync status Synced | salesforceRecordId on Sold To and Bill To, parent Salesforce Id on the Bill To |
Once DIST001 and BILL001 exist, the next chapter is Account lifecycle: edits, another Bill To, and freeze, unfreeze, deactivate, and activate.