REST API#

Heedy’s REST API allows programmatic access to backend data and functionality. Heedy plugins are also encouraged to add their own functionality at /api/{plugin_name}.

This document details the API that comes built-in to heedy by default.

Authorization#

Since heedy was built to be internet-facing, most resources are only available to authorized users. The API can be accessed in three separate ways:

  • By apps, with an app token. Apps have scoped access to the user’s data.

  • By plugins, using their plugin key. Plugins get full access to the API for all users.

  • By users, using a browser cookie. This method is only used for the frontend.

Each of these access methods is described individually below.

Warning

Because access credentials are sent directly with each request, it is important to use https to secure any internet-accessible heedy instance.

App Token#

An app token requires each request to include an Authorization header:

curl --header "Authorization: Bearer MYTOKEN" \
     http://localhost:1324/api/users/myuser

This method is used for all external heedy apps, and is limited in access to the scopes set for the app. You can get an app’s access token in the app’s page.

Plugin Key#

A backend plugin is given a plugin key in the json bundle passed to its stdin on startup (see plugin backends). It uses this key for all requests. The key is passed in a special X-Heedy-Key header:

curl --header "X-Heedy-Key: MYKEY" \
     http://localhost:1324/api/users/myuser

A plugin can access the API as if it were any user or app by including the X-Heedy-As header along with its plugin key (see plugin backends).

Errors#

Each request returns either the requested resource as JSON, or, upon failure, returns a 4xx error code, with the following json response body:

{
  // An error type
  "error": "not_found",
  // Text description of the error
  "error_description": "The given user was not found",
  // ID of the request
  "id": "bpqirkjqfqj3m0sqttf0"
}

API#

Users#

/api/users

GET
Returns a list of users in the heedy instance that are accessible with the given access token.
URL Params
  • icon (boolean,false) - whether or not to include each user’s icon.

Example
curl --header "Authorization: Bearer MYTOKEN" \
     http://localhost:1324/api/users
[{"username": "myuser", ... }, ... ]
POST
Create a new user. Only accessible from plugins and admin users.
Body
  • username (string, required) - the username of the user

  • password (string, required) - the user’s password

  • name (string,””) - the user’s full name

  • description (string,””) - the user’s description

  • icon (string,””) - user’s icon, base64 urlencoded

  • public_read (boolean,false) - whether the user is visible to the public.

  • users_read (boolean,false) - whether the user is visible to other users.

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --header "Content-Type: application/json" \
     --request POST \
     --data '{"username":"user2","password":"xyz"}' \
     http://localhost:1324/api/users
{"result":"ok"}

/api/users/{username}

GET
Returns the user with the given username.
URL Params
  • icon (boolean,false) - whether or not to include the user’s icon.

Example
curl --header "Authorization: Bearer MYTOKEN" \
     http://localhost:1324/api/users/myuser?icon=true
{
    "username":"myuser",
    "name":"",
    "description":"",
    "icon":"",
    "public_read":false,
    "users_read":false
}
PATCH
Updates the user with the included fields.
Body
  • password (string,null) - the user’s password

  • name (string,null) - the user’s full name

  • description (string,null) - the user’s description

  • icon (string,null) - user’s icon, base64 urlencoded

  • public_read (boolean,null) - whether the user is visible to the public.

  • users_read (boolean,null) - whether the user is visible to other users.

Example
curl --header "Authorization: Bearer MYTOKEN" \
     --header "Content-Type: application/json" \
     --request PATCH \
     --data '{"description":"A new user description!"}' \
     http://localhost:1324/api/users/myuser
{"result":"ok"}
DELETE
Deletes the user with the given username, and all of the user's data.
Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --request DELETE \
     http://localhost:1324/api/users/myuser
{"result":"ok"}

Apps#

/api/apps

