This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
REST API
Use the 2log REST API to manage users, groups, permissions and logs programmatically.
The 2log REST API allows you to manage your instance programmatically. This is useful for automating recurring tasks, integrating 2log with other systems, or building your own tools on top of it.
All examples in this section use curl and assume your 2log server is reachable at http://your-server. The Caddy reverse proxy forwards everything under /api to the QuickHub REST backend, so all API endpoints are prefixed with /api.
How the REST API works
Under the hood, 2log is built on the QuickHub framework. QuickHub provides real-time data synchronization over WebSockets. Resources (such as the user list or a device configuration) live on the server and are identified by a resource path like labcontrol/users.
The REST API gives you plain HTTP access to these same resources. Since slashes in URLs would be interpreted as path separators, the REST API uses dots as namespace separators in the URL. The server translates dots back to slashes internally:
| URL path component |
Internal resource path |
labcontrol.users |
labcontrol/users |
labcontrol.groups |
labcontrol/groups |
labcontrol.users.groups.{uuid} |
labcontrol/users/groups/{uuid} |
Resource types
The REST API exposes three types of endpoints, each mapping to a different QuickHub resource type:
| Endpoint prefix |
Resource type |
Description |
/api/lists/{resource} |
Synchronized List |
Ordered collections of items (e.g. users, groups, permissions). Supports GET, POST, PUT, PATCH, DELETE. |
/api/objects/{resource} |
Object |
Key-value stores (e.g. the current user’s profile). Supports GET, PUT, PATCH. |
/api/services/{service}/{method} |
Service |
RPC-style function calls (e.g. addUser, getLogs). Always uses POST. |
Additionally, there are endpoints for binary data:
| Endpoint prefix |
Description |
/api/images/{resource} |
Upload, list and download images. |
/api/files |
Generic file upload and download. |
The following pages explain each area in detail.
1 - Authentication
How to log in, manage session tokens, and log out via the REST API.
Before you can use the API, you need to log in with your admin account. The login endpoint returns a session token that you include in all subsequent requests.
Log in
curl -s -X POST http://your-server/api/login \
-H "Content-Type: application/json" \
-d '{"user": "admin@fablab.org", "pass": "your-password"}'
The server responds with a token:
{"token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}
For convenience, you can store the token in a shell variable:
TOKEN=$(curl -s -X POST http://your-server/api/login \
-H "Content-Type: application/json" \
-d '{"user": "admin@fablab.org", "pass": "your-password"}' | jq -r '.token')
Providing the token
Include the token in every request. The server accepts it in three places (checked in this order):
- Authorization header (recommended):
Authorization: Bearer $TOKEN
- Session cookie (set automatically after login if you preserve cookies)
- Query parameter (fallback):
?token=$TOKEN
Already logged in
If the session cookie already contains a valid token, a second login request returns the existing token without creating a new session.
Log out
When you are done, invalidate the session:
curl -X POST http://your-server/api/logout \
-H "Authorization: Bearer $TOKEN"
Response:
Error responses
| Situation |
Status |
Message |
Missing user or pass |
400 |
Missing credentials |
| Unknown user |
401 |
User not found |
| Wrong password |
401 |
Incorrect password |
| Account not allowed |
403 |
Permission denied |
| Expired or invalid token (on other endpoints) |
403 |
Invalid token. Please log in and try again. |
Note:
Session tokens expire when the server-side session times out. If you receive a 403 error on any endpoint, simply log in again to get a fresh token.
API tokens
For automated or long-running integrations you can create API tokens instead of using session-based login. API tokens are managed through the admin UI and can be used directly as bearer tokens — no login step required.
Creating an API token
- Open the Admin panel in the 2log UI.
- Navigate to the API Tokens section.
- Provide a name, an optional description, permissions, and an optional expiration date.
- Click Create. The token string is shown once — copy and store it securely.
Using an API token
Use the token exactly like a session token. The server accepts it in the same three places:
# Authorization header (recommended)
curl http://your-server/api/some-endpoint \
-H "Authorization: Bearer <API_TOKEN>"
# Query parameter
curl "http://your-server/api/some-endpoint?token=<API_TOKEN>"
Key differences from session tokens
|
Session token |
API token |
| Created via |
POST /api/login |
Admin UI |
| Lifetime |
Expires on session timeout |
Valid until expiration date (or indefinitely if none is set) |
| Scope |
Full admin access |
Configurable permissions |
| Revocation |
POST /api/logout |
Delete in Admin UI |
Tip:
API tokens are ideal for scripts, CI/CD pipelines, or external services that need permanent access without re-authenticating. Set an expiration date to limit the token’s lifetime if needed.
2 - Managing Users
Create, read, update and delete users via the REST API.
User list (admin)
Users are stored as a synchronized list resource at labcontrol.users. You need LAB_ADMIN, IS_ADMIN, or LAB_SEE_USERS permissions to access this resource.
List all users
curl http://your-server/api/lists/labcontrol.users \
-H "Authorization: Bearer $TOKEN"
The response is a JSON array. Each item contains a data object with the user fields and a uuid at the top level:
[
{
"data": {
"name": "Max",
"surname": "Mustermann",
"mail": "max@fablab.org",
"alias": "maxm",
"role": "mem",
"course": "",
"balance": 1500,
"creditLimit": -1,
"state": 1,
"lastLogin": "2024-01-15T10:30:00.000",
"creation": "2023-06-01T08:00:00.000",
"uuid": "f47ac10b58cc4372a5670e02b2c3d479"
},
"uuid": "f47ac10b58cc4372a5670e02b2c3d479"
}
]
Field reference:
| Field |
Type |
Description |
name |
string |
First name |
surname |
string |
Last name |
mail |
string |
Email address |
alias |
string |
Display name (chosen by user) |
role |
string |
mem (member), empl (employee), ext (external/guest) |
course |
string |
Course or semester (e.g. WS2024) |
balance |
int |
Account balance in cents |
creditLimit |
int |
Maximum negative balance in cents. -1 = use global default |
state |
int |
0 = idle, 1 = active, 2 = disabled, 3 = deleted |
lastLogin |
string |
ISO 8601 timestamp of last login |
creation |
string |
ISO 8601 timestamp of account creation |
uuid |
string |
Unique user ID |
Get a single user
By UUID:
curl http://your-server/api/lists/labcontrol.users/f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN"
By index (0-based position in the list):
curl http://your-server/api/lists/labcontrol.users/0 \
-H "Authorization: Bearer $TOKEN"
Update user properties
To change individual properties of an existing user, use PATCH. Only the fields you specify are changed:
curl -X PATCH http://your-server/api/lists/labcontrol.users/f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"role": "empl"
}
}'
Replace a user record
If you want to replace the entire user object, use PUT:
curl -X PUT http://your-server/api/lists/labcontrol.users/f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"name": "Max",
"surname": "Mustermann",
"mail": "max@fablab.org",
"role": "empl",
"balance": 3000
}
}'
Note:
PUT replaces the entire item. Any fields you omit will be lost. Use PATCH if you only want to update specific properties.
Remove a user
curl -X DELETE http://your-server/api/lists/labcontrol.users/f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN"
Add a user via the lab service
To create a new user with permissions, cards, and group assignments in one step, use the lab service. This is the recommended way to add users because it handles all related data at once. You need LAB_ADMIN or LAB_MODIFY_USERS permissions.
curl -X POST http://your-server/api/services/lab/addUser \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"user": {
"name": "Lisa",
"surname": "Lasercut",
"mail": "lisa@fablab.org",
"role": "mem"
},
"permissions": {},
"card": {},
"groups": {}
}'
To create a user or update them if they already exist (useful for CSV imports), use addOrUpdateUser:
curl -X POST http://your-server/api/services/lab/addOrUpdateUser \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"user": {
"name": "Lisa",
"surname": "Lasercut",
"mail": "lisa@fablab.org",
"role": "mem"
},
"permissions": {},
"card": {},
"groups": {}
}'
Delete a user via the lab service
curl -X POST http://your-server/api/services/lab/deleteUser \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"userID": "f47ac10b58cc4372a5670e02b2c3d479"}'
Transfer money
To add or deduct credit from a user’s balance, use the transferMoney method. The value is in cents (positive = credit, negative = debit). Requires LAB_ADMIN or LAB_SERVICE permissions.
curl -X POST http://your-server/api/services/lab/transferMoney \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"value": 500,
"description": "Workshop fee refund"
}'
| Parameter |
Type |
Required |
Description |
userID |
string |
yes |
UUID of the user |
value |
int |
yes |
Amount in cents |
description |
string |
no |
Reason for the transfer |
Reset a user’s password
Sends a temporary password to the user by email:
curl -X POST http://your-server/api/services/lab/resetPassword \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"userID": "f47ac10b58cc4372a5670e02b2c3d479"}'
Look up a user by card ID
Requires LAB_ADMIN, LAB_SERVICE, or LAB_SEE_USERS permissions.
curl -X POST http://your-server/api/services/lab/getUserForCard \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"cardID": "04:A3:2B:1C:D4:E5:F6"}'
Look up a user by external reference
If users are linked to an external system via extRef, you can look them up:
curl -X POST http://your-server/api/services/lab/getUserForExternalReference \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"extRef": "EXT-12345"}'
Response:
{
"errorCode": 0,
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"userName": "Max",
"eMail": "max@fablab.org",
"balance": 1500
}
If no user is found, errorCode is -1.
Reading your own user data
To read the data of the currently logged-in user as an object resource, use labcontrol.user (singular). This does not require admin permissions.
curl http://your-server/api/objects/labcontrol.user \
-H "Authorization: Bearer $TOKEN"
The response wraps each property in a data field:
{
"name": {"data": "Max"},
"surname": {"data": "Mustermann"},
"mail": {"data": "max@fablab.org"},
"alias": {"data": "maxm"},
"role": {"data": "mem"},
"balance": {"data": 1500},
"state": {"data": 1},
"creditLimit": {"data": -1},
"course": {"data": ""},
"lastLogin": {"data": "2024-01-15T10:30:00.000"},
"creation": {"data": "2023-06-01T08:00:00.000"},
"uuid": {"data": "f47ac10b58cc4372a5670e02b2c3d479"}
}
Read a single property
curl http://your-server/api/objects/labcontrol.user/balance \
-H "Authorization: Bearer $TOKEN"
Update your own profile
Regular users can only modify their alias:
curl -X PUT http://your-server/api/objects/labcontrol.user/alias \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"data": "my-new-alias"}'
Admins (LAB_ADMIN, IS_ADMIN, or LAB_MODIFY_USERS) can additionally modify name, surname, mail, role, course, creditLimit, and extRef.
3 - Resources (Machines)
Read and manage resource controllers, their aspects (billing, QR code, permissions, etc.) via the REST API.
Resources represent the logical machines and devices managed by 2log (e.g. a laser cutter, a 3D printer, or a suction system). Each resource is a bundle that combines a controller, a resource state, and a set of aspects that add capabilities like billing, logging, or QR code authentication.
Permissions
- Admins (
IS_ADMIN): full read/write access to all resource objects and properties.
- All authenticated users: read-only access to a subset of public properties (see below).
Resource list
All resources are stored as a synchronized list at 2log.resources. This list is readable by any authenticated user, but only admins can add or remove entries.
List all resources
curl http://your-server/api/lists/2log.resources \
-H "Authorization: Bearer $TOKEN"
The response is a JSON array. Each item contains a data object with the resource properties and a uuid:
[
{
"data": {
"displayName": "Laser Cutter",
"resourceUid": "laser-cutter-01",
"systemType": "machines",
"controllerType": "genericPrimaryBundle",
"controllerState": 0,
"resourceState": 0,
"userName": "",
"userType": "",
"bundleType": "genericPrimaryBundle"
},
"uuid": "laser-cutter-01"
}
]
Public properties (visible to all authenticated users):
| Field |
Type |
Description |
displayName |
string |
Human-readable name of the resource |
controllerState |
int |
Current state of the controller |
systemType |
string |
Resource category (e.g. machines, suctions) |
userType |
string |
User-facing machine type label (free-form, e.g. Lasercutter, 3D-Drucker). Unlike systemType, this is a display label that can be chosen freely. |
resourceUid |
string |
Unique resource identifier |
Admin-only properties (additionally visible to admins):
| Field |
Type |
Description |
controllerType |
string |
Bundle type (e.g. genericPrimaryBundle, prusa3DPrinterBundle) |
resourceState |
int |
Current resource state |
userName |
string |
Name of the currently logged-in user |
bundleType |
string |
Bundle type identifier |
Get a single resource
curl http://your-server/api/lists/2log.resources/laser-cutter-01 \
-H "Authorization: Bearer $TOKEN"
Resource objects (admin only)
Each resource bundle and its sub-components are also available as individual object resources. These endpoints require IS_ADMIN permission.
Resource bundle
Returns all properties of a resource bundle as an object:
curl http://your-server/api/objects/2log.resources.laser-cutter-01 \
-H "Authorization: Bearer $TOKEN"
The response wraps each property in a data field (standard QuickHub object format):
{
"displayName": {"data": "Laser Cutter"},
"resourceUid": {"data": "laser-cutter-01"},
"systemType": {"data": "machines"},
"controllerType": {"data": "genericPrimaryBundle"},
"controllerState": {"data": 0},
"resourceState": {"data": 0},
"userName": {"data": ""},
"userType": {"data": ""},
"imageScale": {"data": 1.0},
"imageCenterX": {"data": 0},
"imageCenterY": {"data": 0}
}
Resource sub-object
Returns the resource state object:
curl http://your-server/api/objects/2log.resources.laser-cutter-01.resource \
-H "Authorization: Bearer $TOKEN"
Controller sub-object
Returns the controller state object:
curl http://your-server/api/objects/2log.resources.laser-cutter-01.controller \
-H "Authorization: Bearer $TOKEN"
Aspects
Aspects are modular capabilities attached to a resource. Each aspect is accessible as an object resource at 2log.resources.{resourceUid}.{aspectName}. All aspect endpoints require IS_ADMIN permission.
The available aspects depend on the bundle type:
| Aspect name |
genericPrimaryBundle |
prusa3DPrinterBundle |
genericSecondaryBundle |
Description |
billing |
yes |
yes |
– |
Usage-based billing |
dblogs |
yes |
yes |
– |
Database logging |
permissioncheck |
yes |
yes |
– |
Permission validation |
dot |
yes |
yes |
– |
2log Dot hardware enabler |
qrcode |
yes |
yes |
– |
QR code authentication |
secondary |
yes |
– |
– |
Companion/secondary resource control |
billing – Usage-based billing
The billing aspect controls how usage of a resource is charged.
curl http://your-server/api/objects/2log.resources.laser-cutter-01.billing \
-H "Authorization: Bearer $TOKEN"
Properties:
| Property |
Type |
Description |
payingMode |
int |
Billing mode (see table below) |
pricePerUnit |
int |
Price per unit in cents |
unitDuration |
int |
Duration of one billing unit (in seconds) |
employeesForFree |
bool |
If true, employees (empl role) are not charged |
minimumCreditBalance |
int |
Minimum credit balance required to start a session (in cents) |
Billing modes (payingMode):
| Value |
Mode |
Description |
| 0 |
DISABLED |
No billing |
| 1 |
BILLING_BY_SESSION_TIME |
Charged based on total session time |
| 2 |
BILLING_BY_PRODUCTIVE_TIME |
Charged based on productive (active) time only |
| 3 |
BILLING_PER_JOB |
Flat fee per job |
| 4 |
BILLING_WHEN_ENABLED |
Charged upon activation |
To update billing settings:
curl -X PUT http://your-server/api/objects/2log.resources.laser-cutter-01.billing/payingMode \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"data": 1}'
qrcode – QR code authentication
The QR code aspect manages how users authenticate at a machine using QR codes (scanned via the 2log mobile app).
curl http://your-server/api/objects/2log.resources.laser-cutter-01.qrcode \
-H "Authorization: Bearer $TOKEN"
Properties:
| Property |
Type |
Description |
mode |
int |
QR code mode (see table below) |
code |
string |
Current QR code value |
QR code modes (mode):
| Value |
Mode |
Description |
| 0 |
DISABLED |
QR code authentication is off |
| 1 |
STATIC |
A fixed QR code is used (defaults to the resource UID) |
| 2 |
DYNAMIC |
The server generates a new random code (12 characters) periodically |
Reading the current QR code
To read the current QR code value for a machine:
curl http://your-server/api/objects/2log.resources.laser-cutter-01.qrcode/code \
-H "Authorization: Bearer $TOKEN"
In static mode the code value stays the same (typically the resourceUid). In dynamic mode the server generates a new 12-character code automatically. To get the current code, simply read this property – the server always returns the currently valid value.
Tip
If you want to display a machine’s QR code in your own application or on an external display, poll the code property periodically in dynamic mode to stay up to date. For real-time updates, consider using the QuickHub WebSocket protocol to subscribe to the 2log/resources/{resourceUid}/qrcode object resource.
Changing the QR code mode
curl -X PUT http://your-server/api/objects/2log.resources.laser-cutter-01.qrcode/mode \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"data": 2}'
dblogs – Database logging
The logging aspect tracks resource usage and writes log entries to the database.
curl http://your-server/api/objects/2log.resources.laser-cutter-01.dblogs \
-H "Authorization: Bearer $TOKEN"
| Property |
Type |
Description |
dbref |
string |
Database reference identifier for this resource’s logs |
This aspect works together with the billing aspect: when a session ends, the billing information is automatically written as a log entry. The resulting logs can be queried via the getLogs service method.
permissioncheck – Permission validation
The permission check aspect verifies whether a user has the required permissions to use a resource. It works asynchronously – when a user attempts to authenticate, it queries the configured permission service and returns the result.
curl http://your-server/api/objects/2log.resources.laser-cutter-01.permissioncheck \
-H "Authorization: Bearer $TOKEN"
secondary – Companion resource control
The companion controller aspect links a primary resource to a secondary resource (e.g. linking a suction system to a laser cutter so that the suction starts automatically).
curl http://your-server/api/objects/2log.resources.laser-cutter-01.secondary \
-H "Authorization: Bearer $TOKEN"
Properties:
| Property |
Type |
Description |
resourceUid |
string |
UID of the linked secondary resource |
interceptWhenNotRunning |
bool |
Block the primary resource if the secondary is not running |
desiredResourceState |
int |
Target state for the secondary resource |
forceInterception |
bool |
Force interception regardless of state |
ready |
bool |
Whether the secondary resource is ready |
dot – 2log Dot hardware enabler
The Dot aspect integrates with the physical 2log Dot device (an NFC/RFID reader attached to the machine).
curl http://your-server/api/objects/2log.resources.laser-cutter-01.dot \
-H "Authorization: Bearer $TOKEN"
| Property |
Type |
Description |
dotDeviceMapping |
string |
Mapping identifier for the associated Dot hardware device |
Aspect lists
You can retrieve a flat list of all aspects of a given type across all resources. This is useful for getting an overview (e.g. all billing configurations or all QR codes at once).
The endpoint is a synchronized list at 2log.resources.{aspectType}:
# Get all billing aspects across all resources
curl http://your-server/api/lists/2log.resources.billing \
-H "Authorization: Bearer $TOKEN"
# Get all QR code aspects across all resources
curl http://your-server/api/lists/2log.resources.qrcode \
-H "Authorization: Bearer $TOKEN"
The valid aspect type values are: billing, dblogs, permissioncheck, secondary, dot, qrcode.
Creating and deleting resources
Resources are managed via the resources service. See the Service Reference for details.
Create a resource
curl -X POST http://your-server/api/services/resources/newController \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Laser Cutter",
"type": "machines",
"uid": "laser-cutter-01"
}'
Delete a resource
curl -X POST http://your-server/api/services/resources/deleteController \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"uid": "laser-cutter-01"}'
4 - Groups and Permissions
Manage groups, individual permissions and RFID cards via the REST API.
Managing groups
Groups are stored as a synchronized list resource at labcontrol.groups. You need LAB_ADMIN, IS_ADMIN, or LAB_SEE_GROUPS permissions to read groups and LAB_MODIFY_GROUPS to modify them.
List all groups
curl http://your-server/api/lists/labcontrol.groups \
-H "Authorization: Bearer $TOKEN"
[
{
"data": {
"name": "Woodworking",
"description": "Access to all woodworking machines",
"systemGroup": false,
"entities": [],
"uuid": "a1b2c3d4e5f67890abcdef1234567890"
},
"uuid": "a1b2c3d4e5f67890abcdef1234567890"
}
]
The system automatically creates three system groups for the built-in roles mem, empl, and ext. Groups with systemGroup: true should not be modified manually.
Create a new group
curl -X POST http://your-server/api/services/lab/addGroup \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Electronics Lab",
"description": "Access to soldering stations and measurement equipment"
}'
Requires LAB_ADMIN, LAB_SERVICE, or LAB_MODIFY_GROUPS.
Update group properties
curl -X PATCH http://your-server/api/lists/labcontrol.groups/a1b2c3d4e5f67890abcdef1234567890 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"description": "Access to all woodworking and CNC machines"
}
}'
Delete a group
curl -X DELETE http://your-server/api/lists/labcontrol.groups/a1b2c3d4e5f67890abcdef1234567890 \
-H "Authorization: Bearer $TOKEN"
Group entities (machines in a group)
Each group has a nested list of entities (the machines and resources it grants access to). This list is accessible at labcontrol.groups.entities.{groupUUID}:
curl http://your-server/api/lists/labcontrol.groups.entities.a1b2c3d4e5f67890abcdef1234567890 \
-H "Authorization: Bearer $TOKEN"
[
{
"data": {
"resourceID": "laser-cutter-01",
"type": 0,
"active": true,
"expires": false,
"expirationDate": "",
"creationDate": "2024-01-10T14:00:00.000"
},
"uuid": "..."
}
]
Add a machine to a group
curl -X POST http://your-server/api/lists/labcontrol.groups.entities.a1b2c3d4e5f67890abcdef1234567890 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"resourceID": "reflow-oven-01",
"active": true,
"expires": false
}
}'
Remove a machine from a group
curl -X DELETE http://your-server/api/lists/labcontrol.groups.entities.a1b2c3d4e5f67890abcdef1234567890/ENTITY_UUID \
-H "Authorization: Bearer $TOKEN"
User group assignments
Group assignments for a user are stored as a nested list at labcontrol.users.groups.{userUUID}. Each entry represents a group membership with optional expiration.
List group assignments
curl http://your-server/api/lists/labcontrol.users.groups.f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN"
[
{
"data": {
"groupID": "a1b2c3d4e5f67890abcdef1234567890",
"active": true,
"expires": false,
"expirationDate": "",
"creationDate": "2024-01-10T14:00:00.000",
"type": 0
},
"uuid": "..."
}
]
Assign a group to a user
curl -X POST http://your-server/api/lists/labcontrol.users.groups.f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"groupID": "b2c3d4e5f67890abcdef1234567890a1",
"active": true,
"expires": false
}
}'
Assign with expiration date
curl -X POST http://your-server/api/lists/labcontrol.users.groups.f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"groupID": "b2c3d4e5f67890abcdef1234567890a1",
"active": true,
"expires": true,
"expirationDate": "2025-03-31T23:59:59.000"
}
}'
Deactivate a group assignment
curl -X PATCH http://your-server/api/lists/labcontrol.users.groups.f47ac10b58cc4372a5670e02b2c3d479/MEMBERSHIP_UUID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"active": false
}
}'
Remove a group assignment
curl -X DELETE http://your-server/api/lists/labcontrol.users.groups.f47ac10b58cc4372a5670e02b2c3d479/MEMBERSHIP_UUID \
-H "Authorization: Bearer $TOKEN"
Individual permissions
Permissions for a specific user are stored at labcontrol.users.permissions.{userUUID}. Each entry grants access to a specific resource (machine).
List user permissions
curl http://your-server/api/lists/labcontrol.users.permissions.f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN"
[
{
"data": {
"resourceID": "laser-cutter-01",
"type": 0,
"active": true,
"expires": true,
"expirationDate": "2025-06-30T23:59:59.000",
"creationDate": "2024-01-10T14:00:00.000"
},
"uuid": "..."
}
]
Grant a permission
curl -X POST http://your-server/api/lists/labcontrol.users.permissions.f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"resourceID": "cnc-mill-02",
"active": true,
"expires": false
}
}'
Revoke a permission
curl -X DELETE http://your-server/api/lists/labcontrol.users.permissions.f47ac10b58cc4372a5670e02b2c3d479/PERMISSION_UUID \
-H "Authorization: Bearer $TOKEN"
Note:
When checking access, 2log first checks the system groups (based on the user’s role), then custom group assignments, and finally individual user permissions. A user has access if any of these sources grants it.
User cards
RFID cards assigned to a user are stored at labcontrol.users.cards.{userUUID}:
curl http://your-server/api/lists/labcontrol.users.cards.f47ac10b58cc4372a5670e02b2c3d479 \
-H "Authorization: Bearer $TOKEN"
Check if a user has permission
The hasPermission method checks if a user or card has access to a specific resource. It evaluates system groups, custom groups, and individual permissions in one call. Requires LAB_SERVICE permissions.
By user ID:
curl -X POST http://your-server/api/services/lab/hasPermission \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"resourceID": "laser-cutter-01"
}'
By card ID:
curl -X POST http://your-server/api/services/lab/hasPermission \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cardID": "04:A3:2B:1C:D4:E5:F6",
"resourceID": "laser-cutter-01"
}'
5 - Working with Logs
Query and create log entries via the REST API.
2log records all machine usage, transactions, and system events as log entries. You can query and filter these logs via the lab service.
Permissions
- Admins (
LAB_ADMIN, LAB_SERVICE, or LAB_SEE_LOGS): can query all logs.
- Regular users: can only see their own logs. The server automatically adds a
userID filter matching the logged-in user.
Querying logs
Use the getLogs service method with a filter object:
curl -X POST http://your-server/api/services/lab/getLogs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"match": {},
"sort": {"timestamp": -1},
"limit": 50
}
}'
The filter object
| Field |
Type |
Description |
match |
object |
MongoDB-style match criteria (see below) |
sort |
object |
Sort order, e.g. {"timestamp": -1} for newest first, {"timestamp": 1} for oldest first |
from |
string |
ISO 8601 datetime – start of time range |
to |
string |
ISO 8601 datetime – end of time range (defaults to now if omitted) |
limit |
int |
Maximum number of results (-1 or omit for unlimited) |
Match criteria
The match object filters log entries by exact field values:
| Field |
Type |
Description |
userID |
string |
Filter by user UUID |
resourceID |
string |
Filter by machine / resource ID |
logType |
int |
Filter by event type (see table below) |
Log event types
| Value |
Name |
Description |
| 0 |
BILL |
Completed session with billing |
| 1 |
SWITCH_ON |
Something was switched on |
| 2 |
SWITCH_OFF |
Something was switched off |
| 3 |
START |
A machine was started |
| 4 |
STOP |
A machine was stopped |
| 5 |
EVENT |
General event |
| 6 |
LOGIN |
User logged in |
| 7 |
LOGOUT |
User logged out |
| 8 |
WARNING |
Warning |
| 9 |
ERROR |
Error |
| 10 |
OPEN |
Something was opened (cabinet, lid) |
| 11 |
CLOSED |
Something was closed |
| 12 |
TRANSFER |
Balance transfer (top-up or deduction) |
| 13 |
JOB |
Job with duration |
| 14 |
OFFLINE |
Device went offline |
Log entry format
Each log entry in the response contains these fields:
{
"logID": "60a7b2c3d4e5f67890abcdef",
"resourceID": "laser-cutter-01",
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"userName": "Max Mustermann",
"email": "max@fablab.org",
"cardID": "04:A3:2B:1C:D4:E5:F6",
"logType": 0,
"units": 120,
"price": 600,
"description": "Laser cutting session",
"timestamp": "2024-01-15T14:30:00.000",
"startTime": "2024-01-15T14:00:00.000",
"endTime": "2024-01-15T14:30:00.000",
"executive": "admin-user-uuid",
"sessionID": "session-uuid",
"extType": "",
"extRef": ""
}
| Field |
Type |
Description |
logID |
string |
Unique log entry ID |
resourceID |
string |
Machine or resource that generated this log |
userID |
string |
UUID of the user involved |
userName |
string |
Name of the user at the time of the event |
email |
string |
Current email of the user (joined from user data) |
cardID |
string |
RFID card used (if applicable) |
logType |
int |
Event type (see table above) |
units |
int |
Usage units (interpretation depends on machine) |
price |
int |
Cost in cents |
description |
string |
Human-readable description |
timestamp |
string |
When the log was created |
startTime |
string |
Start of the session/event |
endTime |
string |
End of the session/event |
executive |
string |
UUID of the user/service that triggered the event |
sessionID |
string |
Session identifier (groups related log entries) |
extType |
string |
External system type (e.g. 2log-paydesk) |
extRef |
string |
External reference ID |
Examples
Logs for a specific user
Get the 20 most recent logs for a user:
curl -X POST http://your-server/api/services/lab/getLogs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"match": {
"userID": "f47ac10b58cc4372a5670e02b2c3d479"
},
"sort": {"timestamp": -1},
"limit": 20
}
}'
Logs for a specific machine
Get all logs for the laser cutter:
curl -X POST http://your-server/api/services/lab/getLogs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"match": {
"resourceID": "laser-cutter-01"
},
"sort": {"timestamp": -1}
}
}'
Logs in a time range
Get all billing events from January 2024:
curl -X POST http://your-server/api/services/lab/getLogs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"match": {
"logType": 0
},
"from": "2024-01-01T00:00:00.000",
"to": "2024-02-01T00:00:00.000",
"sort": {"timestamp": -1}
}
}'
Combining filters
Get all laser cutter sessions for a specific user in Q1 2024:
curl -X POST http://your-server/api/services/lab/getLogs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"match": {
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"resourceID": "laser-cutter-01"
},
"from": "2024-01-01T00:00:00.000",
"to": "2024-04-01T00:00:00.000",
"sort": {"timestamp": -1}
}
}'
Adding a log entry
To create a log entry programmatically, use the addLog method. Requires LAB_ADMIN or LAB_SERVICE permissions.
curl -X POST http://your-server/api/services/lab/addLog \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"resourceID": "laser-cutter-01",
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"logType": 5,
"description": "Manual maintenance note",
"units": 0,
"price": 0
}'
The fields in the request body correspond to the log entry fields described above. The timestamp is set automatically to the current time.
6 - Service Reference
Complete reference for all available service methods.
Services provide RPC-style function calls. All service calls use POST:
POST /api/services/{serviceName}/{methodName}
The request body is a JSON object with the method parameters. The server waits up to 30 seconds for a response before returning a 504 timeout.
Discovering services
List all registered services and their methods:
curl http://your-server/api/services \
-H "Authorization: Bearer $TOKEN"
Get info about a specific service:
curl http://your-server/api/services/lab \
-H "Authorization: Bearer $TOKEN"
{
"name": "lab",
"methods": ["addLog", "getLogs", "resetPassword", "addUser", "deleteUser", ...]
}
lab service
The main service for user management, access control and logging.
| Method |
Description |
Permissions |
addUser |
Create a new user |
LAB_ADMIN, LAB_MODIFY_USERS |
addOrUpdateUser |
Create or update a user |
LAB_ADMIN, LAB_MODIFY_USERS |
deleteUser |
Delete a user |
LAB_ADMIN, LAB_MODIFY_USERS |
resetPassword |
Send a temporary password by email |
(any valid token) |
transferMoney |
Add/deduct credit |
LAB_ADMIN, LAB_SERVICE |
getUserForCard |
Look up user by RFID card |
LAB_ADMIN, LAB_SERVICE, LAB_SEE_USERS |
getUserForExternalReference |
Look up user by external ID |
LAB_ADMIN, LAB_SERVICE, LAB_SEE_USERS |
hasPermission |
Check access to a resource |
LAB_SERVICE |
getLogs |
Query log entries |
LAB_ADMIN, LAB_SERVICE, LAB_SEE_LOGS (or own logs) |
addLog |
Create a log entry |
LAB_ADMIN, LAB_SERVICE |
addGroup |
Create a new group |
LAB_ADMIN, LAB_SERVICE, LAB_MODIFY_GROUPS |
getAccumulatedCostsForUser |
Get total costs for a user in a date range |
LAB_ADMIN, LAB_SERVICE, LAB_SEE_LOGS (or own costs) |
addSystemUser |
Create a system/admin user |
LAB_ADMIN, IS_ADMIN, LAB_SERVICE |
changeUserLevel |
Change a system user’s role |
LAB_ADMIN, IS_ADMIN, LAB_SERVICE |
See Users, Groups & Permissions, and Logs for detailed examples.
getAccumulatedCostsForUser
Returns the total accumulated costs for a user within a given date range. The server aggregates all log entries matching the user and time range, and returns the summed totalCost.
Unprivileged users (without LAB_ADMIN, LAB_SERVICE, or LAB_SEE_LOGS permission) can only query their own costs — the userID parameter is ignored and automatically set to the authenticated user’s ID.
curl -X POST http://your-server/api/services/lab/getAccumulatedCostsForUser \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"from": "2024-01-01T00:00:00.000",
"to": "2024-02-01T00:00:00.000"
}'
| Parameter |
Type |
Required |
Description |
userID |
string |
yes |
UUID of the user to query costs for |
from |
datetime |
yes |
Start of the date range (ISO 8601) |
to |
datetime |
no |
End of the date range (ISO 8601). Defaults to current time if omitted |
Response:
[
{
"totalCost": 4250
}
]
| Field |
Type |
Description |
totalCost |
int |
Sum of all price values (in cents) from matching log entries |
addSystemUser
Creates or updates a system-level user (for admin panel access).
curl -X POST http://your-server/api/services/lab/addSystemUser \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"name": "Admin User",
"eMail": "admin@fablab.org",
"level": "admin"
}'
| Parameter |
Type |
Required |
Description |
userID |
string |
yes |
UUID of the lab user to promote |
name |
string |
yes |
Display name |
eMail |
string |
yes |
Email address |
level |
string |
yes |
Role level (e.g. admin, viewer) |
changeUserLevel
Changes the system role of an existing system user.
curl -X POST http://your-server/api/services/lab/changeUserLevel \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"level": "viewer"
}'
payment service
Handles point-of-sale billing operations (used by the 2log PayDesk).
| Method |
Description |
Permissions |
preparebill |
Validate a shopping cart and calculate totals |
(any valid token) |
bill |
Execute a bill (deduct balance, create logs) |
IS_ADMIN, LAB_ADMIN, LAB_SEND_BILLS |
getsales |
Get product sales history for a date range |
(any valid token) |
preparebill
Validates a shopping cart, resolves the user, and calculates totals per accounting code. Does not deduct money.
curl -X POST http://your-server/api/services/payment/preparebill \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cardID": "04:A3:2B:1C:D4:E5:F6",
"bill": [
{"name": "Laser time 30min", "price": 500, "accountingCode": "laser"},
{"name": "Material fee", "price": 200, "accountingCode": "material"}
],
"total": 700
}'
| Parameter |
Type |
Required |
Description |
cardID |
string |
one of cardID/userID |
RFID card to identify the user |
userID |
string |
one of cardID/userID |
User UUID (alternative to cardID) |
bill |
array |
yes |
List of items, each with name, price (cents), accountingCode |
total |
int |
yes |
Expected total in cents |
Response:
{
"errcode": 0,
"errstring": "",
"userID": "f47ac10b...",
"name": "Max",
"surname": "Mustermann",
"eMail": "max@fablab.org",
"total": 700,
"discountTotal": 700,
"bills": [
{
"accountingCode": "laser",
"totalBrutto": 500,
"totalNetto": 500,
"discountPercent": 0,
"items": [...]
}
]
}
bill
Executes the billing: deducts the amount from the user’s balance, creates log entries, and sends a payment confirmation email.
curl -X POST http://your-server/api/services/payment/bill \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cardID": "04:A3:2B:1C:D4:E5:F6",
"userID": "f47ac10b58cc4372a5670e02b2c3d479",
"bills": [
{
"accountingCode": "laser",
"totalNetto": 500,
"items": [
{"uuid": "item-1", "name": "Laser time 30min", "price": 500, "newprice": 500, "flat": false}
]
}
],
"total": 500,
"discountTotal": 500
}'
| Parameter |
Type |
Required |
Description |
cardID |
string |
one of cardID/userID |
RFID card ID |
userID |
string |
one of cardID/userID |
User UUID |
bills |
array |
yes |
Array of bill groups (from preparebill response) |
total |
int |
yes |
Original total in cents |
discountTotal |
int |
yes |
Final total after discounts in cents |
cartID |
string |
no |
Unique cart ID (prevents double billing) |
Error codes:
| errcode |
Description |
| 0 |
Success |
| -2 |
Unknown user |
| -3 |
Invalid parameters |
| -4 |
Cart already paid (duplicate cartID) |
| -5 |
Credit limit exceeded |
getsales
Returns product sales history for a given date range.
curl -X POST http://your-server/api/services/payment/getsales \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from": "2024-01-01T00:00:00.000",
"to": "2024-02-01T00:00:00.000"
}'
devices service
Manages IoT device mappings and firmware updates.
| Method |
Description |
hookWithShortID |
Map a device to a resource by its short ID |
unhookWithShortID |
Remove a device mapping by its short ID |
getDeviceTypeWithShortID |
Get the device type for a short ID |
getDeviceTypeWithID |
Get the device type for a UUID |
checkForUpdates |
Check for available firmware updates |
startUpdate |
Start a firmware update |
hookWithShortID
Maps a physical device (identified by its short ID printed on the hardware) to a logical resource name.
curl -X POST http://your-server/api/services/devices/hookWithShortID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"shortID": "AB12",
"mapping": "laser-cutter-01",
"force": false,
"expectedType": "switch"
}'
| Parameter |
Type |
Required |
Description |
shortID |
string |
yes |
Short ID of the device |
mapping |
string |
yes |
Logical resource name to assign |
force |
bool |
no |
Overwrite existing mapping (default: false) |
expectedType |
string |
no |
Expected device type; fails if mismatch |
Error codes in response:
| errorcode |
Description |
| 0 |
Success |
| -11 |
Device not found |
| -12 |
Wrong device type |
unhookWithShortID
curl -X POST http://your-server/api/services/devices/unhookWithShortID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"shortID": "AB12"}'
getDeviceTypeWithShortID
curl -X POST http://your-server/api/services/devices/getDeviceTypeWithShortID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"shortID": "AB12"}'
Response:
{"deviceType": "switch", "errorcode": 0}
getDeviceTypeWithID
curl -X POST http://your-server/api/services/devices/getDeviceTypeWithID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"uuid": "device-uuid-here"}'
checkForUpdates
curl -X POST http://your-server/api/services/devices/checkForUpdates \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"mapping": "laser-cutter-01"}'
startUpdate
curl -X POST http://your-server/api/services/devices/startUpdate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mapping": "laser-cutter-01",
"url": "https://firmware.example.com/update.bin"
}'
machineControl service
Handles the association between 2log controllers (Switch, Dot) and machines.
| Method |
Description |
hookSwitch |
Assign a 2log Switch to a machine |
hookDot |
Assign a 2log Dot to a machine |
hookSwitch
Links a 2log Switch device to a machine controller.
curl -X POST http://your-server/api/services/machineControl/hookSwitch \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"shortID": "AB12",
"deviceID": "machine-controller-id",
"force": false
}'
| Parameter |
Type |
Required |
Description |
shortID |
string |
yes |
Short ID of the Switch device |
deviceID |
string |
yes |
ID of the machine controller to attach to |
force |
bool |
no |
Overwrite existing assignment |
hookDot
Links a 2log Dot device to a machine controller.
curl -X POST http://your-server/api/services/machineControl/hookDot \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"shortID": "CD34",
"deviceID": "machine-controller-id",
"force": false
}'
resources service
Manages 2log resource controllers (the logical representations of machines).
| Method |
Description |
newController |
Create a new resource controller |
deleteController |
Delete a resource controller |
newController
curl -X POST http://your-server/api/services/resources/newController \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Laser Cutter",
"type": "machines",
"uid": "laser-cutter-01"
}'
| Parameter |
Type |
Required |
Description |
name |
string |
yes |
Display name |
type |
string |
yes |
Resource type (e.g. machines, suctions) |
uid |
string |
yes |
Unique device/resource ID |
Response:
{"success": true, "data": {...}}
deleteController
curl -X POST http://your-server/api/services/resources/deleteController \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"uid": "laser-cutter-01"}'
codeAuthenticator service
Handles QR-code based authentication (used by the 2log mobile app).
| Method |
Description |
authenticate |
Authenticate a session via a temporary code |
authenticate
The 2log app displays a QR code containing a temporary code. When scanned (e.g. by a terminal), this method is called to authenticate the session.
curl -X POST http://your-server/api/services/codeAuthenticator/authenticate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"code": "https://2log.app/AB3F7K9X"}'
| Parameter |
Type |
Required |
Description |
code |
string |
yes |
The temporary authentication code |
Response:
errcode -1 means the code is unknown or expired.
7 - Error Handling
HTTP status codes and error response format.
All error responses follow a consistent JSON format:
{"error": true, "code": 403, "message": "Invalid token. Please log in and try again."}
HTTP status codes
| Code |
Meaning |
| 200 |
Success |
| 201 |
Created (file upload) |
| 400 |
Invalid request data, missing parameters, or malformed JSON |
| 401 |
Authentication failed (wrong password, unknown user) |
| 403 |
Invalid token or insufficient permissions |
| 404 |
Resource, item, property, service, or method not found |
| 405 |
HTTP method not supported for this endpoint |
| 500 |
Internal server error (e.g. resource could not be accessed) |
| 502 |
Service call returned invalid data |
| 504 |
Service call timed out (30 second limit) |
Common error messages
| Message |
Cause |
Missing credentials |
Login request without user or pass |
Invalid token. Please log in and try again. |
Token expired or invalid |
Permission denied |
Authenticated but lacking the required permission |
Missing 'data' field |
POST/PUT/PATCH without data in body |
Missing item identifier |
PUT/PATCH/DELETE on a list without specifying an ID |
Item not found / Property not found |
ID or property does not exist |
Service not found |
Unknown service name |
Unknown method |
Service exists but method name is wrong |
Service call timed out |
Service did not respond within 30 seconds |
Tip:
If you receive a 403 error, check that your token is still valid. Tokens expire when the server-side session times out. Simply log in again to get a fresh token.