Skip to content
Distributor onboarding

Failures and retry

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:

{
  "error": "conflict",
  "message": "a distributor with this GST number already exists"
}

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 with soldToCode = DIST001 and recordTypeUpgrade = Sold To.
  • Salesforce sets ERP_Sync_Status__c to Synced, writes ERP_Record_ID__c = DIST001, and sets RecordTypeId to the Sold To record type from recordTypeUpgrade.
  • Sold_To_Code__c stays blank, because only the PATCH writes that field.
  • SoldTo_AfterSave can then see a Temp-to-Sold-To change caused by this response write. The callback invocable does not enqueue when Sold_To_Code__c is blank, so no record-id callback goes out.
  • ERP onboarding errorMessage holds 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

POST /api/accounts/onboarding-requests/ONB-2026-0001/respond

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:

{
  "soldToCode": "DIST001",
  "billToCodes": ["BILL001"]
}
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.