GET
Returns a list of apps in the heedy instance that satisfy the given constraints, and are accessible with the given access token. A user only has access to apps belonging to the user, so when querying apps from the frontend or an app, you will only get the user's apps.
URL Params
  • icon (boolean,false) - whether or not to include each app’s icon.

  • token (boolean,false) - whether or not to include each app’s access token.

  • owner (string,null) - limit results to the apps belonging to the given username

  • plugin (string,null) - limit results to apps with the given plugin key

  • enabled (boolean,null) - limit results to apps that are enabled/disabled (true/false)

Example
curl --header "Authorization: Bearer MYTOKEN" \
     http://localhost:1324/api/apps?owner=myuser
[{"id": "051942...", ... }, ... ]
POST
Create a new app. Only accessible from plugins and users. If authenticated as a user, the owner parameter is optional. Returns the created app, subject to query URL params identical to a GET request for the app ID.
Body
  • name (string,required) - the app’s name

  • owner (string,required) - the user to own the app. Automatically set to the current user when authenticated as a user.

  • description (string,””) - the app’s description

  • icon (string,””) - app’s icon, base64 urlencoded

  • plugin (string,””) - the app’s plugin key.

  • enabled (boolean,true) - whether the app’s access token is active

  • scope (string,””) - the scopes given to the app, each separated by a space.

  • settings (object,{}) - the app’s settings

  • settings_schema (object,{}) - the json schema for the app’s settings.

  • access_token (string) - If set to empty string, the app will be created without an access token. Otherwise (and by default), the app will have a randomly generated access token (any other string value of access_token is ignored).

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --header "Content-Type: application/json" \
     --request POST \
     --data '{"name":"My App","owner": "myuser"}' \
     http://localhost:1324/api/apps
{
    "id":"0519420b-e3cf-463f-b794-2adb440bfb9f",
    "name":"My App",
    "description":"",
    "owner":"myuser",
    "enabled":true,
    "created_date":"2020-03-21",
    "last_access_date":null,
    "scope":"",
    "settings":{},
    "settings_schema":{}
}

/api/apps/{appid}

GET
Returns the app with the given ID
URL Params
  • icon (boolean,false) - whether or not to include the app’s icon.

  • token (boolean,false) - whether or not to include the app’s access token.

Example
curl --header "Authorization: Bearer MYTOKEN" \
 http://localhost:1324/api/apps/0519420b-e3cf-463f-b794-2adb440bfb9f
{
    "id": "0519420b-e3cf-463f-b794-2adb440bfb9f",
    "name": "My App",
    "description": "",
    "owner": "myuser",
    "enabled": true,
    "created_date": "2020-03-21",
    "last_access_date": null,
    "scope": "",
    "settings": {},
    "settings_schema": {}
}
PATCH
Updates the app with the included fields.
Body
  • name (string,null) - the app’s full name

  • description (string,null) - the app’s description

  • icon (string,null) - app’s icon, base64 urlencoded

  • plugin (string,null) - the app’s plugin key.

  • enabled (boolean,null) - whether the app’s access token is active

  • scope (string,null) - the scopes given to the app, each separated by a space.

  • settings (object,null) - the app’s settings

  • settings_schema (object,null) - the json schema for the app’s settings.

  • access_token (string,null) - If set to empty string, the access token will be removed. Otherwise, if given a value, the app will generate a new random token (the value is ignored).

Example
curl --header "Authorization: Bearer MYTOKEN" \
     --header "Content-Type: application/json" \
     --request PATCH \
     --data '{"description":"A new app description!"}' \
 http://localhost:1324/api/apps/0519420b-e3cf-463f-b794-2adb440bfb9f
{"result":"ok"}

Since updating an access token randomly generates a new value, if the token was updated, it will be returned as part of the result.

{"result":"ok","access_token":"sdf43ri3i3g4j3ook"}
DELETE
Deletes the given app, and all of its data, including objects it manages.
Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --request DELETE \
 http://localhost:1324/api/apps/0519420b-e3cf-463f-b794-2adb440bfb9f
{"result":"ok"}

