Canopy REST API
Authentication
Users
Devices
Organizations
Other Endpoints
Error Responses
Authentication
Permission Model
The Canopy REST API is used by IoT devices and apps (on behalf of human users). There are two types of credentials, depending on whether a human user or an IoT device is making the request. Additionally, two types of authentication are supported: BASIC AUTH and Session-based AUTH.
Credential Type HTTP BASIC AUTH Session-based AUTH Username Password
User Yes Yes Chosen by user Chosen by user
Device Yes Not supported Device UUID Device Secret Key
Session-based AUTH Login
Request
REST
EXAMPLE
CURL
POST /api/login
{
    "username" : <USERNAME_OR_EMAIL>,
    "password" : <PASSWORD>
}
POST /api/login
{
    "username" : "leela",
    "password" : "P1anetExpre55"
}
curl -c canopy.cookies -d '{ "username" : "leela", "password" : "P1anetExpre55" }' http://sandbox.canopy.link/api/login
Success Response
Header:
Set-Cookie:canopy-login-session=<COOKIE_VALUE>
Body:
REST
EXAMPLE
200 OK
{
    "result" : "ok",
    "username" : <USERNAME>,
    "email" : <EMAIL>
}
200 OK
{
    "result" : "ok",
    "username" : "leela",
    "email" : "leela@planetexpress.com"
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 Non-JSON payload; Required fields missing or incorrect type
incorrect_username_or_password Error 401 Incorrect username/password combo
internal_error Error 500 Internal error occurred
Session-based AUTH Logout
Request
POST /api/logout
Success Response
The /api/logout/ endpoint sends this response whether or not you were logged in.
{
    "result" : "ok"
}
Error Response
This request may return the following errors:
Error Response Code Reason
internal_error Error 500 Internal error occurred
BASIC AUTH for Users

To use BASIC AUTH, include the Base64-encoded "username:password" in every request. For example, if your username is "leela" and your password is "P1anetExpre55", include the following header with your request:

Authorization: Basic bGVlbGE6UDFhbmV0RXhwcmU1NQ==

Most HTTP libraries and tools can handle BASIC AUTH for you. For example:

CURL
JQUERY
PYTHON
curl -u leela:P1anetExpre55
$.ajax({
    username : "leela",
    password : "P1anetExpre55",
    ...
});
from requests.auth import HTTPBasicAuth
requests.get(url, auth=HTTPBasicAuth('leela', 'P1anetExpre55'))
BASIC AUTH for Devices
Canopy REST API requests can be made by both human users and IoT devices. When an IoT device is making the request:
  • The device's UUID is the username.
  • The device's Secret Key is the password.
For example:
For a device with:

    - UUID: d5c827d2-1ba7-4da6-b87b-0864f0969fa8
    - Secret Key: 00ae8a3ba42fa86765c885da90a7fcaec9898acb13fc2e00

You would:

    Base64Encode("d5c827d2-1ba7-4da6-b87b-0864f0969fa8:00ae8a3ba42fa86765c885da90a7fcaec9898acb13fc2e00")

And then send this header:

    Authorization: Basic ZDVjODI3ZDItMWJhNy00ZGE2LWI4N2ItMDg2NGYwOTY5ZmE4OjAwYWU4YTNiYTQyZmE4Njc2NWM4
    ODVkYTkwYTdmY2FlYzk4OThhY2IxM2ZjMmUwMA==
Users
Create a User
Request
REST
EXAMPLE
CURL
POST /api/create_user
{
    "username" : string,
    "email" : string,
    "password" : string,

    // optional:
    "skip-email" : bool
}
POST /api/create_user
{
    "username" : "leela",
    "email" : "leela@planetexpress.com",
    "password" : "P1anetExpre55",
    "skip-email" : true
}
curl -d '{ "username" : "leela", "email": "leela@planetexpress.com", "password" : "P1anetExpre55" }' http://sandbox.canopy.link/api/create_user
Field Required Description Validation
"username" Required Account username string
  • 5-24 characters
  • Characters a-z, A-Z, 0-9 and underscore(_) allowed.
  • Must start with a letter
"email" Required Account email string
  • Must be a valid email address.
"password" Required Account password string
  • 6-120 characters
  • All characters allowed
  • Case sensitive
"skip-email" Optional Defaults to false, meaning a welcome email will be sent to the user. If true the welcome email will not be sent. This is useful when running tests & simulators that should not send out lots of emails. bool
Success Response
REST
EXAMPLE
200 OK
{
    "validated" : false,
    "email" : <EMAIL>,
    "result" : "ok",
    "username" : <USERNAME>
}
200 OK
{
    "validated" : false,
    "email" : "leela@planetexpress.com",
    "result" : "ok",
    "username" : "leela",
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 Non-JSON payload; Required fields missing or incorrect type; Input validation failure.
email_taken Error 400 Email not available
username_taken Error 400 Username not available
internal_error Error 500 Internal error occurred
Get Current User
Request
GET /api/user/self
This request requires that User credentials have been supplied (or a User is logged in), otherwise an error will be returned.
Success Response
{
    "result" : "ok",
    "validated" : <VALIDATED>,
    "username" : <USERNAME>,
    "email" : <EMAIL>,
}
Error Response
This request may return the following errors:
Error Response Code Reason
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
Update User Profile
Request
POST /api/user/self
{
    "email" : <NEW_EMAIL>,
    "old_password" : <OLD_PASSWORD>,
    "new_password" : <NEW_PASSWORD>
}
All fields are optional, although if "new_password" is provided then "old_password" must be provided as well. Omitted fields will not be modified.
Success Response
{
    "result" : "ok",
    "username" : <USERNAME>,
    "email" : <EMAIL>,
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 Non-JSON payload; Required fields missing or incorrect type; Input validation failure.
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
Delete Current User
Request
REST
REST (no request body)
CURL
DELETE /api/user/self
{
    "skip-email" : bool
}
DELETE /api/user/self
curl -X DELETE -u "username:password" http://sandbox.canopy.link/api/user/self
The request body may be omitted.
Field Required Description Validation
"skip-email" Optional Defaults to false, meaning a welcome email will be sent to the user. If true the welcome email will not be sent. This is useful when running tests & simulators that should not send out lots of emails. bool
Success Response
200 OK
{
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 Non-JSON payload; Required fields missing or incorrect type; Input validation failure.
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
Devices
Create User-Linked Devices
Request
To create one or more new devices when authenticated as a User:
POST /api/create_devices
{
    "quanitity" : <QUANTITY>
    "friendly_names" : [<FRIENDLY_NAME>, ...]
}
This will create new device resource(s) and assign the current User to have the "creator" role for those device(s). This will also automatically assign a UUID and Secret Key to each new device resource.
Response
{
    "result" : "ok",
    "count" : <COUNT>,
    "devices" : [ {
        "friendly_name" : <FRIENDLY_NAME>,
        "device_id" : <UUID>,
        "device_secret_key" : <DEVICE_SECRET_KEY>
    }, ... ]
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 Non-JSON payload; Required fields missing or incorrect type; Input validation failure.
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
Create Anonymous Device
TBD[Some deployments of the Canopy Cloud Service allow devices to be created that are not associated with any User account. These are called "anonymous devices". Each anonymous device has a user-assigned Secret Key that is used to restrict access. Anonymous devices are automatically created whenever the API call /api/my_device is made using device credentials that have never been encountered before.]
Delete Device
Request
To delete a device:

TBD: Should there be a way to archive/suspend a device?

REST
EXAMPLE
DELETE /api/device/<UUID>
DELETE /api/device/d5c827d2-1ba7-4da6-b87b-0864f0969fa8
Or to delete the currently authenticated device:
DELETE /api/device/self
Success Response
On success, the Device and all Cloud Variable associated with it are deleted. The Device's credentials can no longer be used to make API calls.
200 OK
{
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
List Devices
Request
To obtain a list of all devices the current User has access to:
GET /api/user/self/devices?
    filter=<FILTER>
    &sort=<SORT>
    &limit=<LIMIT>
Details about filtering, sorting, and paginating the results are described in the following sections.
Filtering Device List

<FILTER> is an expression encoded as a string that evaluates to a boolean value.

A <TERM> corresponds to a single boolean test. A "value test" has the form:

<VARIABLE> <RELATION> <VALUE>
For example:
temperature >= 50.8
Valid relations are:
Relation Description
= Equals
!= Does not equal
> Greater than (numeric and date types only)
>= Greater than or equal to (numeric and date types only)
< Less than (numeric and date types only)
<= Less than or equal to (numeric and date types only)

A "variable presence test" has the form:

HAS <VARIABLE>
For example:
HAS humidity

The ! operator inverts the following TERM.

For example:
!(HAS humidity)
!(temperature < 10)
The boolean operators && (AND) and || (OR) are supported:
<TERM> && <TERM>
<TERM> || <TERM>
For example:
temperature >= 50.8 && temperature < 90.5
Valid terms may be the name of any cloud variable, or one of the system builtins that begin with system.
Key Description Potential Values
system.ws_connection_status Is websocket connected?
  • "never_connected"
  • "connected"
  • "disconnected"
system.activity_status Has device communicated with remote recently
  • "newly_created"
  • "active"
  • "inactive"
<CLOUD_VAR_NAME> Filter based on cloud variable value. Depends on cloud variable datatype.
Finally, parenthesis can be used to construct larger expressions. For example:
!(temperature >= 50.8 && temperature < 90.5) || HAS humidity
Sorting Device List

<SORT> is a sequence of comma-separated keys specifying the sort order. The highest priority key comes first, followed by tie breakers. By default ascending order is used. To sort by a key in descending order, add ! before the key. For sort order determination, a missing cloud variable comes before the minimum value (i.e. at the front of the list when ascending, at the end of the list when descending).

This example sorts by temperature (ascending) then, to break ties, humidity (descending)

temperature,!humidity
The valid keys may be any cloud variable, or builtin. See Filtering Device List.
Limits (Pagination)
By default a system-defined maximum number of results will be returned, or you can provide start and count values to paginate through larger result sets.
limit=<start>,<count>
For example:
limit=0,50
There is an implementation defined maximum limit to the number of results that will be returned. If count is above this number, then the results will be truncated and the response count field will tell you how many items are actually returned.
Response
{
    "result" : "ok",
    "count" : <COUNT>,
    "devices" : [ {
        "device_id" : <UUID>,
        "friendly_name" : <FRIENDLY_NAME>,
        "status" : {
            "ws_connected" : <WS_CONNECTED>,
            "active_status" : <ACTIVE_STATUS>,
            "last_activity_time" : <LAST_ACTIVITY_TIME>,
        },
    }, ...  ]
}
Get Single Device
Request
To obtain information about a single device:
REST
Example
GET /api/device/<UUID>?
    timestamps=<TIMESTAMPS>
GET /api/device/d5c827d2-1ba7-4da6-b87b-0864f0969fa8?timestamps=rfc3339
Or
GET /api/device/self?
    timestamps=<TIMESTAMPS>

The /api/device/<UUID> form of the request obtains a specific device by UUID. The requester (i.e. the authenticated User or Device) must have permission to access the specified Device, otherwise an error will occur.

The second form of the request obtains the currently authenticated device (i.e. the device whose credentials were supplied via BASIC AUTH). It returns an error if User credentials are supplied.

TBD [If the provided device credentials are unknown to the remote, and the server supports anonymous devices, then a new anonymous device will be created. If the first form of the request is used, then an anonymous device will only be created if the UUID in the URI matches the UUID in the credentials, otherwise an error will occur.]

The following query parameters are accepted

Query Parameter Required? Description
timestamps Optional Optionally select timestamp format. This may be either:
  • "epoch_us" - (Default) return timestamps as uint64 microseconds since the Unix epoch.
  • "rfc3339" - return timestamps as RFC3339-encoded datetime strings in UTC timezone.

Success Response
See Device Object Response for details.
Get Historical Cloud Var Data
Request
Retrieve historic cloud variable samples.
REST
EXAMPLE
GET /api/device/<UUID>/<VAR_NAME>?
    start_time=<START_TIME>
    &end_time=<END_TIME>
    &hint_num_samples=<HINT_NUM_SAMPLES>
GET /api/device/d5c827d2-1ba7-4da6-b87b-0864f0969fa8/temperature?start_time='2015-03-19 12:00:00'
Success Response
REST
EXAMPLE
{
    "result" : "ok",
    "current_clock_us" : uint64,
    "current_clock_utc" : string,
    "count" : int,
    "samples" : [
        {
            "t" : string,
            "v" : value
        }, {
            ...
        }
    ]
}
{
    "result" : "ok",
    "current_clock_us" : 1426772581829000,
    "current_clock_utc" : "2015-03-19T22:47:31Z",
    "count" : 3,
    "samples" : [
        {
            "t": "2015-03-18T16:57:36Z",
            "v": 24.821886
        },
        {
            "t": "2015-03-18T16:57:56Z",
            "v": 24.777397
        },
        {
            "t": "2015-03-18T16:58:17Z",
            "v": 24.777014
        }
    ]
}
Field Description
"clock_us" Server time in microseconds since the Unix Epoch.
"clock_utc" Server time reported as an RFC3339-encoded UTC datetime string
"count" Integer number of samples returned.
"samples" Array of Sample objects.
"t" String datetime of sample in RFC3339 format with UTC timezone.
"v" Value of the cloud variable at the timestamp. The JSON datatype depends on the Cloud Variable's datatype.
Update Device Properties
Request
To update device properties:
POST /api/device/<UUID>
{
    "friendly_name" : string,
    "location_note" : string
}

All fields are optional and omitted fields will be left unchanged.

This may be combined with Add/Update Cloud Var Metadata and Update Cloud Var Values into a single request.

Field Description
"friendly_name" User-assigned name for device. May be any UTF-8 string of at most 127 bytes.
"location_note" User-assigned note about the location of this device. May be any UTF-8 string of at most 1023 bytes.

This request may be combined with Declare Cloud Variable and Set Cloud Variable Values into a single request.

Response
The remote responds with the entire Device object. See Device Object Response.
Declare Cloud Variables
Request

Before using a Cloud Variable you must declare it, specifying its name, direction, datatype and other metadata properties. The first time a cloud variable name is declared for a particular device, a cloud variable gets created, but not set. Declaring a cloud variable is an idempotent operation, meaning that subsequent identical declarations will have no effect. However, attempts to redeclare a named cloud variable with different datatype or direction will fail unless you delete it first.

REST
EXAMPLE
POST /api/device/<UUID>
{
    "var_decls" : {
        <VAR_DECL> : <VAR_PROPERTIES>,
        ...
    }
}
POST /api/device/d5c827d2-1ba7-4da6-b87b-0864f0969fa8
{
    "var_decls" : {
        "out float32 temperature" : { },
        "out float32 humidity" : { },
        "in int8 dimmer_brightness" : { },
        "in bool reboot_now" : { },
        ...
    }
}

A <VAR_DECL> is a string with the following format:

"<DIRECTION> <TYPE> <NAME>"
For example:
"out float32 temperature"
The <DIRECTION> determines who can modify the value:
Direction Who Can Modify Value
out Can only be set by the device that the cloud variable belongs to.
in Can only be set by users and other devices (i.e. not the device that the cloud variable belongs to).
inout Anyone with access to the cloud variable can set it.
The <TYPE> determines the datatype of the cloud variable. It may be any of the following:
Datatype Description
bool Boolean value
int8 8-bit signed integer
int16 16-bit signed integer
int32 32-bit signed integer
uint8 8-bit unsigned integer
uint16 16-bit unsigned integer
uint32 32-bit unsigned integer
datetime 64-bit unsigned integer representing microseconds from Epoch
float32 32-bit floating point number
float64 64-bit floating point number
string string value
TBD composite objects TBD
The <NAME> is a string name for the cloud variable. The name can be at most 127 characters in length, must start with a letter or underscore (_), must contain at least one letter (A-Z or a-z), and must only contain uppercase (A-Z), lowercase (a-z), digits (0-9), and underscore (_).

TBD reserved values?

The <VAR_PROPERTIES> is an object specifying additional metadata for the cloud variable. At this time it must be an empty object {} reserved for future use.

This request may be combined with Update Device Properties and Update Cloud Var Value into a single request.

Response
The remote responds with the entire Device object. See Device Object Response.
Set Cloud Variable Values
Request

To set Cloud Variable values, use:

REST
EXAMPLE
POST /api/device/<UUID>
{
    "vars" : {
        <VAR_NAME> : value,
        ...
    }
}
POST /api/device/d5c827d2-1ba7-4da6-b87b-0864f0969fa8
{
    "vars" : {
        "temperature" : 43.0,
        "humidity" : 32.41,
        "dimmer_level" : 4,
        "happy" : true
        ...
    }
}

The datatype of the value must match the datatype of the Cloud Variable.

This request may be combined with Update Device Properties and Declare Cloud Variable into a single request.

Response
The remote responds with the entire Device object. See Device Object Response.
Device Object Response
The same response is sent for the following endpoints:
GET /api/device/<UUID>
GET /api/device/self
POST /api/device/<UUID>
POST /api/device/self
Success Response
REST
EXAMPLE
{
    "result" : "ok",
    "device_id" : <UUID>,
    "friendly_name" : <FRIENDLY_NAME>,
    "status" : {
        "ws_connected" : <WS_CONNECTED>,
        "active_status" : <ACTIVE_STATUS>,
        "last_activity_time" : <LAST_ACTIVITY_TIME>,
    },
    "var_decls" : { <VAR_DECLS> },
    "vars" : { <VARS> },
}
{
    "result" : "ok",
    "device_id" : "d5c827d2-1ba7-4da6-b87b-0864f0969fa8",
    "friendly_name" : "My Toaster 17",
    "status" : {
        "ws_connected" : true,
        "active_status" : "active",
        "last_activity_time" : 1426803897000000,
    },
    "var_decls" : {
        "out float32 temperature" : { },
        "out float32 humidity" : { },
        "in int8 dimmer_brightness" : { },
        "in bool reboot_now" : { },
    },
    "vars" : {
        "temperature" : {
            "t" : 1426803897000000,
            "v" : 37.4,
        },
        "humidity" : {
            "t" : 1426803897000000,
            "v" : 92.3,
        },
        "dimmer_brightness" : {
            "t" : 1426803897000000,
            "v" : 0,
        },
        "reboot_now" : {
            "t" : 1426803897000000,
            "v" : false,
        }
    }
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 Non-JSON payload; Required fields missing or incorrect type; Input validation failure; Attempt to change an existing cloud variable's declaration.
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
url_not_found Error 404 Device with <UUID> does not exist or you have no access to it.
internal_error Error 500 Internal error occurred
Organizations
Create an Organization
Request
This endpoint is only available to authenticated Users. Devices cannot create Organizations.
REST
EXAMPLE
CURL
POST /api/create_org
{
    "name" : string,
}
POST /api/create_org
{
    "name" : "Planet-Express",
}
curl -d '{ "name" : "Planet-Express" }' http://sandbox.canopy.link/api/create_org
Field Required Description Validation
"name" Required Organization name string
  • 5-24 characters
  • Characters a-z, A-Z, 0-9 and underscore(_) allowed.
  • Must start with a letter
  • Shares namespace with User names
Organization names share namespace with usernames.
Success Response
REST
EXAMPLE
200 OK
{
    "name" : <ORG_NAME>,
    "result" : "ok",
}
200 OK
{
    "name" : "Planet-Express",
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 Non-JSON payload; Required fields missing or incorrect type; Input validation failure.
email_taken Error 400 Email not available
username_taken Error 400 Username not available
internal_error Error 500 Internal error occurred
Delete an Organization
Request
This operation is only available to the Organization's Owner(s).
REST
EXAMPLE
CURL
DELETE /api/org/<ORG_NAME>
DELETE /api/org/Planet-Express
curl -X DELETE http://sandbox.canopy.link/api/org/Planet-Express
Field Required Description Validation
"name" Required Organization name string
  • 5-24 characters
  • Characters a-z, A-Z, 0-9 and underscore(_) allowed.
  • Must start with a letter
  • Shares namespace with User names
Organization names share namespace with usernames.
Success Response
REST
EXAMPLE
200 OK
{
    "name" : <ORG_NAME>,
    "result" : "ok",
}
200 OK
{
    "name" : "Planet-Express",
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
url_not_found Error 404 Organization with <ORG_NAME> does not exist or you have no access to it.
internal_error Error 500 Internal error occurred
List Organizations
Request
To obtain a list of all Organizations the current User is a member of:
GET /api/user/self/orgs
Success Response
REST
EXAMPLE
200 OK
{
    "orgs" : [
        <ORG_NAME_0>,
        <ORG_NAME_1>,
        ...
    ],
    "result" : "ok",
}
200 OK
{
    "orgs" : [
        "Planet-Express",
        "MomCorp",
    ]
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
List Members
Request
To obtain a list of all members of an Organization:
GET /api/org/<ORG_NAME>/members

Only members of the organization are authorized to make this request.

Success Response
REST
200 OK
{
    "members" : [ {
        "username" : <USERNAME>,
        "email" : <EMAIL>,
        "teams" : [ <TEAM_0>, <TEAM_1>, ... ]
    }, ...
    ],
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
Add Member
Request
To add a member to an Organization:
POST /api/org/<ORG_NAME>/members
{
    "add_member" : {
       "user" : <USERNAME_OR_EMAIL>,
    }
}

Only owners of the Organization are authorized to make this request.

Success Response
REST
200 OK
{
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 No User found with provided username or email address.
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
Remove Member
Request
To remove a member from an Organization:
POST /api/org/<ORG_NAME>/members
{
    "remove_member" : <USERNAME_OR_EMAIL>,
}

This request will also remove the member from all of the Organization's Teams that they are on.

Only owners of the Organization are authorized to make this request.

This request may be combined with an Add Member request, in which case addition occurs before removal.

Success Response
REST
200 OK
{
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 No User found with provided username or email address.
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
List Teams
Request
To list the Teams within an organization:
GET /api/org/<ORG_NAME>/teams

Only members of the Organization are authorized to make this request.

Success Response
REST
200 OK
{
    "teams" : [
        {
            "name" : <TEAM_NAME>,
            "url_alias" : <TEAM_URL_ALIAS>
        },
        ...
    ]
}
Error Response
This request may return the following errors:
Error Response Code Reason
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
url_not_found Error 404 Organization with <ORG_NAME> does not exist or you have no access to it. Team with <TEAM_URL_ALIAS> does not exist within this organization.
internal_error Error 500 Internal error occurred
Create a Team
Request
To create a Team within an organization:
POST /api/org/<ORG_NAME>/create_team
{
    "name" : <TEAM_NAME>
    "url_alias" : <TEAM_URL_ALIAS>
}

Only owners of the Organization are authorized to make this request.

Success Response
REST
200 OK
{
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 Invalid team name
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
Delete a Team
Request
To delete a Team:
DELETE /api/org/<ORG_NAME>/team/<TEAM_URL_ALIAS>

Only owners of the Organization are authorized to make this request.

Success Response
REST
200 OK
{
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
url_not_found Error 404 Organization with <ORG_NAME> does not exist or you have no access to it. Team with <TEAM_URL_ALIAS> does not exist within this organization.
internal_error Error 500 Internal error occurred
List Team Members
Request
To obtain a list of all members of Team within an Organization:
GET /api/org/<ORG_NAME>/team/<TEAM_URL_ALIAS>/members

Only members of the organization are authorized to make this request.

Success Response
REST
200 OK
{
    "members" : [ {
        "username" : <USERNAME>,
        "email" : <EMAIL>,
    }, ...
    ],
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
Add Member To Team
Request
To add an organization member to a Team:
POST /api/org/<ORG_NAME>/team/<TEAM_URL_ALIAS>/members
{
    "add_member" : {
       "user" : <USERNAME_OR_EMAIL>,
    }
}

Only owners of the Organization are authorized to make this request.

Only members of the Organization may be added to the Team.

Success Response
REST
200 OK
{
    "result" : "ok",
}
Error Response
This request may return the following errors:
Error Response Code Reason
bad_input Error 400 No User found with provided username or email address.
not_authenticated Error 401 Missing credentials: not logged in and missing BASIC AUTH
internal_error Error 500 Internal error occurred
Other Endpoints
Get Server Info
Provides some details about the server, including which version of Canopy it is running and how it is configured.
Request
REST
CURL
GET /api/info
curl https://sandbox.canopy.link/api/info
Anyone can make this request (no authentication needed).
Success Response
REST
EXAMPLE
200 OK
{
    "result" : "ok",
    "service-name" : "Canopy Cloud Service",
    "version" : string,
    "clock_us" : uint64,
    "clock_utc" : string,
    "config" : {
        "allow-anon-devices" : boolean,
        "allow-origin" : string,
        "email-service" : string,
        "enable-http" : false,
        "enable-https" : true,
        "forward-other-hosts" : string,
        "hostname" : string,
        "http-port" : int,
        "https-cert-file" : string,
        "https-port" : int,
        "https-priv-key-file" : string,
        "js-client-path" : string,
        "log-file" : string,
        "sendgrid-username" : string,
        "web-manager-path" : string
    }
}
200 OK
{
    "result" : "ok",
    "service-name" : "Canopy Cloud Service",
    "version" : "0.9.2-beta"
    "clock_us" : 1426772581829000,
    "clock_utc" : "2015-03-19 22:47:31.893205,
    "config" : {
        "allow-anon-devices" : true,
        "allow-origin" : "",
        "email-service" : "sendgrid",
        "enable-http" : false,
        "enable-https" : true,
        "forward-other-hosts" : "",
        "hostname" : "dev02.canopy.link",
        "http-port" : 80,
        "https-cert-file" : "/etc/canopy/cert.pem",
        "https-port" : 443,
        "https-priv-key-file" : "/etc/canopy/key.pem",
        "js-client-path" : "/home/gregp/development/canopy/canopy-js-client",
        "log-file" : "/var/log/canopy/server.log",
        "sendgrid-username" : "canopy-mailer",
        "web-manager-path":  "/home/gregp/development/canopy/canopy-app"
    },
}
Field Description
version Version of the canopy server as a string. For example "0.9.2-beta".
clock_us Server time in microseconds since the Unix Epoch.
clock_utc Server time reported as an RFC3339-encoded UTC datetime string
config Server configuration settings. For details about the "config" fields, see Canopy Server: Configuration Settings.
Error Response
This request may return the following errors:
Error Response Code Reason
internal_error Error 500 Internal error occurred
Error Responses
Success & Error Reporting
A successful API call will have the 200 OK response code and include "result" : "ok" in the response:
200 OK
{
    "result": "ok",
    ...
}

An unsuccesful API call will have a 4XX or 4XX response code and include "result" : "error" in the response. The response will also include an "error_type" field along with additional details about the error if available.

bad_input Error
The "bad_input" error response is sent when request payload validation fails. For example:
400 BAD REQUEST
{
    "result" : "error",
    "error_type" : "bad_input",
    "error_msg" : "String \"username\" expected"
}
email_taken Error
The "email_taken" error response is sent when creating a user or changing a user's email address if the requested email address is already in use by a different user. The response is:
400 BAD REQUEST
{
    "result" : "error",
    "error_type" : "email_taken",
}
incorrect_username_or_password Error
The "incorrect_username_or_password" error response is sent when the provided credentials are incorrect. The response is:
401 UNAUTHORIZED
{
    "result" : "error",
    "error_type" : "incorrect_username_or_password",
}
not_authenticated Error
The "not_authenticated" error response is sent if the request is missing required authentication credentials. The response is:
401 UNAUTHORIZED
{
    "result" : "error",
    "error_type" : "not_authenticated",
}
internal_error Error
The "internal_error" error response is sent when an error outside the user's control has occurred. The response may include an "error_details" field containing further details about the error. If a panic/crash occurred, the response may include an "error_callstack" field showing the callstack. For example:
500 INTERNAL SERVER ERROR
{
    "result" : "error",
    "error_type" : "internal_error",
    "error_msg" : "Unable to connect to database",
    "error_details" : "session has been closed",
}
username_taken Error
The "username_taken" error response is sent when creating a user if the requested username is already in use by a different user. The response is:
400 BAD REQUEST
{
    "result" : "error",
    "error_type" : "username_taken",
}
url_not_found Error
The "url_not_found" error response is sent if the requested URL was not found. This may also occur if the requester does not have permission to access the requested resource, in which case this response is more secure than a 403 FORBIDDEN response because it does not leak information about the existance of an entity. The response is:
404 NOT FOUND
{
    "result" : "error",
    "error_type" : "url_not_found",
}