Skip to content

API

The web interface does everything through the server's REST API, and other programs can use the same API. Its routes start with /api/v1/.

Every running server describes all its routes at /scalar, for example http://localhost:7080/scalar. Log in to the web interface in the same browser first; the description needs it. This page covers logging in and the most common requests.

Two routes need neither a login nor a license: GET /api/v1/activated returns whether a license is active, and GET /api/v1/version returns the server version, e.g. "1.0.0".

The examples use Host: localhost; send the requests to the address and port of the server, 7080 by default.

Authentication

Nearly all requests need a logged-in user, and all of them except the two above need an active license. The web interface uses a session cookie. Other programs log in for an access token and send it with every request.

Creating the first account

On a server without accounts, the first administrator account can also be created through the API, as the Welcome page does:

POST /api/v1/users/admin HTTP/1.1
Host: localhost
Content-Type: application/json

{"username":"<username>","password":"<password>"}

This fails with 401 once an account exists.

Logging in

POST /api/v1/users/login?useCookies=false HTTP/1.1
Host: localhost
Content-Type: application/json

{"username":"<username>","password":"<password>"}

useCookies=false asks for tokens instead of a cookie:

{
    "tokenType": "Bearer",
    "accessToken": "<access token>",
    "expiresIn": 3600,
    "refreshToken": "<refresh token>"
}

Keep both tokens somewhere safe. The access token is valid for an hour (expiresIn is in seconds).

A wrong username or password is answered with 401. After 5 failed attempts the account is locked for 5 minutes; until then the answer is 400, with the end of the lock in errors.endDate.

Refreshing the access token

When the access token expires, exchange the refresh token for new tokens:

POST /api/v1/users/refresh HTTP/1.1
Host: localhost
Content-Type: application/json

{"refreshToken":"<refresh token>"}

The answer is the same as for logging in: a new access token for an hour, and a new refresh token for 14 days. An expired or invalid refresh token is answered with 401, and so is a refresh after the user's password or roles changed; log in again then.

Making authenticated requests

Send the access token in the Authorization header, after Bearer. For example, the current state of all scales:

GET /api/v1/devices/states HTTP/1.1
Host: localhost
Authorization: Bearer <access token>
{
    "01997046-fe18-78bf-8895-fed35b18b997": {
        "connectionStatus": 4,
        "observingWeight": true,
        "weight": {
            "statusBits": 112,
            "alibiText": null,
            "deviceId": "01997046-fe18-78bf-8895-fed35b18b997",
            "significantDigits": 1,
            "protocol": 1,
            "status": 0,
            "unit": 0,
            "net": 93,
            "gross": 161.6,
            "tare": 68.600006,
            "stable": true,
            "inZeroRange": false,
            "time": "2025-09-30T12:37:23.9714247+00:00"
        }
    }
}

Enumerations are numbers:

Field Values
connectionStatus 0 disconnected, 1 error, 2 pending (waiting to reconnect), 3 connecting, 4 connected
protocol, deviceProtocol 0 RedBox, 1 ASCII
unit 0 kg, 1 lb
status 0 OK, any other value is an error code from the scale

Permissions and errors

Changing data and sending commands to scales needs the Manager or Admin role, managing accounts and the license the Admin role (see Roles).

Errors come as JSON problem details:

Status Meaning
400 Invalid input
401 Not logged in, or wrong credentials
403 Not allowed: the user lacks the role, the license lacks the module, or the ASCII command isn't allowed
404 The scale or other resource doesn't exist
409 Conflict, e.g. a weight that was already uploaded
422 The scale answered, but the weight isn't stable or the scale reported an error
504 The scale didn't answer in time

Scales

Most routes take the scale's id, so start by listing the scales. Deleted scales are left out.

GET /api/v1/devices HTTP/1.1
Host: localhost
Authorization: Bearer <access token>
[
    {
        "id": "01997046-fe18-78bf-8895-fed35b18b997",
        "uidName": "6CCE8D",
        "customId": "103",
        "customName": null,
        "networkLocation": "192.168.0.100",
        "lastConnected": "2025-09-30T12:37:42.46625+00:00",
        "updatedAt": "2025-09-22T07:15:24.824767+00:00",
        "locationValid": true,
        "managed": true,
        "deleted": false,
        "deviceProtocol": 1
    }
]
  • id: generated by the server, and never changes.
  • uidName: the Scale unique name, read from the scale. null for a scale that was added by hand and never connected.
  • customName: a free name, shown in the web interface.
  • customId: the Custom ID, unique when set, used by the RDC service and the legacy routes.
  • locationValid: false once another scale has been found at the same networkLocation. Weight requests and commands to the scale are then answered with 404.

