TreeINFO-For-M365 documentation
    Preparing search index...

    WS Advanced – OAuth, Dynamic URL, Parameters, SOAP

    When using WSDataSource, the custom function ExternalLookupCustomFunction_<fieldInternalName> can return not only a request body, but also URL/header/parameter overrides or a pre-fetched response. The function may be async (return a Promise); the result is awaited before processing.

    The custom function return type is ExternalLookupCustomFunctionResult:

    Return value Meaning
    undefined No body; URL and method from configuration.
    plain object or string Used as request body (backward compatible).
    { request: ExternalLookupRequestOverride } Override URL / headers / body / query params; missing values fall back to configuration.
    { response: string | object } Direct response — no HTTP call is made; the value is passed straight to result mapping. See WSDirectResponseExample.

    Obtain a token inside the function (from your own logic, storage, or a token endpoint) and pass it via headers using ExternalLookupRequestOverride:

    function ExternalLookupCustomFunction_myApiLookup(searchText) {
    var token = getAccessToken(); // your logic: cache, refresh, etc.
    return {
    request: {
    headers: {
    "Authorization": "Bearer " + token,
    "Content-Type": "application/json"
    },
    body: { query: searchText }
    }
    };
    }

    The URL is taken from WSDataSourceConfiguration.Url unless request.url is specified. For GET requests you can use request.params instead of a body.


    URL and parameters can be constructed in code:

    function ExternalLookupCustomFunction_mySearch(searchText) {
    var baseUrl = "https://api.example.com/v2/search";
    return {
    request: {
    url: baseUrl,
    params: {
    q: searchText,
    limit: "20"
    }
    }
    };
    }

    request.url overrides the Url from configuration; request.params are used as query parameters (for both GET and POST).


    Use request.method to send a request with an HTTP method other than the one configured in MethodName (e.g. PUT, PATCH, DELETE):

    function ExternalLookupCustomFunction_myLookup(searchText) {
    return {
    request: {
    method: "PATCH",
    url: "https://api.example.com/search",
    body: { query: searchText }
    }
    };
    }

    request.method is ignored when RequestType is "SOAP" — SOAP requests always use "POST".


    POST Body with Headers and Parameters Combined

    Body, headers, and query parameters can be combined freely:

    function ExternalLookupCustomFunction_myLookup(searchText) {
    return {
    request: {
    body: { filter: searchText },
    params: { format: "json" },
    headers: { "X-API-Key": "..." }
    }
    };
    }

    SOAP calls are driven by setting RequestType to "SOAP" in WSDataSourceConfiguration. When RequestType is "SOAP", the request is always sent as an HTTP POST with Content-Type: text/xml; charset=utf-8 and the SOAPAction header. The custom function must return { request: { soapAction, soapEnvelope } } — both fields are required; without them the SOAP call cannot be constructed and a warning is emitted.

    Set RequestType to "SOAP" and ResultSetType to "XML". The ResultSetItems path uses dot-delimited local element names (namespace prefixes are ignored) to navigate to the repeating elements within the SOAP response body.

    {
    "DataSourceType": "WS",
    "DataSourceConfiguration": {
    "Url": "https://example.com/ws/SupplierService",
    "Cache": "1:0:0",
    "RequestType": "SOAP",
    "MethodName": "POST",
    "ResultSetItems": "Body.SearchSupplierResponse.Suppliers.Supplier",
    "ResultSetType": "XML",
    "ResultSet": [
    { "Name": "CompanyName", "Path": "Name" },
    { "Name": "Id", "Path": "SupplierId" },
    { "Name": "City", "Path": "Address.City" }
    ]
    },
    "IndexValue": { "Expression": "{Id}" },
    "TextValue": { "Expression": "{CompanyName}" },
    "RenderMode": "DataTable",
    "DataTableElements": [
    { "ColumnTitle": "Name", "Expression": "{CompanyName}" },
    { "ColumnTitle": "ID", "Expression": "{Id}" },
    { "ColumnTitle": "City", "Expression": "{City}" }
    ]
    }

    The custom function builds the SOAP envelope and returns it via request.soapAction and request.soapEnvelope. Both fields are required when RequestType is "SOAP" — omitting either will cause a console warning and the SOAP call will not be constructed correctly:

    function ExternalLookupCustomFunction_s_soapSupplier(searchText) {
    return {
    request: {
    soapAction: "http://example.com/ws/SearchSupplier",
    soapEnvelope:
    '<?xml version="1.0" encoding="utf-8"?>' +
    '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://example.com/ws/">' +
    '<soap:Body>' +
    '<ws:SearchSupplier>' +
    '<ws:Query>' + searchText + '</ws:Query>' +
    '</ws:SearchSupplier>' +
    '</soap:Body>' +
    '</soap:Envelope>'
    }
    };
    }

    Note: If searchText may contain XML special characters (<, >, &, ", '), escape it before embedding in the envelope to avoid malformed XML. Example: searchText.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')

    Given the configuration above, a SOAP response like the following is parsed using ResultSetItems to find repeating Supplier elements, and ResultSet to extract values from child elements:

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
    <SearchSupplierResponse xmlns="http://example.com/ws/">
    <Suppliers>
    <Supplier>
    <SupplierId>42</SupplierId>
    <Name>Aricoma a.s.</Name>
    <Address><City>Praha</City></Address>
    </Supplier>
    <Supplier>
    <SupplierId>99</SupplierId>
    <Name>Acme Corp.</Name>
    <Address><City>Brno</City></Address>
    </Supplier>
    </Suppliers>
    </SearchSupplierResponse>
    </soap:Body>
    </soap:Envelope>

    The XML path resolution uses local element names — namespace prefixes (like soap:) are stripped automatically, so Body.SearchSupplierResponse.Suppliers.Supplier matches regardless of the namespace prefix used.

    Both ResultSetItems and ResultSet[].Path use the same dot-delimited path syntax based on local element names:

    Path Resolves to
    Body.SearchSupplierResponse.Suppliers.Supplier All <Supplier> elements
    Name <Name> child of each item
    Address.City <City> inside <Address> inside each item

    Existing custom functions that return only a body object (or undefined) continue to work without any changes:

    // This still works exactly as before
    function ExternalLookupCustomFunction_s_dodavatel(searchText) {
    if (searchText.match(/\d+/)) return { ico: [searchText] };
    return { obchodniJmeno: searchText };
    }