Objects#

Heedy objects are special, since each object type has its own API. This section first describes the general object API that is valid for all object types, then it describes the additional API for objects of the type timeseries.

/api/objects

GET
Returns a list of objects in the heedy instance satisfying the given constraints, and accessible to the authenticated entity.
URL Params
  • icon (boolean,false) - whether or not to include each object’s icon.

  • owner (string,null) - limit results to the objects belonging to the given username

  • app (string,null) - limit results to objects belonging to the given app

  • key (string,null) - limit results to objects with the given key

  • tags (string,null) - limit results to objects which each include all the given tags

  • type (string,null) - limit results to objects of the given type

  • limit (int,null) - set a maximum number of results to return

Example
curl --header "Authorization: Bearer MYTOKEN" \
     http://localhost:1324/api/objects?owner=myuser
[{"id": "1a1f624...", ... }, ... ]
POST
Create a new object of the given type. Unless owner/app is set, the object will belong to the authenticated entity.
Body
  • name (string,required) - the object’s name

  • type (string,required) - the object’s type

  • owner (string,current_user) - the user to own the object.

  • app (string,current_app/null) - the app to own the object.

  • description (string,””) - the object’s description

  • icon (string,””) - object’s icon, base64 urlencoded

  • tags (string,””) - a set of space-separated tags to give the object

  • key (string,null) - a key to give the object for easy programmatic access (only objects belonging to apps can have keys)

  • owner_scope (string,”*”) - the set of space-separated scopes to give the object’s owner, if the object belongs to an app. “*” means all scopes.

  • meta (object,{}) - object metadata. Each object type defines its own metadata.

Example
curl --header "Authorization: Bearer MYTOKEN" \
     --header "Content-Type: application/json" \
     --request POST \
     --data '{"name":"My Timeseries","type":"timeseries"}' \
     http://localhost:1324/api/objects
{
    "id":"1a1f624e-96f9-416a-9982-6b1ef618661c",
    "name":"My TS",
    "description":"",
    "owner":"myuser",
    "app": "0519420b-e3cf-463f-b794-2adb440bfb9f",
    "tags":"",
    "type":"timeseries",
    "meta":{"actor":false,"schema":{}},
    "created_date":"2020-03-21",
    "modified_date":null,
    "owner_scope":"*",
    "access":"*"
}

/api/objects/{objectid}

GET
Returns the object with the given ID
URL Params
  • icon (boolean,false) - whether or not to include the object’s icon.

Example
curl --header "Authorization: Bearer MYTOKEN" \
 http://localhost:1324/api/objects/1a1f624e-96f9-416a-9982-6b1ef618661c
{
    "id":"1a1f624e-96f9-416a-9982-6b1ef618661c",
    "name":"My TS",
    "description":"",
    "owner":"myuser",
    "app": "0519420b-e3cf-463f-b794-2adb440bfb9f",
    "tags":"",
    "type":"timeseries",
    "meta":{"actor":false,"schema":{}},
    "created_date":"2020-03-21",
    "modified_date":null,
    "owner_scope":"*",
    "access":"*"
}
PATCH
Updates the object with the included fields.

The meta object is updated on a per-field basis, meaning that the object sent as the meta field will be merged with the existing meta values. Setting meta to {"schema":{"type":"number"}} in a timeseries object will update the schema of the meta object, leaving all other fields intact. To delete a field from the meta object, set it to null ({"actor":null}).

Body
  • name (string,null) - the object’s name

  • description (string,null) - the object’s description

  • icon (string,null) - object’s icon, base64 urlencoded

  • tags (string,null) - a set of space-separated tags to give the object

  • key (string,null) - a key to give the object for easy programmatic access (only objects belonging to apps can have keys)

  • owner_scope (string,null) - the set of space-separated scopes to give the object’s owner, if the object belongs to an app. “*” means all scopes.

  • meta (object,null) - the fields of object metadata to update. Each object type defines its own metadata.

