Identifiers — Key vs. ID

How to reference devices, variables, and other entities by id or by label.

This page explains the difference between identifying entities by a key versus an id.

📘

The ~ convention below applies to the Platform API (/api/v2.0) only. The Data API (/api/v1.6) addresses entities differently — see Identifiers in the Data API further down.

Key as a path parameter

A key identifies an entity such as a Device, Event or Device Group. Key parameters in a path are written as <entity_key> — for example, <device_key>.

A key lets you identify an entity by either its id or its label. To identify by label, prefix it with ~. Ids are 24-character hex strings, and the ~ prefix is required — a bare label with no prefix returns 404.

# GET a specific device — URL definition
https://api.surfact.com/api/v2.0/devices/<device_key>/

# Identify using the device id
https://api.surfact.com/api/v2.0/devices/6a7c777c1da2efbc51f62ba9/

# Identify using the device label (note the ~ prefix)
https://api.surfact.com/api/v2.0/devices/~reefer-trailer-21/

Variables are the exception

Device, Event, Device Group and Organization labels are unique across your account, so those entities accept ~label wherever they appear in a path.

Variable labels are unique only within a device — every reefer in a frozen fleet carries a variable labelled temperature — so a variable can be addressed by label only when it is nested under its device:

# ✅ Works — the device scopes the label
curl -X GET 'https://api.surfact.com/api/v2.0/devices/~reefer-trailer-21/variables/~temperature/?fields=id,label' \
  -H 'X-Auth-Token: your_token_here'

# ❌ Rejected — /variables/ is account-wide, so a bare label is ambiguous
curl -X GET 'https://api.surfact.com/api/v2.0/variables/~temperature/' \
  -H 'X-Auth-Token: your_token_here'

The second call returns HTTP 400:

{
  "code": 400001,
  "message": "Validation Error.",
  "detail": ["Label key is not allowed: `~temperature`"]
}

The top-level /variables/<variable_id>/ path therefore takes the object id only. Look the id up through the device first: GET /devices/~reefer-trailer-21/variables/?fields=id,label.

🚧

The API Reference still names these path parameters {device_id} and {variable_id} even where they accept ~label. Don't infer acceptance from the parameter name on those pages — read the parameter description, which states which forms are allowed.

ID as a path parameter

An id identifies an entity only by its id (never by label). Id parameters in a path are written as <entity_id> — for example, <log_id>. Most ids are 24-character hex strings; event log ids are integers.

# GET a specific event log — URL definition
https://api.surfact.com/api/v2.0/events/<event_key>/logs/<log_id>/

# Identify the log by its id
https://api.surfact.com/api/v2.0/events/<event_key>/logs/818171/

Prefixing an id parameter with ~ is rejected with HTTP 400:

{
  "code": 400001,
  "message": "Validation Error.",
  "detail": ["Label key is not allowed: `~818171`"]
}

Identifiers in the Data API (v1.6)

The ~ prefix is a Platform API convention. In the Data API (/api/v1.6) it always returns 404, and the rules are effectively inverted:

PathWhat it takesExample
/devices/<device_label>/<variable_label>/values/The bare labels, with no prefix — an object id returns 404/api/v1.6/devices/reefer-trailer-21/temperature/values/
/variables/<variable_id>/values/The object id only — a bare label returns 404/api/v1.6/variables/6a7c777d97bcd84c851942c9/values/

There is also no /devices/ collection endpoint in v1.6 — list your fleet with the Platform API, then read its values here.

🚧

Never use the ~ prefix in a v1.6 path. GET /api/v1.6/devices/~reefer-trailer-21/temperature/values/ returns {"code": 404001, "message": "The request was not found."}.

Key as a body parameter

Some POST requests take an entity parameter in the body. A good example is Create a device group, which accepts an organization key.

When creating a device group, the organization parameter can be sent in any of these forms:

curl -X POST 'https://api.surfact.com/api/v2.0/device_groups/' \
  -H 'Content-Type: application/json' \
  -H 'X-Auth-Token: your_token_here' \
  -d '{
    "label": "nordic-frozen-fleet",
    "name": "Nordic Frozen Fleet",
    "organization": "6601f0a1b458490306099001"
  }'
FormExample
By id"organization": "6601f0a1b458490306099001"
By label (with ~)"organization": "~nordic-cold-chain"
As an object"organization": {"id": "6601f0a1b458490306099001"}
❗️

A key in a request body is not the same as a key in a query filter. The ?organization= filter on /events/ and /device_groups/ accepts only the internal numeric organization id (for example ?organization=30); passing the object id or a ~label there returns 400 "Select a valid choice. That choice is not one of the available choices.".

📘

Events do not expose an organization of their own — the field is absent from every event returned by GET /events/ and GET /events/{event_key}/, so don't expect to read one back. Device Groups and Devices do return the owning organization as a nested object.

ID as a body parameter

It's simple: all entity parameters in a request body are keys. That means they can be identified by their id, by their label (with the ~ prefix), or as an object.


Did this page help you?