Requesting weights

With the scale management module, the current weight of any known scale, ASCII or RedBox, is:

GET /api/v1/devices/01997046-fe18-78bf-8895-fed35b18b997/weight HTTP/1.1
Host: localhost
Authorization: Bearer <access token>
{
    "statusBits": 112,
    "alibiText": null,
    "deviceId": "01997046-fe18-78bf-8895-fed35b18b997",
    "significantDigits": 1,
    "protocol": 1,
    "status": 0,
    "unit": 0,
    "net": 93,
    "gross": 161.6,
    "tare": 68.600006,
    "stable": true,
    "inZeroRange": false,
    "time": "2025-09-30T14:34:31.3197938+00:00"
}

A RedBox scale answers with the same fields and more: the scale interval (scaleNumber), the tilt (xAngle, yAngle), the totals (total…) and the weigh-in-motion progress. Instead of alibiText and statusBits it has alibiNr, as a RedBox alibi is always a number.

POST /api/v1/devices/{id}/save-current-weight (Manager role) reads the weight the same way and stores it as a saved weight. It fails with 422 if the weight isn't stable or the scale reports an error.

A managed scale answers over its open connection; if it isn't connected at that moment, the request fails with 504 after about 2 seconds. For a scale that isn't managed, the server connects first, waiting up to 30 seconds, and stays connected until no request has come for 30 seconds.

Scale commands

These need the Manager role and the scale management module, and fail with 422 if the scale answers with an error:

Command Route Body
Set zero POST /api/v1/devices/{id}/zero empty
Automatic tare POST /api/v1/devices/{id}/auto-tare empty
Manual tare POST /api/v1/devices/{id}/manual-tare the tare as a JSON number, e.g. 12.5

ASCII scales also take:

  • POST /api/v1/devices/{id}/ascii-command, with the command as a JSON string, e.g. "GG". Allowed are GG, GN, GT, SZ, RZ, SR, RS, GP, GA, MN, MG, AN and AG; others are refused with 403. The answer is the scale's.
  • POST /api/v1/devices/{id}/ascii-reconfigure, with an empty body, reads the scale's parameters again, as Reconfigure in the web interface does (see Device connections).

Weight history

The saved weights and weight updates are read a page at a time:

  • GET /api/v1/devices/saved-weights: saved weights, with the RDC or the data collection module
  • GET /api/v1/devices/weight-changes: weight updates, with the data collection module

Both return up to 1000 records per request. Their /export routes return CSV files of up to 100 000 records. The filters are query parameters, described at /scalar.

Uploading weights

Weights weighed elsewhere can be uploaded as saved weights, with the RDC module. Whether these routes need a logged-in user depends on the Require authentication for legacy routes setting.

  • POST /api/v1/devices/save-weight
  • POST /api/v1/devices/save-weight/redbox: the same, plus the RedBox fields inZeroRange, scaleNumber, totalNumberOfWeighings, totalNet, totalGross, xAngle and yAngle
POST /api/v1/devices/save-weight HTTP/1.1
Host: localhost
Authorization: Bearer <access token>
Content-Type: application/json

{
    "deviceId": "01997046-fe18-78bf-8895-fed35b18b997",
    "alibi": "1234",
    "significantDigits": 1,
    "status": 0,
    "unit": 0,
    "net": 93,
    "gross": 161.6,
    "tare": 68.6,
    "time": "2025-09-30T14:34:31+00:00",
    "customFields": {"Product": "A12"}
}

deviceId is the scale's id; an unknown one is answered with 404. A status other than 0 is refused with 400. Without time, the time of receipt is used.

Application settings

GET /api/v1/settings reads the application settings, POST /api/v1/settings changes them (Manager role). Send only the settings to change; the answer has all of them. Unknown settings and values out of range are refused with 422.

POST /api/v1/settings HTTP/1.1
Host: localhost
Authorization: Bearer <access token>
Content-Type: application/json