Example
curl --header "Authorization: Bearer MYTOKEN" \
     --header "Content-Type: application/json" \
     --request PATCH \
     --data '{"meta":{"schema":{"type":"number"}}}' \
 http://localhost:1324/api/objects/1a1f624e-96f9-416a-9982-6b1ef618661c
{"result":"ok"}
DELETE
Deletes the given object, and all of its data.
Example
curl --header "Authorization: Bearer MYTOKEN" \
     --request DELETE \
 http://localhost:1324/api/objects/1a1f624e-96f9-416a-9982-6b1ef618661c
{"result":"ok"}

Timeseries#

The timeseries is a builtin object type. It defines its own API for interacting with the datapoints contained in the series.

Meta#

Each object holds a meta field. A timeseries object’s meta object has the following fields:

  • schema (object,{}) - a JSON Schema to which each datapoint must conform.

/api/objects/{objectid}/timeseries

GET
Returns the timeseries data subject to the given constraints.
URL Params
  • t (float,string*,null) - get just the datapoint with the given timestamp

  • i (int,null) - get just the datapoint at the given index

  • t1 (float,string*,null) - return only datapoints where t >= t1

  • t2 (float,string*,null) - return only datapoints where t < t2

  • i1 (int,null) - return only datapoints where index >= i1

  • i2 (int,null) - return only datapoints where index < i2

  • limit (int,null) - return a maximum of this number of datapoints

  • transform (string,null) - a PipeScript transform to run on the data

*: The t, t1 and t2 queries accept strings of times relative to now. For example, t1=now-2d sets t1 to exactly 2 days ago.

Example
curl --header "Authorization: Bearer MYTOKEN" \
 http://localhost:1324/api/objects/1a1f624e-96f9-416a-9982-6b1ef618661c/timeseries?t1=now-2h
[
  { "t": 1584812297, "d": 3 },
  { "t": 1584812303, "d": 2 },
  { "t": 1584812313, "d": 2 },
  { "t": 1584812339, "d": 2 }
]
POST
Insert new datapoints into the timeseries
URL Params
  • method (string,”update”) - the insert method, one of:

    • update - Overwrite any datapoints that already exist with time ranges defined by the inserted datapoints.

    • append - Only permit appending datapoints to the end of the timeseries

    • insert - Don’t permit inserting datapoints that interfere with data already in the timeseries

Body
A json array of datapoints, conforming to the timeseries schema, with each datapoint in the following format:
{
    // unix timestamp in seconds
    "t": 1584812297.1,
    // (optional) duration of the datapoint
    "dt": 60.0,
    // the datapoint's data (anything that can be encoded as json)
    "d": 3
}
Example
curl --header "Authorization: Bearer MYTOKEN" \
     --header "Content-Type: application/json" \
     --request POST \
     --data '[{"t":1584812297,"d":3},{"t":1584812303,"d":2}]' \
 http://localhost:1324/api/objects/1a1f624e-96f9-416a-9982-6b1ef618661c/timeseries
{ "result": "ok" }
DELETE
Delete the timeseries data that satisfies the given constraints
URL Params
  • t (float,string*,null) - remove just the datapoint with the given timestamp

  • i (int,null) - remove just the datapoint at the given index

  • t1 (float,string*,null) - remove only datapoints where t >= t1

  • t2 (float,string*,null) - remove only datapoints where t < t2

  • i1 (int,null) - remove only datapoints where index >= i1

  • i2 (int,null) - remove only datapoints where index < i2

*: The t, t1 and t2 queries accept strings of times relative to now. For example, t1=now-2d sets t1 to exactly 2 days ago.

Example
curl --header "Authorization: Bearer MYTOKEN" \
     --request DELETE \
 http://localhost:1324/api/objects/1a1f624e-96f9-416a-9982-6b1ef618661c/timeseries?t1=now-2h
{ "result": "ok" }

