Failures and retry¶
Two failure points behave differently. A rejection of the Temp Account push leaves Salesforce unsynced and leaves ERP without masters. A failure of the later Salesforce write-back still returns HTTP 200, and the masters are already saved.
Push rejected before masters exist¶
Salesforce posts the body on Salesforce → ERP. ERP returns 400 or 409, or 500 if code allocation cannot reach Salesforce. Nothing is inserted. The full message list is on that page.
Salesforce sets Account.ERP_Sync_Status__c to Failed. Max_Retries__c is 0, so the queueable does not send the push again. soldToCode is not written, because the response mapping runs only after a 2xx.
The Account is still record type Temp. Sold_To_Code__c is blank. A new attempt means sending Distributor_Sync again for that Account, for example from a manual retry once the payload or the ERP data has been corrected.
Example. The same prospect is sent with "billingAddress": { "state": "KA" }. ERP responds:
{
"error": "validation_error",
"message": "billing state must be a valid Indian state/UT name (e.g. Maharashtra), not an abbreviation like MH"
}
HTTP 400. No DIST001, no BILL001, no ONB-2026-0001.
A second post of a GSTIN that is already on a Sold To returns:
HTTP 409. The existing distributor is left as it is.
Write-back fails after masters exist¶
For Sharma Distributors the local rows are committed first: Sold To DIST001, Bill To BILL001, onboarding ONB-2026-0001 with status responded. writeBackOnboarding then PATCHes Salesforce. processOnboardingReceive discards the error from that call.
So if the PATCH throws upgrade temp account to sold to: ..., all of the following are true at once:
- Salesforce, the caller of
POST /webhook/onboarding, receives HTTP 200 and the normal body withsoldToCode = DIST001andrecordTypeUpgrade = Sold To. - Salesforce sets
ERP_Sync_Status__cto Synced, writesERP_Record_ID__c = DIST001, and setsRecordTypeIdto the Sold To record type fromrecordTypeUpgrade. Sold_To_Code__cstays blank, because only the PATCH writes that field.SoldTo_AfterSavecan then see a Temp-to-Sold-To change caused by this response write. The callback invocable does not enqueue whenSold_To_Code__cis blank, so no record-id callback goes out.- ERP onboarding
errorMessageholds the PATCH error. - The inbound log for the receive is still a success line (
flowB receive erpRef=ONB-2026-0001 ...). - The outbound log for the write-back is a failure, reference
flowB writeback sfId=001000000000001AAA soldTo=DIST001.
A Bill To failure after a successful PATCH is the same HTTP 200, with the Sold To already converted and the Bill To Account missing. The local Bill To row exists. The Sold To callback may already be queued. The Bill To callback is not, until a later create succeeds.
Bank account numbers are not written on these log lines. The failure path that masks a Sold To uses bank=***.
Retry the write-back¶
The {id} segment accepts the onboarding primary key or the ERP reference ONB-2026-0001.
When the row is responded and errorMessage is non-blank, the handler ignores the body and calls writeBackOnboarding again with the codes already stored. A success response is HTTP 200:
{
"soldToCode": "DIST001",
"billToCodes": ["BILL001"],
"recordTypeUpgrade": "Sold To",
"erpReference": "ONB-2026-0001",
"status": "responded"
}
errorMessage is cleared. The Sold To PATCH and the Bill To create run again. The Bill To pre-check updates BILL001 if that Account was created on the earlier attempt.
If the write-back fails again, this endpoint does not hide it. The response is HTTP 500:
{
"error": "internal_error",
"message": "internal error: salesforce onboarding write-back failed: upgrade temp account to sold to: ..."
}
errorMessage on the onboarding row is updated to the new Salesforce error.
This retry does not tell the original Salesforce queueable anything. That queueable already marked the Temp Account Synced when it saw the first 200. The retry's effect on Salesforce is the PATCH and the Bill To POST themselves.
Manual respond, when the row is still pending¶
The automatic receive path sets status to responded immediately. The pending branch remains for a row that was stored without codes. POST /api/accounts/onboarding-requests/{id}/respond then requires:
| HTTP | error |
message |
|---|---|---|
| 400 | invalid_json |
invalid request body |
| 400 | validation_error |
onboarding id is required |
| 400 | validation_error |
soldToCode is required |
| 400 | validation_error |
at least one billToCode is required |
| 404 | not_found |
onboarding request not found |
| 409 | conflict |
onboarding request has already been responded to |
| 409 | conflict |
sold to code already exists |
| 409 | conflict |
bill to code already exists: BILL001 |
| 409 | conflict |
sold to code DIST001 already exists in Salesforce on Account {id} |
| 409 | conflict |
bill to code BILL001 already exists in Salesforce on Account {id} |
| 409 | conflict |
a distributor with this GST number already exists |
The "already been responded to" conflict is for a responded row whose errorMessage is empty. A responded row that still has an error message takes the retry branch instead.
Callback failures¶
Callback errors are independent of the push. The request, the error list, and the retry schedule are on Salesforce → ERP. A 404 sold to account not found means erpCode is not in this ERP database. A 409 means the row is already linked to a different Account Id, and a retry of the same body will keep failing until that link is cleared.
While retries remain, ERP_Sync_Status__c stays Pending. After the last retry it becomes Failed, and a dead-letter Integration_Log__c is inserted with Alert_Type__c = Callback Dead Letter.