{"broadcastSelf": false}
Setting In the web interface
receiveRdc, receiveRdcPort RDC server
discoverRedBoxes Discover RedBox scales
discoverAscii Discover ASCII scales
broadcastSelf not shown, see Server discovery responder
noMessageTimeoutMs Disconnect timeout
reconnectLinearIntervalStepMs Reconnection time step
maximumReconnectIntervalMs Reconnection maximum time
enableDataCollection Data collection
collectDeviceWeights Collect weight updates
collectAppDeviceLogs Collect device-related application logs
collectDeviceLogs Collect RedBox device logs
logsRetentionTimeSeconds Application and device logs retention time
weightRetentionTimeSeconds Device weight retention time
requireAuthForScaleManagement Require authentication for legacy routes

Legacy API

These routes answer like the RAVAS Integration Server (RIS) and the RAVAS Data Collector (RDC) did, and find scales by their Custom ID:

  • POST /rest/scale/weight: upload a weight, as the RDC did over HTTP
  • GET /rest/scale/{customId}/weight/gross: the current gross weight
  • GET /rest/scale/{customId}/weight/net: the current net weight
  • GET /rest/scale/{customId}/weight-alibi-nr/gross: the gross weight with the alibi number
  • GET /rest/scale/{customId}/weight-alibi-nr/net: the net weight with the alibi number

Whether they need a logged-in user depends on the Require authentication for legacy routes setting, enabled by default.

Reading weights

The GET routes need the scale management module, and only work with ASCII scales whose location is valid; other scales are answered with 404. weight/gross and weight/net take noMotion=true, which sends the scale's no-motion command (MG or MN instead of GG or GN). The alibi routes send AG or AN.

GET /rest/scale/103/weight/gross?noMotion=true HTTP/1.1
Host: localhost
Authorization: Bearer <access token>
{
    "timestamp": "2025-09-30 15:43:56.419",
    "alias": "103",
    "type": "GROSS_WEIGHT_NO_MOTION",
    "weight": 162.4
}

timestamp is the server's local time. type depends on the route:

Route type
weight/gross GROSS_WEIGHT, or GROSS_WEIGHT_NO_MOTION with noMotion=true
weight/net NET_WEIGHT, or NET_WEIGHT_NO_MOTION with noMotion=true
weight-alibi-nr/gross GROSS_WEIGHT_NO_MOTION_WITH_ALIBI
weight-alibi-nr/net NET_WEIGHT_NO_MOTION_WITH_ALIBI

The alibi routes add the alibi number as alibiNr. When the scale answers with an error, or with something that isn't a weight, the request fails with 422; when it doesn't answer in time, with 504.

Uploading weights over HTTP

POST /rest/scale/weight needs the RDC module, and stores the weight like a record from the RDC service.

POST /rest/scale/weight HTTP/1.1
Host: localhost
Authorization: Bearer <access token>
Content-Type: application/json

{
    "scaleId": "103",
    "operatorId": "7",
    "sequenceNr": 15,
    "weightUnit": "kg",
    "grossWeight": 161.6,
    "netWeight": 93,
    "tareWeight": 68.6,
    "alibiNumber": "1234",
    "weighingDate": "2025-09-30",
    "weighingTime": "15:43",
    "totalWeighing": 0,
    "totalWeight": 0,
    "productId1": "",
    "productId2": "A12",
    "productId3": "",
    "productId4": ""
}
  • scaleId, grossWeight, netWeight and tareWeight are required.
  • scaleId is matched against the scale's Custom ID, Scale unique name or id.
  • weightUnit is kg or lb; anything but lb is stored as kg.
  • weighingDate is yyyy-MM-dd, dd-MM-yyyy or MM-dd-yyyy (a date that fits both is read as dd-MM-yyyy), and weighingTime is HH:mm, both in the server's time zone. When they can't be read, the time of receipt is used.
  • productId1 to productId4 are stored as Field #1 to Field #4, skipping empty ones: in the example, A12 is Field #2.

A successful upload is answered with:

{"statuscode": 200, "statusmessage": "OK"}

An unknown scaleId is answered with 404. A weight with the same sequenceNr and alibiNumber as one already stored for the scale is answered with 409 and not stored again.