REST API

All of Relution’s functionality is available through an extensive REST API. This API can be used to control Relution from any other application or via scripts.


API Documentation

Every Relution server ships the complete API specification, matching exactly the installed server version. It documents all endpoints with their parameters, request and response bodies, and the returned status codes.

The specification is opened from the Relution portal: the entry Web API in the support menu (top right corner) opens the interactive documentation. Alternatively, it is available directly at https://<server>/web-api/index.html.

Requests to the REST API are subject to the same permission checks as actions performed in the portal, and security-relevant actions are recorded in the Audit Log →.


Create API Access Tokens

To access Relution from third-party systems or scripts, an API Access Token is required.

  1. Click on the username in the upper right corner of the Relution portal.
  2. Navigate to: Profile → Access Token → Add Access Token.
  3. Name the token and copy it.

Use the token in the X-User-Access-Token HTTP header to authenticate requests.


1. Users in CSV File Format

  • In the first line of the .csv file, insert a header line with the desired property names.
  • The order of the columns is not relevant.
  • Properties that are not needed can be omitted.
  • Mandatory fields:

userid,email,first name,last name,password

Passwords must follow the policy under Settings → Password Policy. Phone numbers must be in international format, e.g. +4912345.

Up to 15 custom properties can be added, named: custom1 – custom15

👉 Download a template via the Relution Portal: Users → Users → Three dots menu → Import users via CSV → Download template

Example CSV:

name,email,givenName,surName,password,phone,position,country,managedAppleId,custom1,custom2
user1,user1@company.com,Heinz,Ketchup,p4ssw0rd,+49234346345,user1 position,user1 country,user1.id@company.com,43,Operations
user2,user2@company.com,Heinz,Ketchup,p4ssw0rd,+49234346345,user2 position,user2 country,user2.id@company.com,24,Marketing
user3,user3@company.com,Heinz,Ketchup,p4ssw0rd,+49234346345,user3 position,user3 country,user3.id@company.com,30,HR
user4,user4@company.com,Heinz,Ketchup,p4ssw0rd,+49234346345,user4 position,user4 country,user4.id@company.com,32,Sales
user5,user5@company.com,Heinz,Ketchup,p4ssw0rd,+49234346345,user5 position,user5 country,user5.id@company.com,45,Finance
user6,user6@company.com,Heinz,Ketchup,p4ssw0rd,+49234346345,user6 position,user6 country,user6.id@company.com,30,Purchase

2. Uploading Users via the Relution Portal

  1. Go to: Users → Users → Three dots menu → Import users via CSV
  2. Select the CSV file and separator.
  3. Assign imported users to one or more groups.
  4. Imported users will then appear in the overview.

3. Creating Users via the Relution API

Steps

  1. Prepare a users.csv file.

  2. Copy the bash script below into a file, e.g. uploadUserCSV.sh.

  3. Make it executable:

    chmod +x ./uploadUserCSV.sh
    
  4. Run it:

./uploadUserCSV.sh -f 'FILEPATH' -h 'HOST' -a 'YOUR-ACCESS-TOKEN' -u 'ORGANIZATION_UUID'

Arguments

ArgumentMeaningExampleDefaultRequired
-fPath to the users.csvuser.csv✅
-hRelution hosthttps://relution.io✅
-aAPI access tokenxxxxxxxx✅
-uOrganization UUID01995C17-6BD0-7F84-87E9-21A1498B386F✅
-rRoles assigned to usersOrganame%20Device%20User❌
-sSeparator for parsingSEMICOLONCOMMA❌
-wOverwrite existing userstruefalse❌

Output Example

============
success: 1, skipped: 1, failed: 1
2: [SUCCESS] User ... successfully stored with version 1.
3: [FAILED] Number of data fields does not match number of headers.
4: [SKIPPED] User Test2 should not be overwritten.

4. Bash Script: Upload Users via CSV

Show full configuration example
 #!/bin/bash
set -e
# --------------------------------------------------------------------------------

while getopts "a:h:f:u:w:r:s:" opt; do
  case $opt in
    a) # apikey
    ACCESS_TOKEN="$OPTARG"
    ;;

    h) # host
    HOST="$OPTARG"
    ;;

    f) # File
    FILE="$OPTARG"
    ;;

    u) # organization uuid
    ORGANIZATION_UUID="$OPTARG"
    ;;

    w) # Overwrite
    OVERWRITE="$OPTARG"
    ;;

    r) # Role
    ROLE="$OPTARG"
    ;;

    s) # Separator
    SEPARATOR="$OPTARG"
    ;;

    \?)
    echo "Unknown option -$OPTARG" >&2
    ;;
  esac