/api/objects/{objectid}/timeseries/length

GET
Returns the number of datapoints in the timeseries.
curl --header "Authorization: Bearer MYTOKEN" \
 http://localhost:1324/api/objects/1a1f624e-96f9-416a-9982-6b1ef618661c/timeseries/length
4

Notifications#

Notifications are a built-in plugin that allows attaching messages to users/apps/objects. These messages are visible from the main heedy UI.

/api/notifications

GET
Read the list of notifications subject to the given constraints.
URL Params
  • key (string,null) - only return the notifications with the given key

  • user (string,null) - limit to notifications for the given user

  • app (string,null) - limit to notifications for the given app

  • object (string,null) - limit to notifications for the given app

  • global (boolean,false) - limit to notifications that show in the notifications page

  • seen (boolean,null) - limit to notifications that have/have not been seen

  • dismissible (boolean,null) - limit to notifications that are/are not dismissible

  • type (string,null) - limit to notifications of the given type

  • include_self _(boolean,false) - whether to include self when * present. For example, when user=myuser&app=*, notifications for user myuser are included if and only if include_self is true.

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
      http://localhost:1324/api/notifications?user=myuser
POST

Create a notification for a user/app/object. If a notification with the given key exists for the given user/app/object, update the notification with the given data.

Body
  • key (string,required) - set the notification’s key (unique for the user/app/object’s notifications)

  • title (string,””) - the header text to show

  • description (string,””) - main notification content. Can include markdown.

  • user (string,required*) - add the notification to the given username

  • app (string,required*) - add the notification to the given app

  • object (string,required*) - add the notification to the given object

  • global (boolean,false) - show in the global notification page?

  • seen (boolean,false) - has the notification been seen by the user?

  • dismissible (boolean,true) - allow the user to dismiss the notifcation

  • type (string,null) - the notification type, one of info,warning,error

  • actions (array,[]) - the list of actions to give the notification, which are shown to the user as buttons. Each action object has the following fields:

    • title (string,required) - the text to display in the button

    • href (string,required) - the url to navigate to. If it starts with #, it is relative to the UI. If starts with /, relative to heedy’s root. Otherwise, it is considered a raw URL.

    • description (string,””) - the tooltip to show on button hover

    • icon (string,””) - the icon to show in the button

    • new_window (boolean,false) - whether to open href in a new window

    • dismiss (boolean,false) - whether to dismiss the notification on click

*: Only one of the user/app/object fields can be set (the notification can only belong to a user or an app, or an object, not all at the same time)

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --header "Content-Type: application/json" \
     --request POST \
     --data '{"key":"mynotification","user":"myuser","title": "Hello World!"}' \
     http://localhost:1324/api/notifications
{ "result": "ok" }
PATCH

Modify the included fields of all notifications that satisfy the constraints given in the URL Params.

URL Params
  • key (string,null) - only return the notifications with the given key

  • user (string,null) - limit to notifications for the given user

  • app (string,null) - limit to notifications for the given app

  • object (string,null) - limit to notifications for the given app

  • global (boolean,false) - limit to notifications that show in the notifications page

  • seen (boolean,null) - limit to notifications that have/have not been seen

  • dismissible (boolean,null) - limit to notifications that are/are not dismissible

  • type (string,null) - limit to notifications of the given type

  • include_self _(boolean,false) - whether to include self when * present. For example, when user=myuser&app=*, notifications for user myuser are included if and only if include_self is true.

