Working code:

int GetGlobalOrder(char *global, int start, int end, CACHE_EXSTRP result)
{
    if (isInitialized == false) {
        Initialize(NULL);
    }

    // narg Number of subscript expressions pushed onto the argument stack.
    int narg = 1;

    // Direction for the $Order is 1 for forward, -1 for reverse.
    int dir = 1;
    // Indicates whether the data value, if there is one, should be returned.
    int valueflag = 1;

    // Has argument flag
    int flag = 0;

    // key - current subscript
    int key = start - 1;
    
    // row count
    int c=0;

    while (key<end) {
        CACHEPUSHGLOBAL(strlen(global), global);
        CACHEPUSHINT(key);
        CACHEGLOBALORDER(narg,  dir, valueflag);

        CACHEPOPINT(&flag);
        if (flag) {
          //use CACHETYPE() to get the value type, then the appropriate CACHEPOP for the subscript
          CACHEEXSTRKILL(result);
          CACHEPOPEXSTR(result);
          // do stuff with result
        }
        //use CACHETYPE() to get the subscript type, then the appropriate CACHEPOP for the subscript
        int type = CACHETYPE();

        CACHEPOPINT(&key);

        if (key==NULL) {
            break;
        }

        c++;
    }
    return ZF_SUCCESS;
}

Thanks to  @Chuck Sorenson for help!

Here's a sample C code to iterate over $lb structure.

#include <math.h>

/// Convert unsigned integer bytes to integer
int64_t makeint(const char *buff, size_t offset, size_t offsetinint, size_t len)
{
    union
    {
        int64_t i64;
        uint8_t u8[8];
    }d64;

    offsetinint = offsetinint & 7;
    memset(&d64, 0, sizeof(d64));

    memcpy(&d64.u8[offsetinint], buff + offset, len > (8 - offsetinint) ? (8 - offsetinint) : len);
    return d64.i64;
}

/// get next power of 2 greater than v
int64_t next2(int64_t v)
{
    v--;
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v |= v >> 32;
    v++;
    return v;
}

/// List types. NONE and NONE2 are placeholders
/// See %CACHE_HOME%\dev\Cache\callout\demo\czf.pdf (Section "Lists")
enum ListTypes {NONE, STRING, USTRING, NONE2, INTP, INTN, DOUBLEP, DOUBLEN, FLOAT};

/// This function iterates over all elements in a list
void ListToTuple(CACHE_EXSTRP result)
{
    // $lb structure
    char* list = result->str.ch;
    int listLength = result->len;

    // current element
    int num = 0;

    // current byte position
    int i=0;

    // length of current element
    int l = 0;

    // datatype
    int type = 0;

    while (i<listLength) {

        // Calculate length of current element - START
        if (0 == (l = (list[i]&255))) {

            // First BYTE is 0, length is in following 2 BYTEs
            size_t t_n = ((list[i+1]&255)|((list[i+2]&255)<<8));
            if (t_n != 0) {
                  l = t_n + 3;
            } else {
                // 4 Byte length
                l = ((list[i+3]&255) | ((list[i+4]&255) << 8) | ((list[i+5]&255) << 16) | ((list[i+6]&255) << 24)) + 7;
            }
        }
        // Calculate length of current element - END

        // Calculate data position - START
        int dataStart = 0;
        int dataLength = 0;

        if (l < 255) {
            type = list[i+1];
            dataStart = i + 2;
            dataLength = l - 2;
        } else if (l < 65536) {
            type = list[i+3];
            dataStart = i + 4;
            dataLength = l - 4;
        } else {
            type = list[i+7];
            dataStart = i + 8;
            dataLength = l - 8;
        }
        // Calculate data position - END
        
        if (type==STRING) {
            char* value;
            memcpy(value, list+dataStart, dataLength);
        } else if (type==USTRING) {
            char* value;
            memcpy(value, list+dataStart, dataLength/2);
        } else if (type == INTP) {
            int64_t value = makeint(list, dataStart, 0, dataLength);
        } else if (type==INTN) {
            int64_t value = 0;
            if (l==2) {
                value = -1;
            } else {
                memcpy(&value, list+dataStart, dataLength);
                if (value == 0) {
                    value = - (1 << (dataLength * 8));
                } else {
                    int64_t pow2 = next2(value);
                    value = value - pow2;
                }
            }
        } else if (type==DOUBLEP) {
            int64_t temp = makeint(list, dataStart+1, 0, dataLength-1);
            signed char exp = list[dataStart];
            double value = temp*pow(10, exp);
        } else if (type==DOUBLEN) {
            int64_t temp = 0;
            memcpy(&temp, list+dataStart+1, dataLength-1);
            if (temp == 0) {
                temp = - (1 << (dataLength * 8));
            } else {
                int64_t pow2 = next2(temp);
                temp = temp - pow2;
            }
            signed char exp = list[dataStart];
            double value = temp*pow(10, exp);
        } else if (type==FLOAT) {
            double value;
            memcpy(&value, list+dataStart, 8);
        }
        
        i += l;
        num++;
    }
}

Your error looks like it's cause because there's no HeaderOut property defined in current class.

Anyway, you need to create a custom class which would be your header:

Class custom.Header Extends %SOAP.Header
{

Parameter XMLFORMAT = "literal";

Parameter XMLIGNORENULL = "RUNTIME";

Parameter NAMESPACE = "urn:epic-com.2013.Interconnect.Headers";

/// The XMLPREFIX parameter controls the prefix to be used for the XML namespace that
/// is given by the NAMESPACE parameter.
///Parameter XMLPREFIX As STRING = "";

Parameter XMLTYPE = "Epic-Client-ID";

Property Value As %String(XMLPROJECTION = "CONTENT", MAXLEN = 36) [ InitialExpression = "12349fe5-2ff8-4b79-b723-e69efbabcdef" ];

}

After that you need to create an instance of this class and add it to HeaderOut property of your WS client.