done

if [[ -z ACCESS_TOKEN ]]; then
  echo "Please specify an access token (-a)"
  exit 1
fi

if [[ -z $HOST ]]; then
  echo "Please specify a Host URL (-h)"
  exit 1
fi

if [[ ! -f $FILE ]]; then
  echo "Please specify an existing csv file (-f)"
  exit 1
fi

if [[ -z $ORGANIZATION_UUID ]]; then
  echo "Please specify an organization uuid (-o)"
  exit 1
fi

if [[ -z $SEPARATOR ]]; then
  SEPARATOR="COMMA"
fi

if [[ -z $OVERWRITE ]]; then
  OVERWRITE=false
fi

if [[ -z $ROLE ]]; then
  ROLE=Organame%20Device%20User
fi

call() {
 # Create a temporary file for the response body
  tmpfile=$(mktemp) || { echo "Failed to create temp file" >&2; exit 1; }

  http_code=$(curl -sw "%{http_code}" -o "$tmpfile" -H "Accept: application/json" -H "X-User-Access-Token: $ACCESS_TOKEN" "$@")
  content=$(cat "$tmpfile")
  rm -f "$tmpfile"

  if [[ "$http_code" != "200" &&  "$http_code" != "201" ]]; then
    echo "fail: $http_code" >&2
    echo $content >&2
    exit 1
  fi

 # Output the content for capture
  printf "%s" "$content"
}

echo "Will start importing users to '${ORGANIZATION_UUID}' on server '${HOST} as '${ROLE//%20/ }'".
echo "Using '$FILE' with separator of type '$SEPARATOR'."
echo "Next step: uploading file"
upload=$(call -F "file=@$FILE;type=text/csv" "$HOST/api/management/v1/csvImport/upload/users")
file_uuid=$(echo "$upload" | jq -r '.results[0].uuid')
echo "Done uploading file: $file_uuid"

echo "============"

echo "Next step: start import job"
job=$(call -X POST "$HOST/api/management/v1/security/users/import/fromFile/$file_uuid?overwrite=$OVERWRITE&role=$ROLE&csvSeparator=$SEPARATOR")
job_uuid=$(echo "$job" | jq -r '.uuid')
echo "Job started: $job_uuid"

job_status=""
while [[ "$job_status" != "FINISHED" && "$job_status" != "FAILED" ]]; do
  sleep 1
  job=$(call -X GET "$HOST/api/management/v1/csvImport/job/$job_uuid")
  job_status=$(echo "$job" | jq -r '.results[0].status')
  echo "Job status: $job_status"
done

echo "============"

success_count=$(echo "$job" | jq -r '.results[0].successCount')
skipped_count=$(echo "$job" | jq -r '.results[0].skippedCount')
failure_count=$(echo "$job" | jq -r '.results[0].failureCount')
echo "success: $success_count, skipped: $skipped_count, failed: $failure_count"

if [[ "$failure_count" -ne 0 ]]; then
  query='{"sortOrder":{"sortFields":[{"name":"line","ascending":true}]}}'
  report=$(call -X POST -H "Content-Type: application/json" --data "$query" "$HOST/api/management/v1/csvImport/job/$job_uuid/entityStates/query")
  reports=$(echo $report | jq -c '.results')
  echo "$reports" | jq -c '.[]' | while read -r item; do
    line=$(echo "$item" | jq -r '.line')
    status=$(echo "$item" | jq -r '.status')
    message=$(echo "$item" | jq -r '.message')
    echo "$line: [$status] $message"
  done
fi

5. Create a New Organization

