Skip to main content

Customer Sync

Use this API to convert retailer rows and shop rows from Excel into Mulltiply customer records. A retailer comes from the Customer Sheet, and each matching shop row becomes a shop under that retailer.

At A Glance

POST/v2/retailers/sync-data
AreaDetail
PurposeSync retailer/customer data and shop/branch data from Excel.
Content typeapplication/json
Authenticationx-api-key request header
Request bodyArray of retailer objects
Primary keyRetailer syncId, mapped from Sheet 1 No
Shop linkSheet 2 Customer_No must match Sheet 1 No
info

The Word source contains a concrete API key example. The public documentation uses YOUR_API_KEY instead so an implementation team knows where the key goes without exposing a real credential.

Source Workbook

The integration reads two sheets from the Excel file.

SheetPurposeKey columns
Sheet 1Retailer / Customer dataNo, Contact, Name, Mobile_Phone_No, Phone_No, Search_Name, address fields, GST fields
Sheet 2Shop / Branch dataCustomer_No, Code, Name, address fields

The script converts rows from these sheets into the API payload before sending them to Mulltiply.

Authentication And Headers

Send the request as JSON and include the issued API key.

Content-Type: application/json
x-api-key: YOUR_API_KEY

Request Body

The endpoint expects an array. Each item in the array is one retailer.

Request body
[
{
"name": "Demo Retailer",
"firstName": "Demo",
"lastName": "Retailer",
"email": "",
"phoneCountryCode": "+91",
"phone": "9999901001",
"syncId": "RET-1001",
"note": "",
"tags": ["DEMO_TAG"],
"taxExempt": false,
"createdAt": "2026-03-09T07:46:56Z",
"updatedAt": "2026-03-09T11:41:27Z",
"outStandingAmount": "1500",
"shops": [],
"billingAddress": {
"syncId": null,
"address1": "Sample Address Line 1",
"address2": "",
"city": "Sample City",
"province": "Sample State",
"provinceCode": "WB",
"country": "India",
"countryCode": "IN",
"zip": "000001",
"phoneCountryCode": "+91",
"phone": "9999901001",
"company": "",
"firstName": "Demo",
"lastName": "Retailer",
"isDefault": true,
"isBillingAddress": true
}
}
]

Retailer Mapping

Retailer fields come from Sheet 1, also called the Customer Sheet.

API fieldExcel columnRule
nameContact or NameRetailer name.
firstNameContact or NameRetailer first name.
lastNameName_2Retailer last name.
phoneMobile_Phone_No or Phone_NoProcessed through the phone rules below.
syncIdNoUnique retailer identifier.
tagsSearch_NameSent as a one-item tags array when present.
createdAtGenerated automaticallyCurrent timestamp.
updatedAtGenerated automaticallyCurrent timestamp.
outStandingAmountBalance_LCYStored at the top level of the retailer payload.

Phone Number Rules

The integration chooses the retailer phone in this order:

  1. Read Mobile_Phone_No.
  2. If it contains multiple numbers separated by /, use the first number.
  3. If the selected number has more than 10 digits, keep the last 10 digits.
  4. If the result is invalid, try Phone_No.
  5. If Phone_No is also invalid, send an empty phone value.

Example:

Mobile_Phone_No = 9999901001/9999901002
Final phone = 9999901001

Shop Matching

Shops are read from Sheet 2 and attached to a retailer when this condition is true:

Sheet2.Customer_No = Sheet1.No

When a match exists, each matching Sheet 2 row becomes a separate shop under the retailer.

Shop Mapping

API fieldExcel columnRule
syncIdCustomer_No + / + CodeUnique shop identifier.
customerSyncIdCustomer_NoLinks the shop back to the retailer.
shopNameNameShop name.
cityCityShop city.
stateState_CodeShop state.
pincodePost_CodeShop postal code.
gstNumberSheet 1 GST_Registration_NoGST number for the retailer/shop.
outStandingAmountSheet 1 Balance_LCYUsed for the primary or first shop when applicable.

Shop syncId example:

Customer_No = RET-1001
Code = SHOP-001
syncId = RET-1001/SHOP-001

Address Mapping

Retailer address fields are used to generate billing and shipping addresses.

API fieldExcel column
address1Address
address2Address_2
cityCity
provinceState_Code
countryCountry_Region_Code
zipPost_Code
phonePhone_No

Shop Creation Logic

Case 1: Retailer Has No Matching Shops

If the retailer does not have matching rows in Sheet 2, the integration creates one shop automatically from the retailer row.

Shop fieldValue used
Shop nameRetailer name
CityRetailer city
GSTRetailer GST

Two addresses are generated from the retailer address fields:

  • Billing address
  • Shipping address

Case 2: Retailer Has Matching Shops

If Sheet 2 contains one or more rows where Customer_No matches the retailer No, each matching row becomes a separate shop. In this case:

  • Shop address comes from Sheet 2.
  • Billing address still comes from Sheet 1.
  • The shop remains linked to the retailer through customerSyncId.

Tags

Retailer tags come from Search_Name.

Search_Name = DEMO_TAG

Result:

{
"tags": ["DEMO_TAG"]
}

Normalized Final Payload

The example below is normalized as valid JSON and uses the documented shop syncId rule.

Normalized final payload
[
{
"name": "Demo Retailer",
"firstName": "Demo",
"lastName": "Retailer",
"email": "",
"phoneCountryCode": "+91",
"phone": "9999901001",
"syncId": "RET-1001",
"note": "",
"tags": ["DEMO_TAG"],
"taxExempt": false,
"createdAt": "2026-03-09T07:46:56Z",
"updatedAt": "2026-03-09T11:41:27Z",
"outStandingAmount": "1500",
"billingAddress": {
"syncId": null,
"address1": "Sample Address Line 1",
"address2": "",
"city": "Sample City",
"province": "Sample State",
"provinceCode": "WB",
"country": "India",
"countryCode": "IN",
"zip": "000001",
"phoneCountryCode": "+91",
"phone": "9999901001",
"company": "",
"firstName": "Demo",
"lastName": "Retailer",
"isDefault": true,
"isBillingAddress": true
},
"shops": [
{
"syncId": "RET-1001/SHOP-001",
"customerSyncId": "RET-1001",
"shopName": "Demo Shop",
"outStandingAmount": "1500",
"shopAddress": "",
"city": "Sample City",
"state": "Sample State",
"pincode": "000001",
"documentType": "GST",
"gstNumber": "22AAAAA0000A1Z5",
"identityAttachments": [],
"shopPhotos": [],
"isPrimary": true,
"shippingAddress": {
"syncId": null,
"address1": "Sample Address Line 1",
"address2": "",
"addressType": "SHIPPING_ADDRESS",
"city": "Sample City",
"province": "Sample State",
"provinceCode": "WB",
"country": "India",
"countryCode": "IN",
"zip": "000001",
"phoneCountryCode": "+91",
"phone": "9999901001",
"company": "",
"firstName": "Demo",
"lastName": "Retailer"
}
}
]
}
]

Excel Column Checklist

Use this checklist before running the sync.

Excel columnSheetUsed for
NoSheet 1Retailer unique identifier
ContactSheet 1Retailer first name
NameSheet 1Retailer name
Name_2Sheet 1Retailer last name
Mobile_Phone_NoSheet 1Primary retailer phone
Phone_NoSheet 1Backup retailer phone
Search_NameSheet 1Retailer tag
Balance_LCYSheet 1Outstanding amount
AddressSheet 1Retailer address line 1
Address_2Sheet 1Retailer address line 2
CitySheet 1Retailer city
State_CodeSheet 1Retailer state
Country_Region_CodeSheet 1Retailer country
Post_CodeSheet 1Retailer postal code
GST_Registration_NoSheet 1GST number
Customer_NoSheet 2Links shop to retailer
CodeSheet 2Shop identifier
NameSheet 2Shop name
AddressSheet 2Shop address line 1
Address_2Sheet 2Shop address line 2
CitySheet 2Shop city
State_CodeSheet 2Shop state
Country_Region_CodeSheet 2Shop country
Post_CodeSheet 2Shop postal code

Responses

All Rows Valid

All rows valid
{
"count": 3,
"message": "All rows are being synced. You will be notified once done."
}

Some Rows Invalid, Up To 20 Errors

Some rows invalid
{
"count": 5,
"message": "Some rows had validation errors and were not processed. Please check the nonProcessableRows field for details.",
"nonProcessableRows": [
{
"row": 2,
"errors": ["Invalid phone number"]
},
{
"row": 4,
"errors": ["Missing shop name"]
}
]
}

Some Rows Invalid, More Than 20 Errors

When more than 20 rows fail validation, the response only returns the first 20 non-processable rows. Complete details can be checked in the bulk upload logs.

More than 20 invalid rows
{
"count": 50,
"message": "Only showing first 20 of 32 non-processable rows. Please check the nonProcessableRows field for details. You can also check the bulk upload log for complete details.",
"nonProcessableRows": [
{
"row": 1,
"errors": ["Invalid GST number"]
},
{
"row": 2,
"errors": ["Missing address"]
}
]
}

All Rows Invalid

All rows invalid
{
"count": 0,
"message": "All rows had validation errors and were not processed. Please check the nonProcessableRows field for details.",
"nonProcessableRows": [
{
"row": 1,
"errors": ["Missing retailer name"]
},
{
"row": 2,
"errors": ["Invalid document type"]
}
]
}

Response Field Notes

FieldMeaning
countTotal number of rows received in the request.
nonProcessableRowsPresent only when validation errors exist.
nonProcessableRows[].rowRow number that failed validation.
nonProcessableRows[].errorsValidation messages for that row.

Implementation Checklist

  • Confirm Sheet 1 has one row per retailer.
  • Confirm Sheet 2 shop rows use Customer_No values that exist in Sheet 1 No.
  • Confirm every retailer has a stable No value.
  • Confirm phone values are clean or can be normalized by the phone rules.
  • Confirm Balance_LCY is available when outstanding amount is required.
  • Confirm address columns are present for billing and shipping address creation.
  • Send a JSON array, even when syncing one retailer.