User API uses a JSON based API to allow users to register, secure and edit their information as well as claim and share sensors, retrieve sensor data and alter their subscription details. Fields marked with * are mandatory, and omitting them results in backend returning HTTP 400 - Bad Request.
For example, if the body parameter in the sections below refers to an email field, the corresponding JSON payload would look like this:
Copy {
"email": "<YOUR VALUE>"
}
All authenticated queries are ratelimited to 8 * MAX_SENSORS_OWNED + 0.1 * MAX_HISTORY_DAYS per minute. For example user with Basic plan has maximum of 90 days of history on 25 sensors and can make up to 209 queries per minute. The throtteled response has response code of 429 and payload of:
Copy {
result: 'error',
error: 'Too many requests.',
code: 'ER_THROTTLED'
}
Register User or Reset Token
POST
https://network.ruuvi.com/register
Registers a new user or resets an existing user's password if the user already exists.
Request Body
200 On a successful call, you will receive a 200 OK. An e-mail will be sent to the given address to confirm the registration.
In case of a reset, the token will be provided as a response to the call. 500 If a something goes wrong with the request itself, you might receive an Unknown error. Please contact your system administrator if this occurs. 429: Too Many Requests Over 10 calls in an hour
Copy {
"result": "success",
"data": {
"email": "matti@meikalainen.com"
}
}
Copy {
"result": "error",
"error": "Unknown error occurred.",
"code": "ER_INTERNAL"
}
Copy {
result: 'error',
error: 'Too many requests.',
code: 'ER_THROTTLED'
}
Verify Account
GET
https://network.ruuvi.com/verify
Verifies the given e-mail address and finalizes creating the account and creating a Ruuvi Network subscription. Notice that the token for this end-point is delivered via e-mail by the Register User endpoint.
Query Parameters
200 On a successful response, you will receive the confirmation on the e-mail address an an access token to be used with other end-points.
Store it well as the only way to retrieve it is to go through the reset flow again. 403 Forbidden if user is not allowed to access resource. 500 (Actual code 493): You will receive an error if the token is invalid.
Copy {
"result": "success",
"data": {
"email": "<E-MAIL ADDRESS OF THE USER>",
"accessToken": "<NEW ACCESS TOKEN>",
"newUser": <true|false>
}
}
Copy {
"result": "error",
"error": "Forbidden.",
"code": "ER_FORBIDDEN"
}
Copy {
"result": "error",
"error": "Code used or expired.",
"code": "ER_TOKEN_EXPIRED"
}
Request deletion of account
POST
https://network.ruuvi.com/request-delete
This operation requests complete removal of user account from Ruuvi Cloud. After a successful call to this endpoint, user gets a verification email with a link to confirm deletion of account.
Request Body
200: OK Verification email has been sent 400: Bad Request Missing authorization or email parameter 403: Forbidden Returned if Authorization token does not match email
Copy {
"result" : "success" ,
"data" : {
"email" : "otso+test3@ruuvi.com"
}
}
Copy {
"result" : "error" ,
"error" : "<SPECIFIC ERROR>" ,
"code" : "ER_<ERROR>"
}
Copy {
"result" : "error" ,
"error" : "Unauthorized request." ,
"code" : "ER_UNAUTHORIZED"
}
Verify account deletion operation
GET
https://network.ruuvi.com/verify-delete
Following actions will be done:
User sensors will be unshared
Sensors shared to user will be removed.
Data of user sensors will be deleted . (TODO)
User account data, including sensor claims and settings, will be deleted.
Account deletion is a permament action which cannot be undone
Path Parameters
200: OK Account deletion was started 403: Forbidden Invalid or missing authorization token
Copy {
{
"result" : "error" ,
"error" : "Unauthorized request." ,
"code" : "ER_UNAUTHORIZED"
}
Claim a sensor for user
POST
https://netowrk.ruuvi.com/claim
After this call, given sensor is claimed under authenticated user account
Request Body
200: OK Sensor was claimed successfully 400: Bad Request Request had malformed JSON or was missing a required parameter. Alternatively user account has reached subscription limit 401: Unauthorized Request had no authorization token 409: Conflict Sensor is claimed by another account 500: Internal Server Error Unexpected error occurred in handler
Copy {
"result" : "success" ,
"data" : {
"sensor" : "C5:2A:E7:4D:CE:7F"
}
}
Copy {
'code' : 'ER_SENSOR_ALREADY_CLAIMED' ;
}
Unclaim a sensor from your user
POST
https://network.ruuvi.com/unclaim
Unclaims a sensor from your user, revoking your own access to it and making it claimable by other users.
Request Body
200
Copy {
"result": "success"
}
Share a sensor
POST
https://network.ruuvi.com/share
You can share your sensor data with other users via share end-point. In addition to sensor you want to share, you must also include the e-mail address of the recipient. This will grant them access to the data via the get end-point.
Furthermore, it will also send the target user a notification e-mail about the new share. If the target user does not exist yet, an invitation to create an account will be sent to them and they will gain access upon sign up.
Request Body
200 On success, you will get the shared sensor id returned back to you. 400 If the sharing failed due to malformed data, such as missing arguments. 401 If an invalid or expired access token is provided, you will receive 401 Unauthorized. 409 You will receive a conflict if the sensor is already shared to the user. 500 If something went wrong with the request, you will receive a 500 internal server error. Please contact your System Administrator.
Copy {
"result": "success",
"data": {
"sensor": "<YOUR SENSOR ID>"
"invited": true
}
}
Copy {
"result": "error",
"error": "Unauthorized request.",
"code": "ER_UNAUTHORIZED"
}
Copy {
"result": "error",
"error": "Sensor already shared to user.",
"code": "ER_SENSOR_ALREADY_SHARED"
}
Copy {
"result": "error",
"error": "Unknown error occurred.",
"code": "ER_INTERNAL"
}
Unshare a sensor
POST
https://network.ruuvi.com/unshare
Unshares (i.e. revokes access to) the sensor from a target user. This can also currently be used to remove sensors shared with your user.
Request Body
200 400 Returned with Invalid or missing fields. 403 Returned when user 404 Returned if shared sensor or user is not found.
Copy {
"result": "success"
}
Copy {
"result": "error",
"error": "<SPECIFIC ERROR>",
"code": "ER_<ERROR>"
}
Copy {
"result": "error",
"error": "Unauthorized request.",
"code": "ER_UNAUTHORIZED"
}
Copy {
"result": "error",
"error": "User not found.",
"code": "ER_USER_NOT_FOUND"
}
Get your sensors
GET
https://network.ruuvi.com/sensors
Fetches a list of sensors you have access to including who those are shared to. This end-point deprecates the old shared end-point.
Query Parameters
200 401
Copy {
"result": "success",
"data": {
"sensors": [
{
"sensor": "<SENSOR ID>",
"name": "<SENSOR NAME>",
"picture": "<SENSOR PICTURE URL>",
"public": <TRUE|FALSE>,
"canShare": <TRUE|FALSE>,
"offsetHumidity": <DOUBLE>,
"offsetTemperature": <DOUBLE>,
"offsetPressure": <DOUBLE>,
"measurements": [
{
"gwmac": "<SOURCE GATEWAY MAC>",
"coordinates": "<COORDINATES / N/A>",
"rssi": <RSSI>,
"timestamp": <UNIX TIMESTAMP OF MEASUREMENT>,
"data": "<HEX ENCODED SENSOR DATA>"
}
]
"sharedTo": [
"<EMAIL OF TARGET USER 1>",
...
]
},
...
],
"sharedToMe": [
{
"sensor": "<SENSOR ID>",
"name": "<SENSOR NAME>",
"picture": "<SENSOR PICTURE URL>",
"public": <TRUE|FALSE>,
"canShare": <TRUE|FALSE>,
"offsetHumidity": <DOUBLE>,
"offsetTemperature": <DOUBLE>,
"offsetPressure": <DOUBLE>,
"measurements": [
{
"gwmac": "<SOURCE GATEWAY MAC>",
"coordinates": "<COORDINATES / N/A>",
"rssi": <RSSI>,
"timestamp": <UNIX TIMESTAMP OF MEASUREMENT>,
"data": "<HEX ENCODED SENSOR DATA>"
}
]
},
]
}
}
Copy {
"result": "error",
"error": "Unauthorized.",
"code": "ER_UNAUTHORIZED"
}
Get your sensors with calibration data, latest measurement, and alerts settings
GET
https://network.ruuvi.com/sensors-dense
Fetches the list of claimed and shared sensors with calibration data, sensor last measurement, subscription type and alert settings. By default the endpoint returns only the claimed sensors with calibration data. Optional arguments must be passed to get shared sensors, last measurement, and alert settings.
Query Parameters
401: Unauthorized 200: OK
Copy {
"result" : "error" ,
"error" : "Unauthorized." ,
"code" : "ER_UNAUTHORIZED"
}
Copy {
"result" : "success" ,
"data" : {
"sensors" : [
{
"sensor" : "<SENSOR ID>" ,
"name" : "<SENSOR NAME>" ,
"picture" : "<SENSOR PICTURE URL>" ,
"public" : < TRUE | FALSE > ,
"canShare" : < TRUE | FALSE > ,
"offsetHumidity" : < DOUBLE >,
"offsetTemperature": < DOUBLE >,
"offsetPressure": < DOUBLE >,
"measurements": [
{
"gwmac" : "<SOURCE GATEWAY MAC>" ,
"coordinates" : "<COORDINATES / N/A>" ,
"rssi" : < RSSI >,
"timestamp": < UNIX TIMESTAMP OF MEASUREMENT >,
"data": "< HEX ENCODED SENSOR DATA >"
}
],
"sharedTo": [
"< EMAIL OF TARGET USER 1>",
...
],
"alerts" : [
{
userId: < userId >,
sensorId: < sensorMAC >,
type: <humidity|pressure|temperature>,
min: < lower limit >,
max: < higher limit >,
enabled: <true|false>,
offsetHumidity: < double >,
offsetTemperature: < double >,
offsetPressure: < double >,
triggered: <true|false>,
triggeredAt: < timestamp >
},
...
]
},
owner: <EMAIL, masked if public sensor>
subscription: {
"maxHistoryDays" : < INT >,
"maxResolutionMinutes": < INT >,
"emailAlertAllowed": <true|false>,
"pushAlertAllowed": <true|false>,
"subscriptionName": < STRING >
}
...
]
}
}
Get User Info
GET
https://network.ruuvi.com/user
Fetches user information for an authenticated user.
200 User information successfully retrieved. 401 Unauthorized request.
Copy {
"result": "error",
"error": "Unauthorized request.",
"code": "ER_UNAUTHORIZED"
}
Get Sensor data
GET
https://network.ruuvi.com/get
Returns the data points for the requested sensor. Notice that for implementing pagination, you can use since and until parameters with custom limit to segment your results as they are always returned in either ascending or descending order by timestamp.
Data can be fetched in dense, sparse and mixed mode. Dense mode returns highest data density possible, but has a limited time range before data is pruned to save storage space. Sparse mode has downsampled data, but time range is not limited. Mixed mode returns all the dense data available and rest of the time range is filled with sparse data
Query Parameters
200 Returns the most recent data points for the requested tag based on configuration and parameters.f 400 401 In case of an invalid or expired authentication token, you will receive a unauthorized response. 403 If you have not claimed or been shared the target sensor, you will receive a Forbidden.
Copy {
"result": "success",
"data": {
"sensor": "<SENSOR ID>",
"total": <TOTAL MEASUREMENTS RETURNED>,
"name": "<SENSOR NAME>",
"picture": "<SENSOR PICTURE URL OR FILENAME>",
"measurements": [
{
"gwmac": "<SOURCE GATEWAY MAC>",
"coordinates": "<COORDINATES / N/A>",
"rssi": <RSSI>,
"timestamp": <UNIZ TIMESTAMP OF MEASUREMENT>,
"data": "<HEX ENCODED SENSOR DATA>"
},
...
]
}
}
Copy {
"result": "error",
"error": "Invalid <SPECIFIC ERROR>",
"code": "ER_INVALID_<SPECIFIC>"
}
Copy {
"result": "error",
"error": "Unauthorized request.",
"code": "ER_UNAUTHORIZED"
}
Copy {
"result": "error",
"error": "Forbidden.",
"code": "ER_FORBIDDEN"
}
Update Sensor metadata
POST
https://network.ruuvi.com/update
Updates sensor metadata.
Request Body
200 Only returns the fields that had an update targeted to them. 403 404 500 409: Conflict Cloud has fresher data than timestamp
Copy {
"result": "success",
"data": {
"sensor": "<SENSOR ID>",
"name": "<GIVEN NAME>",
"public": "<GIVEN PUBLIC VALUE>"
}
}
Copy {
"result": "error",
"error": "Forbidden.",
"code": "ER_FORBIDDEN"
}
Copy {
"result": "error",
"error": "Sensor not claimed or found. Data not updated.",
"code": "ER_SENSOR_NOT_FOUND"
}
Copy {
"result": "error",
"error": "Unknown error occurred.",
"code": "ER_INTERNAL"
}
Copy {
"result": "error",
"error": "Newer setting already exists",
"code": "ER_CONFLICT",
"sub_code": "ER_OLD_ENTRY"
}
Upload Sensor image (part 1)
POST
https://network.ruuvi.com/upload
Retrieves a signed upload URL to a bucket. This makes the back-end ready for the image upload to happen.
Request Body
200 403
Copy {
"result": "success",
"data": {
"uploadURL": "<SIGNED UPLOAD URL>"
}
}
Copy {
"result": "error",
"error": "Forbidden.",
"code": "ER_FORBIDDEN"
}
Upload the actual image
PUT
<URL FROM part 1>
Create a PUT request to the URL produced by /upload end-point with the data payload to complete the upload.
Request Body
Get User Settings
GET
https://network.ruuvi.com/settings
Gets the full list of existing user settings.
200
Copy {
"status": "success",
"data": {
"settings": {
"<SETTING 1>": "<SETTING 1 VALUE>",
...
}
}
}
Update user setting
POST
https://network.ruuvi.com/settings
Sets a single user setting (currently).
Request Body
200 409: Conflict Cloud has fresher data than timestamp
Copy {
"status": "success",
"data": {
"action": "<added|updated>"
}
}
Copy {
"result": "error",
"error": "Newer setting already exists",
"code": "ER_CONFLICT",
"sub_code": "ER_OLD_ENTRY"
}
Create and update Alerts
POST
https://network.ruuvi.com/alerts
Sets an alert on a sensor for a given metric. The alert condition is tested against the absolute value received from the sensors in conjunction with the use set offsets for that particular sensor.
Request Body
200 400: Bad Request 409: Conflict Cloud has fresher data than timestamp
Copy {
"result": "success",
"data": {
"action": "success"
}
}
Copy {
"result": "error",
"error": "Invalid MAC address format",
"code": "ER_INVALID_MAC_ADDRESS"
}
Copy {
"result": "error",
"error": "Newer setting already exists",
"code": "ER_CONFLICT",
"sub_code": "ER_OLD_ENTRY"
}
Get alerts
GET
https://network.ruuvi.com/alerts
Fetches alerts for all sensors user has access to or a single sensor if optional parameter is provided.
Request Body
200
Copy {
"result": "success",
"data": {
"alerts": [
{
userId: <userId>,
sensorId: <sensorMAC>,
description: <string>
type: <humidity|pressure|temperature|movement|offline>,
counter: number, // Relevant for movement
min: <lower limit>,
max: <higher limit>,
enabled: <true|false>,
offsetHumidity: <double>,
offsetTemperature: <double>,
offsetPressure: <double>,
triggered: <true|false>,
triggeredAt: <timestamp>,
delay: <number, positive integer>
}
]
}
}
Check if a sensor with given MAC address is claimed by someone
GET
https://network.ruuvi.com/check
Query Parameters
200: OK Masked email of sensor owner, empty string if sensor is not owned 400: Bad Request If request doesn't have "sensor" parameter, or parameter is not a valid MAC address 403: Forbidden If there was no valid authentication
Copy {
"status" : "success" ,
"data" : {
"email" : < string >
}
}
Copy "status" : "success" ,
"data" : {
"email" : < string >
}
}
Copy {
"result" : "error" ,
"error" : "Unauthorized request." ,
"code" : "ER_UNAUTHORIZED"
}
Contest ownership of a sensor
POST
https://network.ruuvi.com/contest-sensor
This call is used to reclaim a sensor claimed by someone else. After this endpoint returns 200, the sensor is claimed by calling account. Parameters are passed as a body JSON object.
Request Body
200: OK Sensor ownership was transferred 400: Bad Request Missing parameter or user has full claim count 401: Unauthorized User is not authenticated 403: Forbidden macAddress and secret do not match
Copy {
"result" : "success" ,
"data" : {
"sensor" : "C5:2A:E7:4D:CE:7F"
}
}
Copy {
'code' : { 'ER_CLAIM_COUNT_REACHED' , 'ER_MISSING_ARGUMENT' }
}
Copy {
'code' : 'ER_UNAUTHORIZED'
}
Copy {
'code' : 'ER_FORBIDDEN'
}
Claim a subscription by a code
POST
https://network.ruuvi.com/subscription
This endpoints applies a new subscription to user immediately. Previous subscription is lost. Parameters are passed as JSON in body. The success response has full subscription history of user, with active subscription being first element of array of subscriptions.
Request Body
200: OK Subscription was applied successfully 400: Bad Request Missing code 409: Conflict Subscription code is already used 404: Not Found Subscription code does not exist
Copy {
"result" : "success" ,
"data" : {
"subscriptions" : [
{
"subscriptionName" : "DEV" ,
"maxClaims" : 25 ,
"maxShares" : 40 ,
"maxSharesPerSensor" : 5 ,
"maxHistoryDays" : 720 ,
"maxResolutionMinutes" : 1 ,
"isActive" : true ,
"startTime" : 1673435374 ,
"endTime" : 1673608174
} ,
{
"subscriptionName" : "DEV" ,
"maxClaims" : 25 ,
"maxShares" : 40 ,
"maxSharesPerSensor" : 5 ,
"maxHistoryDays" : 720 ,
"maxResolutionMinutes" : 1 ,
"isActive" : false ,
"startTime" : 1673435292 ,
"endTime" : 1673608092
}
]
}
}
Copy {
"result" : "error" ,
"error" : "Invalid request format." ,
"code" : "ER_INVALID_FORMAT"
}
Copy {
"result" : "error" ,
"error" : "Code already claimed" ,
"code" : "ER_SUBSCRIPTION_CODE_USED"
}
Copy {
"result" : "error" ,
"error" : "Code not found" ,
"code" : "ER_SUBSCRIPTION_NOT_FOUND"