Show full configuration example
 #!/bin/bash
 #--------------------------------------------------------------------------------

 # Web service URL, change the server name as needed
    SVR_URL="https://<myserver>/api/management/v1/security/organizations/creationWizardRequests"

 # Access token of System Admin, create it by opening https://<server>/#/profile
 # Open this URL in your browser while logged in as System Administrator.
 # NOTE: This is not an URL you can reach through clicking through the portal!
    ACCESS_TOKEN="<sysadmin_api_token>"

 # HTTP Headers
    ACCEPT="application/json"
    ACCEPT_CHARSET="UTF-8"

 # Query example
    read -r -d '' JSON_BODY << 'EOF'
    {
        "limit": 10,
        "offset": 0,
        "getNonpagedCount": true,
        "sortOrder": {
            "sortFields": [
                {
                    "name": "lastConnectionDate",
                    "ascending": false
                }
            ]
        },
        "filter": {
            "type": "logOp",
            "operation": "AND",
            "filters": [
                {
                    "type": "stringEnum",
                    "fieldName": "platform",
                    "values": [
                        "ANDROID",
                        "ANDROID_ENTERPRISE",
                        "IOS"
                    ]
                },
                {
                    "type": "stringEnum",
                    "fieldName": "status",
                    "values": [
                        "COMPLIANT",
                        "INACTIVE"
                    ]
                }
            ]
        }
    }
    EOF

    echo "Querying devices at $SVR_URL..."
    echo

 # No changes should be required beyond this line...

    RESPONSE=$(curl -X POST \
        ${SVR_URL}${FILTER} \
        -H "X-User-Access-Token: $ACCESS_TOKEN" \
        -H "Accept: $ACCEPT" \
        -H "Accept-Charset: $ACCEPT_CHARSET" \
        -H "Content-Type: $ACCEPT" \
        --write-out " HTTP_STATUS=%{http_code}" \
        --silent \
        -d "$JSON_BODY")
    if [[ $RESPONSE =~ HTTP_STATUS=([0-9]+) ]]; then
        HTTP_STATUS=${BASH_REMATCH[1]}
    fi

    if [[ $HTTP_STATUS -lt 200 || $HTTP_STATUS -gt 299 ]]; then
        echo "HTTP status: $HTTP_STATUS"
    else
        echo "Done. Parsing ouput..."
        echo
        echo "${RESPONSE%HTTP_STATUS*}" | jq '.results[].name'
    fi

6. Query Devices in an Organization

Show full configuration example
 #!/bin/bash
 #--------------------------------------------------------------------------------

 # Web service URL, change the server name as needed
    SVR_URL="https://<myserver>/api/management/v2/devices/baseInfo/query"

 # Access token of an Orga Admin, create it by clicking on the user name in the top right corner of the portal.
 # Then click "Profile", "Access tokens", "Add".
    ACCESS_TOKEN="<orga_admin_access_token>"

 # HTTP Headers
    ACCEPT="application/json"
    ACCEPT_CHARSET="UTF-8"

 # Query example
    read -r -d '' JSON_BODY << 'EOF'
    {
        "limit": 100,
        "offset": 0,
        "getNonpagedCount": true,
        "sortOrder": {
            "sortFields": [
                {
                    "name": "lastConnectionDate",
                    "ascending": false
                }
            ]
        },
        "filter": {
            "type": "logOp",
            "operation": "AND",
            "filters": [
                {
                    "type": "stringEnum",
                    "fieldName": "platform",
                    "values": [
                        "ANDROID",
                        "ANDROID_ENTERPRISE",
                        "IOS"
                    ]
                },
                {
                    "type": "stringEnum",
                    "fieldName": "status",
                    "values": [
                        "COMPLIANT"
                    ]
                }
            ]
        }
    }
    EOF

    echo "Querying devices at $SVR_URL..."
    echo

 # No changes should be required beyond this line...

    RESPONSE=$(curl -X GET \
      ${SVR_URL}${FILTER} \
      -H "X-User-Access-Token: $ACCESS_TOKEN" \
      -H "Accept: $ACCEPT" \
      -H "Accept-Charset: $ACCEPT_CHARSET" \
      -H "Content-Type: $ACCEPT" \
      --write-out " HTTP_STATUS=%{http_code}" \
      --silent \
      -d "$JSON_BODY")
    if [[ $RESPONSE =~ HTTP_STATUS=([0-9]+) ]]; then
      HTTP_STATUS=${BASH_REMATCH[1]}
    fi

    if [[ $HTTP_STATUS -lt 200 || $HTTP_STATUS -gt 299 ]]; then
      echo " HTTP status: $HTTP_STATUS"
    else
        echo "Done. Parsing ouput..."
        echo ${RESPONSE%HTTP_STATUS*} | jq '.results[].name'
    fi
    echo

API Token Best Practices

  • Always set an expiration date on API access tokens. Tokens without expiration remain valid indefinitely.
  • Revoke tokens immediately when the associated account’s role changes, the account is deactivated, or the account holder leaves the organisation.
  • Token secrets are stored as BCrypt hashes and cannot be recovered after creation. Generate a new token when needed rather than attempting to retrieve an existing one.
Top