Published at: 2025-10-30
Custom Connector Configuration Guide
1 Document Usage Instructions
The custom connector is designed to integrate ShareCRM with other SaaS or on-premise systems that provide APIs, such as customer-developed services or third-party SaaS systems. After integration, users can synchronize business documents between systems automatically through the connector without manually logging into both systems to enter or verify data.
This document provides step-by-step guidance and FAQs for the Custom Connector. Intended audience: implementation consultants, technical consultants, sales managers, CSMs, and similar roles.
2 Custom Connector Implementation and Configuration
2.1 Pre-implementation Requirements
Before implementation, confirm that the customer tenant is activated and that the customer has purchased the Custom Connector product (verify that the order is active).
2.2 Implementation and Configuration
2.2.1 Connection Configuration
Create a new connector and select connector type: Custom Connector. Fill in the connector information:

Result format — instructions: If the API returns JSON like the example below, map the connector fields as follows: error code → code, error message → message, data → data, and success code = 0.
{ “code” : “0”, “message” : “成功”, “data” : { … } }
Get header script — instructions: There are two ways to add headers to requests. If you configure both, Headers parameter configuration takes precedence.
- Headers parameter configuration: parameter values support placeholders. Currently placeholders support only tenantId and push token (Connector → API Configuration → Time Subscription → Parameter Description → Header Parameter → token).


- Custom APL function, e.g.:
return [“token”:”test123”, “tenantId”:context.tenantId]
2.2.2 API Configuration
Continue to configure object structures and fields according to the customer’s API examples and API documentation. After configuration, you can inspect requests and responses via API Configuration. Use the request path and request template to send verification requests with Postman and compare the response body against the sample to ensure protocol compatibility.


2.2.3 API Adaptation
Use API functions to implement Create/Read/Update/Delete for objects. If the tenant service protocol is not compatible with the standard connector, use custom functions to call the tenant service and transform the response to match the connector configuration protocol. The following are examples of custom functions for each request type. You may also choose the appropriate function template when creating functions.

Note: After creating a function, set a parameter in the top-right: Type: Map, Name: syncArg. syncArg contains objectData, whose meaning varies across API interfaces.

API function common return value description:

Below are detailed parameter definitions and function examples by sync action.
2.2.3.1 Create
objectData parameter description:
| Field Name | Meaning | Notes |
|---|---|---|
| objAPIName | ERP object API name | |
| masterFieldVal | Primary Object data | |
| detailFieldVals | Sub-object data | key = sub-object apiName, value = list of detail records |
API function example:
/** * Connector object API - Create * Todo: modify or add implementation as required by the customer. * Ensure parameter Map type syncArg is configured in the top-right. **/ String errorCodeKey = “code” // Error code field name — matches connector’s error code field String successCode = “0” // Success code — matches connector’s success code String errorMessageKey = “message” // Error message field name — matches connector’s error msg field String dataKey = “data” // Data field name — matches connector’s data field String dataIdKey = “masterDataId” // Primary Object data id field name String detailDataIdsKey = “detailDataIds” // Sub-object data id field name
Map objectData = syncArg[“objectData”] as Map def apiName = objectData[“objAPIName”] // ERP object apiName, e.g. BD_MATERIAL def masterData = objectData[“masterFieldVal”] // Primary Object data // detail object data: key=sub-object apiName, value=list of detail records Map<String, List<Map<String, Object»> detailMap = objectData[“detailFieldVals”] as Map
// todo: create Map<String, String> headers = [“token”: “xxxxxx”] def url = “http://xxx/xxx//create” Map<String, Object> arg = [“objAPIName”: apiName, “masterData”: masterData, “detailMap”: detailMap] def (Boolean error, HttpResult httpResult, String msg) = http.post(url, headers, arg) if (error) { log.info(“Error creating ERP object: “ + msg) // return error Map<String, Object> result = [:] result[errorCodeKey] = “500” result[errorMessageKey] = msg return result } log.info(“Create ERP object, url:” + url + “ args:” + arg + “ response:” + httpResult) Map createDataResult = httpResult.content as Map
// todo: build return data
String masterDataId = createDataResult[“data”][“masterDataId”]
// todo: detail object IDs — key=sub-object apiName, value=list of detail IDs; must return IDs in the same order as input
Map<String, List
Map<String, Object> data = [:] data[dataIdKey] = masterDataId data[detailDataIdsKey] = detailIdsMap
// return success Map<String, Object> result = [:] result[errorCodeKey] = successCode result[errorMessageKey] = “” result[dataKey] = data return result
2.2.3.2 Update
objectData parameter description:
| Field Name | Meaning | Notes |
|---|---|---|
| objAPIName | ERP object API name | |
| masterFieldVal | Primary Object data | |
| detailFieldVals | Sub-object data | key = sub-object apiName, value = list of detail records |
API function example:
/** * Connector object API - Update * Todo: modify or add implementation as required by the customer. * Ensure parameter Map type syncArg is configured in the top-right. **/ String errorCodeKey = “code” String successCode = “0” String errorMessageKey = “message” String dataKey = “data” String dataIdKey = “masterDataId” String detailDataIdsKey = “detailDataIds”
Map objectData = syncArg[“objectData”] as Map def apiName = objectData[“objAPIName”] def masterData = objectData[“masterFieldVal”] Map<String, List<Map<String, Object»> detailMap = objectData[“detailFieldVals”] as Map
// todo: update Map<String, String> headers = [“token”: “xxxxxx”] def url = “http://xxx/xxx//update” Map<String, Object> arg = [“objAPIName”: apiName, “masterData”: masterData, “detailMap”: detailMap] def (Boolean error, HttpResult httpResult, String msg) = http.post(url, headers, arg) if (error) { log.info(“Error updating ERP object: “ + msg) Map<String, Object> result = [:] result[errorCodeKey] = “500” result[errorMessageKey] = msg return result } log.info(“Update ERP object, url:” + url + “ args:” + arg + “ response:” + httpResult) Map updateDataResult = httpResult.content as Map
// todo: build return data
String masterDataId = updateDataResult[“data”][“masterDataId”] as String
Map<String, List
Map<String, Object> data = [:] data[dataIdKey] = masterDataId data[detailDataIdsKey] = detailIdsMap
Map<String, Object> result = [:] result[errorCodeKey] = successCode result[errorMessageKey] = “” result[dataKey] = data return result
2.2.3.3 Batch Query for Update/Invalidation
The protocol for batch query-for-update and batch query-for-invalidate is the same. Note: One batch sync may call the batch query API multiple times with pagination using offset and limit.
objectData parameter description:
| Field Name | Meaning | Notes |
|---|---|---|
| objAPIName | ERP object API name | |
| startTime | Change start timestamp | unit: milliseconds |
| endTime | Change end timestamp | unit: milliseconds |
| offset | Record offset | |
| limit | Number of records in this request |
API function example:
/** * Connector object API - Query ERP data by time (batch) * Todo: modify or add implementation as required by the customer. * Ensure parameter Map type syncArg is configured in the top-right. **/ String errorCodeKey = “code” String successCode = “0” String errorMessageKey = “message” String dataKey = “data” String totalKey = “totalNum” String dataListKey = “dataList”
Map objectData = syncArg[“objectData”] as Map def apiName = objectData[“objAPIName”] def startTime = objectData[“startTime”] def endTime = objectData[“endTime”] def includeDetail = objectData[“includeDetail”] def offset = objectData[“offset”] def limit = objectData[“limit”]
// todo: get data Map<String, String> headers = [“token”: “xxxxxx”] def url = “http://xxx/xxx/queryMasterBatch?objAPIName=” + apiName + “&startTime=” + startTime + “&endTime=” + endTime + “&includeDetail=” + includeDetail + “&offset=” + offset + “&limit=” + limit def (Boolean error, HttpResult httpResult, String msg) = http.get(url, headers) if (error) { log.info(“Error querying ERP data by time: “ + msg) Map<String, Object> result = [:] result[errorCodeKey] = “500” result[errorMessageKey] = msg return result } log.info(“Query ERP by time, url:” + url + “ response:” + httpResult) Map<String, Object> queryDataByTimeResult = httpResult.content as Map int totalNum = queryDataByTimeResult[“data”][“totalNum”] as Integer List<Map<String, Object» dataList = queryDataByTimeResult[“data”][“dataList”] as List
// build return data Map<String, Object> data = [:] data[totalKey] = totalNum data[dataListKey] = dataList
Map<String, Object> result = [:] result[errorCodeKey] = successCode result[errorMessageKey] = “” result[dataKey] = data return result
2.2.3.4 Single Record (by ID) Query
objectData parameter description:
| Field Name | Meaning | Notes |
|---|---|---|
| objAPIName | ERP object API name | |
| dataId | Data id | |
| includeDetail | Whether to include sub-object data |
API function example:
/** * Connector object API - Query by ID * Todo: modify or add implementation as required by the customer. * Ensure parameter Map type syncArg is configured in the top-right. **/ String errorCodeKey = “code” String successCode = “0” String errorMessageKey = “message” String dataKey = “data” String masterDataKey = “masterFieldVal” String detailDataKey = “detailFieldVals”
Map objectData = syncArg[“objectData”] as Map def apiName = objectData[“objAPIName”] def dataId = objectData[“dataId”] def includeDetail = objectData[“includeDetail”]
// todo: get object data Map<String, String> headers = [“token”: “xxxxxx”] def url = “http://xxx/xxx/queryMasterById?objAPIName=” + apiName + “&dataId=” + dataId + “&includeDetail=” + includeDetail def (Boolean error, HttpResult httpResult, String msg) = http.get(url, headers) if (error) { log.info(“Error retrieving ERP object: “ + msg) Map<String, Object> result = [:] result[errorCodeKey] = “500” result[errorMessageKey] = msg return result } log.info(“Get ERP object, url:” + url + “ response:” + httpResult) Map<String, Object> erpData = httpResult.content as Map
// todo: split primary and sub-object data Map<String, Object> masterData = erpData[“data”][“masterData”] as Map // sub-object data: key=sub-object apiName, value=list of sub-object records Map<String, List<Map<String, Object»> detailsDataList = dataResult[“data”][“detailData”] as Map
// build return data Map<String, Object> data = [:] data[masterDataKey] = masterData data[detailDataKey] = detailsDataList
// return success Map<String, Object> result = [:] result[errorCodeKey] = successCode result[errorMessageKey] = “” result[dataKey] = data return result
2.2.3.5 Invalidate
objectData parameter description:
| Field Name | Meaning | Notes |
|---|---|---|
| objAPIName | ERP object API name | |
| masterFieldVal | Primary Object data | primary object id field: “_id” |
API function example:
/** * Connector object API - Invalidate * Todo: modify or add implementation as required by the customer. * Ensure parameter Map type syncArg is configured in the top-right. **/ String errorCodeKey = “code” String successCode = “0” String errorMessageKey = “message”
Map objectData = syncArg[“objectData”] as Map def apiName = objectData[“objAPIName”] def dataId = objectData[“masterFieldVal”][“_id”]
// todo: invalidate Map<String, String> headers = [“token”: “xxxxxx”] def url = “http://xxx/xxx//invalid” Map<String, Object> arg = [“objAPIName”: apiName, “masterFieldVal”: [“_id”: dataId]] def (Boolean error, HttpResult httpResult, String msg) = http.post(url, headers, arg) if (error) { log.info(“Error invalidating ERP object: “ + msg) Map<String, Object> result = [:] result[errorCodeKey] = “500” result[errorMessageKey] = msg return result } log.info(“Invalidate ERP object, url:” + url + “ args:” + arg + “ response:” + httpResult)
// return success Map<String, Object> result = [:] result[errorCodeKey] = successCode result[errorMessageKey] = “” return result
2.2.3.6 Re-enable (Un-disable)
objectData parameter description:
| Field Name | Meaning | Notes |
|---|---|---|
| objAPIName | ERP object API name | |
| masterFieldVal | Primary Object data | primary object id field: “_id” |
API function example:
/** * Connector object API - Re-enable * Todo: modify or add implementation as required by the customer. * Ensure parameter Map type syncArg is configured in the top-right. **/ String errorCodeKey = “code” String successCode = “0” String errorMessageKey = “message”
Map objectData = syncArg[“objectData”] as Map def apiName = objectData[“objAPIName”] def dataId = objectData[“masterFieldVal”][“_id”]
// todo: re-enable Map<String, String> headers = [“token”: “xxxxxx”] def url = “http://xxx/xxx//enable” Map<String, Object> arg = [“objAPIName”: apiName, “masterFieldVal”: [“_id”: dataId]] def (Boolean error, HttpResult httpResult, String msg) = http.post(url, headers, arg) if (error) { log.info(“Error re-enabling ERP object: “ + msg) Map<String, Object> result = [:] result[errorCodeKey] = “500” result[errorMessageKey] = msg return result } log.info(“Re-enable ERP object, url:” + url + “ args:” + arg + “ response:” + httpResult)
// return success Map<String, Object> result = [:] result[errorCodeKey] = successCode result[errorMessageKey] = “” return result
2.2.3.7 Invalidate Detail Record
objectData parameter description:
| Field Name | Meaning | Notes |
|---|---|---|
| objAPIName | ERP object API name | |
| masterFieldVal | Primary Object data | primary object id field: “_id” |
| detailFieldVals | Detail object data | key = sub-object apiName, value = sub-object data id |
API function example:
/** * Connector object API - Invalidate sub-object * Todo: modify or add implementation as required by the customer. * Ensure parameter Map type syncArg is configured in the top-right. **/ String errorCodeKey = “code” String successCode = “0” String errorMessageKey = “message”
Map objectData = syncArg[“objectData”] as Map def apiName = objectData[“objAPIName”] def dataId = objectData[“masterFieldVal”][“_id”] // sub-object data: key=sub-object apiName, value=sub-object data id Map<String, String> detailDataIdMap = objectData[“detailFieldVals”] as Map
// todo: invalidate detail Map<String, String> headers = [“token”: “xxxxxx”] def url = “http://xxx/xxx//invalidDetail” Map<String, Object> arg = [“objAPIName”: apiName, “masterFieldVal”: [“_id”: dataId], “detailFieldVal”: detailDataIdMap] def (Boolean error, HttpResult httpResult, String msg) = http.post(url, headers, arg) if (error) { log.info(“Error invalidating ERP sub-object: “ + msg) Map<String, Object> result = [:] result[errorCodeKey] = “500” result[errorMessageKey] = msg return result } log.info(“Invalidate ERP sub-object, url:” + url + “ args:” + arg + “ response:” + httpResult)
// return success Map<String, Object> result = [:] result[errorCodeKey] = successCode result[errorMessageKey] = “” return result
2.2.3.8 Event Subscription
objectData parameter description (special note: For Kingdee K3C push, masterFieldVal must include “id”:”ID”. For more details, see Event Subscription):
| Field Name | Meaning | Notes |
|---|---|---|
| objAPIName | ERP object API name | |
| masterFieldVal | Primary Object data | |
| detailFieldVals | Sub-object data | key = sub-object apiName, value = list of detail records |
API function example:
todo: modify or add implementation as required by the customer. * Ensure parameter Map type syncArg is configured in the top-right. **/ String errorCodeKey = “code” String successCode = “0” String errorMessageKey = “message” String dataKey = “data” String dataListKey = “dataList” String masterDataKey = “masterFieldVal” String detailDataKey = “detailFieldVals”
Map objectData = syncArg[“objectData”] as Map def apiName = objectData[“objAPIName”] Map<String, Object> masterData = objectData[“masterFieldVal”] as Map Map<String, List<Map<String, Object»> detailMap = objectData[“detailFieldVals”] as Map
// todo: adjust data Map<String, String> headers = [“token”: “xxxxxx”] def url = “http://xxx/xxx//asyncpush” Map<String, Object> arg = [“objAPIName”: apiName, “masterFieldVal”: [“_id”: masterData[“dataId”]]] def (Boolean error, HttpResult httpResult, String msg) = http.post(url, headers, arg) if (error) { log.info(“Error pushing ERP object: “ + msg) Map<String, Object> result = [:] result[errorCodeKey] = “500” result[errorMessageKey] = msg return result } log.info(“Push ERP object, url:” + url + “ args:” + arg + “ response:” + httpResult) Map<String, Object> dataResult = httpResult.content as Map
// todo: build object data Map<String, Object> object = dataResult[“data”][“masterData”] as Map Map<String, List<Map<String, Object»> detailsDataList = dataResult[“data”][“detailData”] as Map
Map<String, Object> pushData = [:] pushData[masterDataKey] = object pushData[detailDataKey] = detailsDataList Map<String, Object> data = [:] data[dataListKey] = [pushData]
// return success Map<String, Object> result = [:] result[errorCodeKey] = successCode result[errorMessageKey] = “” result[dataKey] = data return result
2.3 Optional: Support via Proxy Service
If the tenant environment cannot access the public internet or does not expose services externally, use the proxy service (attach contains proxy program code). Proxy service configuration:
Modify application.properties token to authenticate incoming calls and ensure requests originate from the Integration Platform.
Note: Configure the same X-fs-erpdss-token value in the connection headers as set in the proxy service.


Implement RouterAdaptController:
- create: called when creating in ERP
- update: called when updating in ERP
- invalid: invalidates ERP primary data
- queryMasterBatch: ERP -> CRM sync uses this to query ERP incremental data
- queryMasterById: ERP -> CRM uses this to query a single record by ERP primary key
3 FAQ
Under construction…
Attachments
erpdss-template-stdapi.zip 315.0 KB
https://saas.bk-cdn01.com/t/e77d24d5-3d21-4bbc-8ec5-886214579a51/u/94794ae2-e207-45f4-ad4c-20efe8d21ba1/1736417363228/erpdss-template-stdapi.zip