Body
  • key (string,null) - set the notification’s key (unique for the user/app/object’s notifications)

  • title (string,null) - the header text to show

  • description (string,null) - main notification content. Can include markdown.

  • global (boolean,null) - show in the global notification page?

  • seen (boolean,null) - has the notification been seen by the user?

  • dismissible (boolean,null) - allow the user to dismiss the notifcation

  • type (string,null) - the notification type, one of info,warning,error

  • actions (array,null) - the list of actions to give the notification, which are shown to the user as buttons. Each action object has the following fields:

    • title (string,required) - the text to display in the button

    • href (string,required) - the url to navigate to. If it starts with #, it is relative to the UI. If starts with /, relative to heedy’s root. Otherwise, it is considered a raw URL.

    • description (string,””) - the tooltip to show on button hover

    • icon (string,””) - the icon to show in the button

    • new_window (boolean,false) - whether to open href in a new window

    • dismiss (boolean,false) - whether to dismiss the notification on click

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --header "Content-Type: application/json" \
     --request PATCH \
     --data '{"seen":true}' \
     http://localhost:1324/api/notifications?user=myuser&key=mynotification
{ "result": "ok" }
DELETE

Deletes the notifications that satisfy the given constraints.

URL Params
  • key (string,null) - delete the notifications with the given key

  • user (string,null) - limit to notifications for the given user

  • app (string,null) - limit to notifications for the given app

  • object (string,null) - limit to notifications for the given app

  • global (boolean,false) - limit to notifications that show in the notifications page

  • seen (boolean,null) - limit to notifications that have/have not been seen

  • dismissible (boolean,null) - limit to notifications that are/are not dismissible

  • type (string,null) - limit to notifications of the given type

  • include_self _(boolean,false) - whether to include self when * present. For example, when user=myuser&app=*, notifications for user myuser are included if and only if include_self is true.

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --request DELETE \
     http://localhost:1324/api/notifications?user=myuser&seen=true
{ "result": "ok" }

Key-Value Storage#

The key-value database is a built-in plugin, allowing other plugins to store metadata attached to users, apps and objects. It is recommended that a plugin use its own plugin name as the namespace under which it stores its data.

An app can also store its own metadata, by using its app ID or self as the namespace when accessing the app’s key-value store.

/api/kv/users/{id}/{namespace}

GET
Returns a json object containing all of the key-value pairs in the given namespace
Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     http://localhost:1324/api/kv/users/myuser/myplugin
{
  "mykey": 45.54
}
POST

Sets the key/values of the namespace to the posted body

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --request POST \
     --header "Content-Type: application/json" \
     --data '{"mykey": 45.54}' \
     http://localhost:1324/api/kv/users/myuser/myplugin
{ "result": "ok" }
PATCH

Updates only the given key/value pairs for the given namespace

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --request PATCH \
     --header "Content-Type: application/json" \
     --data '{"mykey": 45.54}' \
     http://localhost:1324/api/kv/users/myuser/myplugin
{ "result": "ok" }

/api/kv/users/{id}/{namespace}/{key}

GET
Get the value of the given key in the given namespace.
Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     http://localhost:1324/api/kv/users/myuser/myplugin/mykey
45.54
POST

Sets the given key to the posted json value

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --request POST \
     --header "Content-Type: application/json" \
     --data '45.54' \
     http://localhost:1324/api/kv/users/myuser/myplugin/mykey
{ "result": "ok" }
DELETE

Deletes the given key from the given namespace.

Example
curl --header "X-Heedy-Key: MYPLUGINKEY" \
     --request DELETE \
     http://localhost:1324/api/kv/users/myuser/myplugin/mykey
{ "result": "ok" }

/api/kv/apps/{id}/{namespace}

Refer to /api/kv/users/{id}/{namespace}, which has an identical API

/api/kv/apps/{id}/{namespace}/{key}

Refer to /api/kv/users/{id}/{namespace}/{key}, which has an identical API

/api/kv/objects/{id}/{namespace}

Refer to /api/kv/users/{id}/{namespace}, which has an identical API

/api/kv/objects/{id}/{namespace}/{key}

Refer to /api/kv/users/{id}/{namespace}/{key}, which has an identical API