evrsync API

Version 1.15.7

0. Overview

The base url for the api is https://evrsync.evrbit.com/api

All times are represented as integer values of unix timestamps (seconds since epoch, utc).

In general, unless they return data, the api call replies will contain no body, and formally content type text/plain; with status code/errors in headers.

In case of errors, calls will respond with status codes in the 4xx range and may set x-error headers with diagnostic information. See appendix A. In general, error 400 is reserved for parameter or data errors, and 401 for missing, incorrect or expired login/authorisation/session parameter.

All objects that can be published must be unpublished to be modified. Attempting to modify a published object will return an error, see appendix A.

0.1 Kleinhirn mode

The system runs in kleinhirn mode on servers whose hostname starts with 'kleinhirn'.

In kleinhirn mode it is only possible to modify data of projects that do not have their 'is_mirror' flag set. In particular it is not possible to directly add projects or users.

Projects or users must be added on the central evrsync server and then the kleinhirn synchronised with it by data update.

Note that any locally created project data will be lost on update if the project-kleinhirn assignment is set to 'is_mirror' on the central server. On the other hand, a kleinhirn can mirror a project, and then be set to local data, and will keep the mirrored data. This functionality reacts badly to interrupted update processes though.

0.2 Client API access

Normally client devices do not login into the API and only use a handful of project specific API calls:

Clients should evaluate status codes of the requests usefully. A status 401 indicates that the project requires a check-in and will not go away by itself. Other non-20x status may be transient errors, especially in the 5xx range. See call descriptions and Appendix A for the possible error conditions that should be handled. In most cases, a retry loop is appropriate.

1. Users

Users are created by an existing administrator user.

Users are only relevant for the management functionality.

Any request that accepts a session_id will refresh the session. Any request with a session_id may be declined with status 401 if the session expired. A client may implement a "remember me" or "keep logged in" functionality by re-issuing a /login call in the background; or it may require user input.

/activateaccount

POST or GET request. disallowed on kleinhirn.

parameter type required description
key string yes activation key from confirmation mail

Activates the user account specified by the key. Replies with status 302 and pre-configured feedback page in location header on success. Replies with status 303 and pre-configured error page in location header on error.

/changepassword

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid (admin) session id
user_id int no set password of given user
password string (no) current password
newpassword string yes new password

Replies with status 204 on success.

If called from an admin session, allows to set the password of user given by user_id; in that case the password is ignored.

Outside of an admin session, requires the current password. The client app should require the user to re-enter the current password to guard against malicious unattended-device manipulation.

/getprofiledata

GET request

parameter type required description
session_id bigint yes valid (admin) session id
id uint no user id

Returns profile data for the current user.

If in admin session and id is given, retrieves profile data for that user.

The profile data is returned as a json data structure:

{
"id"=<user id>,
"email"=<login email>,
"name"=<screen name>,
"is_admin"=1|0
"is_active"=1|0
"cfullname"=...
"cemail"=...
"cphone"=...
"cmobile"=...
...
}

Note that if no user is found this also returns status 401.

/login

POST request.

parameter type required description
email email yes email address. not checked for correctness.
password string yes

Replies with status 204 on success.

reply header type description
x-session-id string Session id for subsequent user requests

/logout

POST request

parameter type required description
session_id bigint yes valid session id

Replies with status 204 on success. Terminates the session immediately.

/passwordreset

POST request. disallowed on kleinhirn.

parameter type required description
email email yes email used to login

Replies with status 204 on success. Generates and sends a mail with a password-reset-confirmation link.

/passwordresetconfirm

POST or GET request. disallowed on kleinhirn.

parameter type required description
key string yes activation key as printed in the confirmation mail

Replaces the password for the user matching the key with a random string and sends it to the user's mail. Replies with status 302 and pre-configured feedback page in location header on success. (In dev mode status 200 and mail body). Replies with status 303 and pre-configured error page in location header on error.

/setprofiledata

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid (admin) session id
user_id int no admin only: edit user
email string no new login email
name string no new screen name
is_active int no 1/0, admin only
is_admin int no 1/0, admin only
contact field string no sets given contact info field

If called from an admin session, allows to edit the data of the user given by user_id. Only in that case can is_active and is_admin be set.

Sets new values for any of the parameters if they are present in the request. If not in an admin session with explicit user_id, sets the values for the current user.

name is the public screen name and for the sake of readibility basic sanity rules are enforced: it must start with a letter and may contain only letters, numbers and a limited set of other characters. It must be between 4 and 30 characters long. See validity regex: [a-zA-Z][a-zA-Z0-9 ._@-]{3,19}. The name must be unique and the method will reply with status 400 and x-error header '1 name not unique' if that is not the case.

contact field can be any of the following:

field name length description
cfullname 50 full name
cemail 50 email
ccompany 50 company
cphone 20 phone
cmobile 20 mobile
cstreet 50 address: street
czip 10 address: post code
ccity 30 address: city
ccountry 30 address: country

Replies with status 204 on success.

2. User Admin

Functions for user and user/project permission management.

All calls are secure-only and require a session_id logged in with an user with the is_admin flag set.

/createsession

POST request.

parameter type required description
session_id string yes valid admin session id
id uint yes internal user id

Creates a session linked to the given user.

Replies with status 204 on success.

reply header type description
x-session-id string Session id for the requested user.

FIXME missing in UI?

/createuser

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
email string yes login email for the new user
password string no initial password

Replies with status 204 on success.

Creates a new inactive user. If no password is given as parameter, a new random password is generated. Sets x-user-id to the new user's row id.

Generates and sends a mail to the given email with the password and an invitation-confirmation link.

(Note, when echo_mail is set to True for development, replies instead with 200 and the mail bod.y)

Replies with status 400 and x-error 2 if email is already in use.

/deleteuser

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
id uint yes user to delete

Deletes the user and all linked data. Returns 204 on success.

/listusers

GET request

parameter type required description
session_id string yes valid admin session id
email string no login email filter
name string no screen name filter
is_active int no account activated flag
is_admin int no 1/0 admin flag filter
cfullname string no full name filter
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

Lists selected users.

If filter is given, and specifies a text column, the filtering is with a LIKE %filter_val% search.

If sort/sort_dir is given, the list is sorted accordingly, otherwise by user id ascending.

Reply is JSON-encoded list of objects [{ "id"=, "created_at"=, ... }, ...]

field type description
id int internal user id
created_at int timestamp of user creation
updated_at int timestamp of last update
email string login email
name string screen name
is_admin int admin flag
is_active int admin flag
cfullname string full name from contact details
reply header type description
x-count int total result count

TODO unittest

3. Projects

Projects are customer-specific evrsync deployments.

Projects can only be created and assigned to users by an admin user. Projects are created empty except for the configured default logo/background/test movie.

Only admin users can set project-specific license limits: number of simultaneous devices, number of movies and project expiration date; and allowed client software.

Projects are only accessible outside of a session (by client devices) if they are published. Projects must have a valid license to be published. A background job depublishes projects whose license expires.

The maximum number of allowed client devices can be configured. Normally clients are counted on each data access implicitely, but they can be forced to check in to the project with a user-input password.

/addprojectsoftware

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
project_id uint yes project id
software_id uint yes software id

Replies with status 204 on success and sets x-project_software-id to the new row id.

Adds software/project link.

Replies with status 400 and x-error 6 if the project/software link already exists.

/addprojectuser

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
project_id uint yes project id
user_id uint yes user id

Replies with status 204 on success and sets x-project_user-id to the new row id.

Adds user/project access link.

Replies with status 400 and x-error 6 if the project/user access already exists.

/createproject

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
code string(20) yes internal codename.
name string(50) no project display name

Replies with status 204 on success and sets x-project-id to the new project id.

Creates a new project with default logo and background, a default test movie and a default testing license. The testing license limits are configured in settings, and by default are: 2 clients, 2 movies and 400mb of storage (one average movie).

If no name is given, uses code as name. Code will be used in various internal symbolic ids and may therefore consist only of lower letters and digits, and may not begin with a digit.

Replies with status 400 and x-error 5 if code is already in use.

/deleteproject

POST request. disallowed on kleinhirn. disallowed for locked projects.

parameter type required description
session_id bigint yes valid admin session id
id uint yes project id

Replies with status 204 on success.

Deletes the project and all linked data.

Replies with status 400 and x-error 12 if project not found.

/deleteprojectsoftware

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
project_id uint yes project id
software_id uint yes software id

Replies with status 204 on success.

Removes software/project link.

Replies with status 400 and x-error 20 if the project/software link does not exist.

/deleteprojectuser

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
project_id uint yes project id
user_id uint yes user id

Replies with status 204 on success.

Removes user/project access link.

Replies with status 400 and x-error 7 if the project/user access does not exist.

/editproject

POST request. disallowed on kleinhirn for mirrored projects. disallowed for locked projects.

parameter type required description
session_id bigint yes valid (admin) session id
id uint yes project id
code string(20) no internal codename.
name string(50) no project display name
is_checkin_required int no 1 or 0
password string no new password. empty string to disable (no checkin possible)
background uint no id of project image to use as default background
logo_position string no empty or logo position as csv list of floats: x,y,z
logo_rotation string no empty or logo rotation as csv list of floats: u,v,w
logo_blend string no empty or type of logo blend: "alpha", "add", "multiply"
text_color string no empty text color as csv list of floats 0-1: r,g,b,a
aux string no free text

Replies with status 204 on success.

Requires a user with access to the project, or admin user.

Updates project data.

Requires admin session to change code. Code should not be changed lightly, as it is used to address the project by client devices.

Setting the is_checkin_required flag or changing the password while this flag is set removes all present non-blocked client-project associations.

Note: to update the project logo use /uploadimage on the image generated on project creation.

Replies with status 400 and x-error 5 if code is already in use. Replies with status 400 and x-error 12 if no project found.

/getproject

GET request

parameter type required description
session_id string yes valid (admin) session id
id uint no project id

Returns project data for given id, only if the session user has access to it.

Reply is JSON-encoded project data

{
    "id": <project id>,
    "code": "<project code>",
    "name": "<project name>",
    "is_published": 1/0,
    "is_checkin_required": 1/0,
    "has_password": 1/0,
    "max_movies": <max movies as allowed by active licenses>,
    "max_clients": <max devices as allowed by active licenses>,
    "storage_quota": <storage quota in mb as allowed by active licenses>,
    "logo": <logo image id>,
    "background": <default background image id>,
    "logo_position": "<logo position vector as csv list: x,y,z>"
    "logo_rotation": "<logo rotation vector as csv list: u,v,w>",
    "logo_blend": "<logo blend mode>",
    "text_color": "<text color tuple as csv list: r,b,g,a>",
    "aux": "<additional skinning data>",
    "created_at": <timestamp>,
    "updated_at": <timestamp>,
    "is_visible": <1 if project is published and has valid licenses>,
    "is_mirror": <kleinhirn mirror flag, only on a kleinhirn>,
    "lock": <null or id of locking job>
}

since the password is write-only, only has_password is set to indicate whether the project uses a password or not.

is_visible is set if the project is published and has at least one valid license.

/listprojects

GET request

parameter type required description
session_id string yes valid (admin) session id
user_id uint no user id
kleinhirn_id uint no kleinhirn id
software_id uint no software id
is_published int no 1/0 published filter
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit or 0 for all

When id is given, and the session is an admin session, lists projects the user id can access.

When no id is given, lists all projects the session user can access (all if admin).

If kleinhirn_id is given, lists only projects linked to the given kleinhirn. In that case the returned data includes is_mirror.

If software_id is given, lists only projects that use the given software.

If sort/sort_dir is given, the list is sorted accordingly, otherwise by project name ascending.

sort can be one of 'code', 'name', 'created_at', 'updated_at'. sdir can be one of 'asc', 'desc'.

Reply is JSON-encoded list of objects:

[{
    "id": <project id>,
    "code": "<project code>",
    "name": "<project name>",
    "is_published": 1/0,
    "is_checkin_required": 1/0,
    "has_password": 1/0,
    "max_movies": <max movies as allowed by active licenses>,
    "max_clients": <max devices as allowed by active licenses>,
    "storage_quota": <storage quota in mb as allowed by active licenses>,
    "storage_used": <currently used storage in mb>,
    "logo": <logo image id>,
    "background": <default background image id>,
    "logo_position": "<logo position vector as csv list: x,y,z>"
    "logo_rotation": "<logo rotation vector as csv list: u,v,w>",
    "logo_blend": "<logo blend mode>",
    "text_color": "<text color tuple as csv list: r,b,g,a>",
    "aux": "<additional skinning data>",
    "created_at": <timestamp>,
    "updated_at": <timestamp>,
    "is_visible": <1 if project is published and has valid licenses>,
    "is_mirror": <kleinhirn mirror flag, only with _kleinhirn_id_>,
    "lock": <null or id of locking job>
},
...]
reply header type description
x-count int total result count

/listprojectsoftware

GET request

parameter type required description
session_id string yes valid (admin) session id
id uint yes project id
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

Returns list of software configured for the project.

Paging and sorting is included for api uniformity, we do not expect a project to have more than a handful or softwares assigned.

sort can be one of 'label', 'platform', 'created_at', 'updated_at'. sdir can be one of 'asc', 'desc'.

Reply is JSON-encoded list of objects:

[{
    (... see /listsoftware ...)
},
...]

For non-admin users returns only published software.

/listprojectusers

GET request

parameter type required description
session_id string yes valid (admin) session id
id uint yes project id
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

Returns list of users that can access the project.

sort can be one of 'email', 'name', 'created_at', 'updated_at'. sdir can be one of 'asc', 'desc'.

Reply is JSON-encoded list of objects:

[{
    (... see /getprofiledata ...)
},
...]

/project

parameter type required description
id uint (yes) project id
code str(20) (yes) project code
session_id bigint (yes) valid session id
device_name string(36) (yes) client device name
version string no project data format version

Requires either session_id or device_name. When accessed with a device name:

When accessed with a session_id, simply returns the project data. Note that in that case the id is required.

Generates json index of published project data.

The output format can be selected via version. Omitting the version parameters selects the latest format. Currently defined formats are:

See Appendix B of the API documentation for detailed format description.

/publishproject

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked projects.

parameter type required description
session_id bigint yes valid (admin) session id
id uint yes project id
is_published int yes 1/0

Replies with status 204 on success.

Requires a user with access to the project, or admin user.

Publishes/unpublishes the project and its logo.

Returns x-error 33 if the project has no logo or default background.

Returns x-error 16 if the project's background image is not published.

A project must be set to published for its data to be available in /project

4. Licenses

Licenses define limits and availability for projects.

A project is only accessible by client devices if it is published and has a valid license.

Licenses control the number of devices allowed to check-in to the project, the number of movies (as media) allowed, and the storage quota.

Note that storage quota is a value in MB, with MB being the commonly used million bytes, not the technical MiB.

Licenses can be limited to a time interval.

Multiple licenses can be active for a project. In that case the currently highest limits for movies, devices and quota are applied. This way licenses can be separated by purpose.

Normally projects are created with a default testing license, see /createproject.

Licenses can only be managed by admin users.

/createlicense

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
id uint yes project id

Replies with status 204 on success and sets x-license-id to the new project id.

Creates a new license for given project.

/deletelicense

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
id uint yes license id

Replies with status 204 on success.

Deletes the license row. This may make a project invisible.

Replies with status 400 and x-error 12 if license not found.

/editlicense

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
id uint yes license id
label string(50) no display name
max_clients int no max client devices
max_movies int no max movies
storage_quota int no storage quota in mb (million bytes)
valid_from int no valid from timestamp or -1 to reset
valid_to int no valid to timestamp or -1 to reset

Replies with status 204 on success.

Updates license data.

/getlicense

GET request

parameter type required description
session_id string yes valid session id
id uint no license id

Returns license data for given id, if the user has access to its project.

Reply is JSON-encoded project data

{
    "id"=<license id>,
    "project_id"=<project id>,
    "label"=<display name>,
    "max_clients"=<max client devices>,
    "max_movies"=<max movies>,
    "storage_quota"=<storage quota in mb>,
    "valid_from"=<null or start of validity interval>,
    "valid_to"=<null or end of validity interval>,
    "is_valid"=1/0,
    "created_at"=<timestamp>,
    "updated_at"=<timestamp>
}

/listlicenses

GET request

parameter type required description
session_id string yes valid (admin) session id
project_id uint no project filter
is_valid int no 1/0 validity filter
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

When project_id is not given, requires an admin session. Otherwise, requires the current user to have access to the project.

When project_id is given, lists licenses for that project. Otherwise lists all licenses.

If sort/sort_dir is given, the list is sorted accordingly, otherwise by project name ascending.

sort can be one of 'label', 'created_at', 'updated_at'. sdir can be one of 'asc', 'desc'.

Reply is JSON-encoded list of objects:

[{
    (see /getlicense)
},
...]
reply header type description
x-count int total result count

5. Client devices

Lists client devices checked-in to projects.

Devices can be checked-in implicitely on first project access, as long as the project has less than current licence's max_clients devices checked-in; or they need to explicitely /checkin if the project is password-protected and explicit checkin is required.

/blockprojectdevice

POST request

parameter type required description
session_id bigint yes valid (admin) session id
id uint yes project id
device_name string(36) yes client device name

Replies with status 204 on success.

Sets is_blocked on the selected client in the project.

Replies with status 400 and x-error 11 if the client/project link does not exist or is already blocked.

/checkin

POST request

parameter type required description
id uint (yes) project id
code str(20) (yes) project code
device_name string(36) yes client device name
password string yes project password

Either project id or project code is required.

Check in a client device to a project that requires checkin with password. Note that the project still needs to be published, and within its validity interval. Note also that the checkin can still fail with the correct password if the project is at the client limit.

Replies with status 204 on success. 401 without further error if login fails. Also replies with status 204 if already checked in.

/deleteprojectdevice

POST request

parameter type required description
session_id bigint yes valid (admin) session id
id uint yes project id
device_name string(36) yes client device name

Replies with status 204 on success.

Removes the selected client/project association.

Replies with status 400 and x-error 12 if the link does not exist.

/listprojectdevices

GET request

parameter type required description
session_id string yes valid (admin) session id
id uint yes project id
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

Returns list of clients associated with the project sorted by device_name.

sort can be one of 'device_name', 'is_blocked', 'created_at', 'updated_at'. sdir can be one of 'asc', 'desc'.

Reply is JSON-encoded list of objects:

[{
    "id"=<row id>,
    "project_id"=<project id>,
    "device_name"="<client device name>",
    "is_blocked"=1/0,
    "created_at"=<timestamp>,
    "updated_at"=<timestamp>
}, {...}, ...]

6. Client device software

Manages version information and binary data or download link for client device software applications.

Generally a project uses one or more target platforms, p.ex. android_mono, android_gear, android_cardboard, windows, macos; and optionally controllers for a subset of those platforms, p.ex. macos_ctl, android_mono_ctl.

Software is either general or feature-specific evrsync software, or project-specific custom software. Either can be used in any project. Only admin users can assign software to a project.

/createsoftware

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id

Replies with status 204 on success and sets x-software-id to the new software id.

Creates a new empty software with a default thumbnail.

/deletesoftware

POST request. disallowed on kleinhirn. disallowed for locked software.

parameter type required description
session_id bigint yes valid admin session id
id uint yes software id

Replies with status 204 on success.

Deletes the software and its datafile(s).

Replies with status 400 and x-error 12 if software not found.

/editsoftware

POST request. disallowed on kleinhirn. disallowed for locked software.

parameter type required description
session_id bigint yes valid admin session id
id uint yes software id
platform str(20) no
build_type str(50) no
version str(40) no
filename str(100) no
label str(50) no
description str(250) no
download_url str(250) no
is_custom int no 1/0

Updates software data.

Software may not be published to be edited.

Note that build_type is used by the automatic update check. it must be empty or one of the values defined in the build_app.sh script.

Replies with status 204 on success.

/getsoftware

GET request

parameter type required description
session_id string yes valid admin session id
id uint no software id

Returns software metadata for given id.

{
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    binary_file INT UNSIGNED NULL,
    platform VARCHAR(20) NOT NULL COMMENT 'Target system',
    build_type VARCHAR(50) NULL COMMENT 'Build type for automatic update check',
    version VARCHAR(20) NOT NULL COMMENT 'Software version/release tag',
    filename VARCHAR(100) NOT NULL COMMENT 'Filename',
    label VARCHAR(50) NOT NULL COMMENT 'Human-readable label',
    description VARCHAR(250) NULL COMMENT 'Optional detailed description',
    thumbnail INT UNSIGNED NULL COMMENT 'Thumbnail',
    download_url VARCHAR(250) NULL COMMENT 'External download url',
    is_custom TINYINT NOT NULL DEFAULT 0 COMMENT 'Project/Feature-specific build',
    is_published TINYINT NOT NULL DEFAULT 0 COMMENT 'Active/published',
    created_at INT UNSIGNED NOT NULL COMMENT 'Object creation time, as timestamp',
    updated_at INT UNSIGNED NOT NULL COMMENT 'Last change timestamp',
    lock: <0 or id of locking job>
}

/listsoftware

GET request

parameter type required description
session_id string yes valid (admin) session id
platform str(20) no exact filter
has_download int no 1/0 has external download url
has_datafile int no 1/0 has uploaded binary
is_custom int no 1/0 filter
is_published int no 1/0 filter
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

Returns list of software as filtered.

sort can be one of 'label', 'platform', 'created_at', 'updated_at'. sdir can be one of 'asc', 'desc'.

Reply is JSON-encoded list of objects:

[{
    (... see /getsoftware ...)
},
...]

/publishsoftware

POST request. disallowed on kleinhirn. disallowed for locked software.

parameter type required description
session_id bigint yes valid admin session id
id uint yes software id
is_published int yes 1/0

Publishes or unpublishes software.

Can only be published if either datafile or download_url is defined, and label/version/filename is not empty, and a thumbnail exists.

Replies with status 204 on success.

/uploadsoftware

POST request with special formatting. disallowed on kleinhirn. disallowed for locked software.

Requires its parameters either as cookies or as request headers, and the upload data as binary body post data. Creates a new job and updates it while processing upload data. Once the upload starts the job status can be fetched via /getjob.

cookie header type required description
session_id x-session-id bigint yes valid admin session id
id x-id uint yes software id
filename x-filename str(100) yes original filename

Note that ContentLength of the upload must be correctly set to size of uploaded file, and ContentType should be set to 'application/octet-stream'. The original filename must be url-encoded if it contains non-ascii characters.

Replies with status 204 and x-datafile-id on success.

7. Kleinhirn management

Kleinhirne are local-lan mirrors of the evrsync server. They are generally rented out for local event management.

Kleinhirn data is managed on the evrsync central server. Kleinhirne are assigned to projects and will then synchronise or mirror the project data on selfupdate.

Kleinhirn management and project assignment is admin-only.

Local klenhirn instances can be configured to mirror a project, making it read-only on the kleinhirn and synchronising completely; or they can be allowed to have local data, where the kleinhirn will synchronise data from the main server, but also allow local media upload. Note that even in kleinhirn instances that have local data, the local data will be overwritten if it is a modification of server data and the server data is updated.

/addkleinhirnproject

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
project_id uint yes project id
kleinhirn_id uint yes kleinhirn id

Replies with status 204 on success and sets x-kleinhirn_project-id to the new row id.

Adds kleinhirn/project link, initially with is_mirror set.

Replies with status 400 and x-error 22 if the project/kleinhirn link already exists.

/createkleinhirn

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id

Replies with status 204 on success and sets x-kleinhirn-id to the new kleinhirn row id.

Creates a new empty kleinhirn. If an unnamed kleinhirn exists already,

/deletekleinhirn

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
id uint yes kleinhirn id

Replies with status 204 on success.

Deletes the kleinhirn.

Replies with status 400 and x-error 12 if kleinhirn not found.

/deletekleinhirnproject

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
project_id uint yes project id
kleinhirn_id uint yes kleinhirn id

Replies with status 204 on success.

Removes kleinhirn/project link.

Replies with status 400 and x-error 23 if the project/kleinhirn link does not exist.

/editkleinhirn

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
id uint yes kleinhirn id
type str(20) no
hardware_spec str(250) no
mac_addr str(17) no
hostname str(20) no
has_audio_sync int no 1/0
status str(50) no
notes str() no note practically limited by browsers

Updates kleinhirn data.

Replies with status 204 on success.

/editkleinhirnproject

POST request. disallowed on kleinhirn.

parameter type required description
session_id bigint yes valid admin session id
project_id uint yes project id
kleinhirn_id uint yes kleinhirn id
is_mirror int no 1/0

Replies with status 204 on success.

Modifies kleinhirn/project link settings.

/getkleinhirn

GET request

parameter type required description
session_id string yes valid admin session id
id uint no kleinhirn id

Returns kleinhirn metadata for given id.

{
    'id': <kleinhirn id>,
    'type': <hardware type short desc>,
    'hardware_spec': <hardware spec text>,
    'mac_addr': <mac address of wifi interface>,
    'hostname': <Hostname (without domain part)>,
    'has_audio_sync': <1/0 audio sync enabled flag>,
    'status': <Current disposition, text description>,
    'notes': <free text entry>,
    'last_updatecheck': <timestamp of last update check>,
    'system_version': <system version installed>,
    'khadmin_version': <kleinhirn admin version installed>,
    'evrsync_version': <evrsync version installed>,
    'created_at': <creation timestamp>,
    'updated_at': <last update timestamp>,
}

/kleinhirndata

GET request

parameter type required description
kleinhirn_name string yes kleinhirn hostname
system_version string no installed system version
khadmin_version string no kleinhirn admin version
evrsync_version string no evrsync version

Note that this api call must be locked behind server-side http auth. The server must translate the authenticated user name into the value of the kleinhirn_name parameter. See the default nginx-server.conf.

Returns list of data relevant to the kleinhirn, emulating output format of

mysqldump --defaults-file=code/tools/mysqldump.conf            --no-create-info --hex-blob --skip-add-locks evrsync

If any of the data is locked, returns status 400 with x-error 42.

Stores the time of the call, and the *_version values if given.

/listkleinhirn

GET request

parameter type required description
session_id string yes valid (admin) session id
project_id int no project filter
type str(20) no type filter
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

Returns list of kleinhirn as filtered.

sort can be one of 'type', 'hostname', 'created_at', 'updated_at', 'system_version', 'khadmin_version', 'evrsync_version', 'last_updatecheck'. But note that the version numbers are compared as strings not as logical version numbers.

sdir can be one of 'asc', 'desc'.

Reply is JSON-encoded list of objects:

[{
    'id': <kleinhirn id>,
    'type': <hardware type short desc>,
    'hardware_spec': <hardware spec text>,
    'mac_addr': <mac address of uplink interface>,
    'hostname': <Hostname (without domain part)>,
    'has_audio_sync': <1/0 audio sync enabled flag>,
    'status': <Current disposition, text description>,
    'notes': <free text entry>,
    'last_updatecheck': <timestamp of last update check>,
    'system_version': <system version installed>,
    'khadmin_version': <kleinhirn admin version installed>,
    'evrsync_version': <evrsync version installed>,
    'created_at': <creation timestamp>,
    'updated_at': <last update timestamp>,
    'is_mirror': <1|0, only present if filtered by project>
},
...]

8. Project Media

Represents sync-able audiovisual media in a project. Usually it's a (optionally multilanguage) movie.

Media can also be declared to be 2D content and ordered into a slideshow, the order of which is shared with images.

/createmedia

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked projects.

parameter type required description
session_id bigint yes valid session id
id uint yes project id

Replies with status 204 on success and sets x-media-id to the new media id.

If the project already has reached max_movies (see license) media, return status 401 with x-error 30.

Creates a new empty media in the project.

/deletemedia

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked media.

parameter type required description
session_id bigint yes valid session id
id uint yes media id

Replies with status 204 on success.

Deletes the media, its movies and their datafiles.

Replies with status 400 and x-error 12 if media not found.

/editmedia

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked media.

parameter type required description
session_id bigint yes valid session id
id uint yes media id
thumb_sphere int no flag, if true thumbnail is spherical
startimage uint no id of project image to use as start background
endimage uint no id of project image to use as end background
show_logo int no flag, if true show project logo while playing the movie
is_2d int no flag, if true image is 2d content
slideshow_order int no manual setting of slideshow order. 0 to clear.

Updates media metadata.

startimage and endimage must be images in the same project. To unassign a start/endimage, set the id to 0. start/endimages can be used by multiple media.

While slideshow_order can be set here, it is preferable to use /editslideshow

Replies with status 204 on success.

/getmedia

GET request

parameter type required description
session_id string yes valid session id
id uint no media id

Returns media data for given id, only if the session user has access to the project the media is linked to.

Reply is JSON-encoded media data

{
    "id": <media id>,
    "project_id": <project id>,
    "is_published": 1/0,
    "thumbnail": { <thumbnail data see /getimage> },
    "thumb_sphere": 1/0,
    "startimage": <id of startimage>,
    "endimage": <id of startimage>,
    "show_logo": 1/0,
    "is_2d": 1/0,
    "slideshow_order": <0 or slideshow order>,
    "created_at": <timestamp>,
    "updated_at": <timestamp>,
    "movies": [
        { <movie data see /getmovie> },
        ...
    ],
    "title_en": "<null or title of en-language movie>",
    "view_count": <movie view count>,
    "lock": <0 or id of locking job>
}

Note that view counts are reported only since client version 0.18.0

Note that title_en will be null if there are no movies or no movies with lang = 'en'. If there are multiple movies with lang 'en', the resulting title is unpredictable.

/listmedia

GET request

parameter type required description
session_id string yes valid (admin) session id
id uint no project id
is_published int no 1/0 published filter
is_2d int no 1/0 2d content filter
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

Returns list of media matching filter, sorted according to options. For admin users, the project id is optional and retrieves a list of all media. For non-admin users the project id must be given and retrieves a list of media in the project.

sort can be one of 'id', 'title_en', 'is_published', 'is_2d', 'created_at', 'updated_at', 'view_count'. sdir can be one of 'asc', 'desc'.

Returns a JSON list of media data as in /getmedia.

/publishmedia

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked media.

parameter type required description
session_id bigint yes valid session id
id uint yes media id
is_published int yes media published flag

Media can only be published if the media has at least one movie, and that movie has it's file. If there are multiple movies, their language attributes must be set and must be unique. If there are start/end images set, those must be already published.

Replies with status 204 on success.

9. Movie data

Manages project movies. Movies can be language-specific, and are grouped in media.

Movie upload functionality requires a working ffmpeg and ffprobe.

Note that creating, modifying or deleting movies is only allowed if the movie's media is not published.

/createmovie

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked projects or media.

parameter type required description
session_id bigint yes valid session id
id uint yes media id

Replies with status 204 on success and sets x-movie-id to the new movie id.

Creates a new empty movie for the given media with title '[No title]' and defined system default language.

The media must not be published.

/deletemovie

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked media or movie.

parameter type required description
session_id bigint yes valid session id
id uint yes movie id

Replies with status 204 on success.

Deletes the movie and its datafile.

The movie's media must not be published.

Replies with status 400 and x-error 12 if movie not found.

/editmovie

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked movies.

parameter type required description
session_id bigint yes valid session id
id uint yes movie id
lang str(2) no movie language, ISO 639-1
title str(50) no movie title. if set must be not empty.
subtitle str(100) no subtitle, claim, short description
is_stereo int no flag, if true is a top/down stereo movie

Updates movie data.

The movie's media must not be published.

Note that lang must be unique within the media. This is not enforced at the db level since it would make editing languages a bit less comfortable, but the media will not be publishable if the languages of the movies are inconsistent.

/getmovie

GET request

parameter type required description
session_id string yes valid session id
id uint no movie id

Returns movie data for given id, only if the session user has access to the project the movie is linked to.

Reply is JSON-encoded project data

{
    "id":<movie id>,
    "project_id":<project id>,
    "media_id":<media id>,
    "lang":"<movie language, iso 639-1 code>",
    "title":"<movie title>",
    "subtitle":"<movie subtitle>",
    "is_stereo":1/0,
    "movie_file": { movie file data, see /getfile },
    "width":<movie width in px>,
    "height":<movie height in px>,
    "duration":<movie duration in seconds, note:float>,
    "fps":<frames per second>,
    "bitrate":<average bitrate in b/s>,
    "is_fully_keyframed":<has only keyframes flag>,
    "keyframes":null|[<keyframe seconds>, <keyframe seconds>, ...],
    "created_at":<timestamp>,
    "updated_at":<timestamp>,
    "lock":<0 or id of locking job>
}

/uploadmovie

POST request with special formatting. disallowed on kleinhirn in mirrored projects. disallowed for locked projects, media or movie.

Requires its parameters either as cookies or as request headers, and the upload data as binary body post data. Creates a new job and updates it while processing upload data. Once the upload starts the job status can be fetched via /getjob.

cookie header type required description
session_id x-session-id bigint yes valid session id
id x-id uint yes movie id
filename x-filename str(100) yes original filename
is_chunked x-is-chunked int no 0/1 chunked upload flag
file_size x-file-size uint (yes) file size for chunked uploads

Note that ContentLength of the upload must be correctly set to size of uploaded file, and ContentType should be set to 'application/octet-stream'. The original filename is required by ffprobe/ffmpeg since they need the correct file extension. The original filename must be url-encoded if it contains non-ascii characters.

Replies with status 204 and x-job-id if the upload is accepted for processing. The job describes the entire upload, conversion, analysis process, but the request ends after a successful upload. Conversion and keyframe analysis is done in a background process, and the job must be monitored to see whether the processing is successful and if the movie is ready.

Note that the background process will check the project's storage quota after verifying/converting the movie, so the job may stop with an error status.

The movie's media must not be published to upload movies.

If is_chunked is 1, an this call only prepares and creates the upload job, actual data upload and handoff to movie processor happens in /uploadmoviechunk. In this case, the size of the uploaded file must be given as file_size.

/uploadmoviechunk

POST request with special formatting. disallowed on kleinhirn in mirrored projects.

Requires its parameters either as cookies or as request headers, and the upload data as binary body post data.

Adds chunk of data to datafile being uploaded.

cookie header type required description
session_id x-session-id bigint yes valid session id
id x-id uint yes job id
start x-start uint yes offset in file

Note that ContentLength of the upload must be set to size of the uploaded chunk, ContentType should be set to 'application/octet-stream'.

id must be of a running chunked movie upload job. Replies with x-error 35 if the job is not currently waiting for upload data. start must match the number of already uploaded bytes. Replies with x-error 39 if the job is cancelled.

The logged in user must be an admin or the owner of the job, otherwise replies with x-error 37.

If the last expected bytes of the file are written, the job is finished and handed off to movie_process, just like /uploadmovie would do.

Replies with status 204 on success.

10. Image handling

This encompasses backgrounds and logo/thumbnails.

Backgrounds are created and handled similar to media/movies, and can be assigned to movies as startimage/endimage and to the project as background.

Backgrounds can also be declared to be 2D images and ordered into a slideshow, the order of which is shared with media. Note that that flag is ignored for non-background images like logos where 2d is assumed.

Non-background images (logo, thumbs) are created and managed in the objects using them, and can only be replaced with a different upload here.

/createimage

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked projects.

parameter type required description
session_id bigint yes valid session id
id uint yes project id

Replies with status 204 on success and sets x-image-id to the new image id.

Creates a new empty background image in the project.

/deleteimage

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked images.

parameter type required description
session_id bigint yes valid session id
id uint yes image id

Replies with status 204 on success.

Deletes the image and its datafiles. Only deletes a background image since thumbnails etc are managed with their parent objects.

Replies with status 400 and x-error 12 if image not found. Replies with status 400 and error 32 if the image is used as a default background in its project.

/editimage

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked images.

parameter type required description
session_id bigint yes valid session id
id uint yes image id
caption string(50) no image caption
is_stereo int no flag, if true image uses two files for left/right
is_2d int no flag, if true image is 2d content
slideshow_order int no manual setting of slideshow order. 0 to clear.

Updates image data. Note that only background images can be edited. Non-background images are treated as part of their parent objects. The image must not be published to be editable.

While slideshow_order can be set here, it is preferable to use /editslideshow

/getimage

GET request

parameter type required description
session_id string yes valid session id
id uint no image id

Returns image data for given id, only if the session user has access to the project the image is linked to.

Reply is JSON-encoded project data

{
    "id": <image id>,
    "project_id": <project id>,
    "is_published": 1/0,
    "caption": "<image caption>",
    "is_background": 1/0,
    "is_stereo": 1/0,
    "is_2d": 1/0,
    "slideshow_order": <0 or slideshow order>,
    "image_file": { <datafile data see /getfile> },
    "image_file_right": { <datafile data for right-side image> } >,
    "width": <image width in px>,
    "height": <image height in px>,
    "created_at": <timestamp>,
    "updated_at": <timestamp>,
    "lock": <0 or id of locking job>
}

/listimages

GET request

parameter type required description
session_id string yes valid (admin) session id
id uint no project id
is_published int no 1/0 published filter
is_background int no 1/0 background images filter
is_stereo int no 1/0 stereo filter
is_2d int no 1/0 2d content filter
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

Returns list of images matching filter, sorted according to options. For admin users, the project id is optional and retrieves a list of all images. For non-admin users the project id must be given and retrieves a list of images in the project. For non-admin users the is_background filter is always set.

sort can be one of 'is_published', 'is_2d', 'is_background', 'is_stereo', 'caption', 'created_at', 'updated_at'. sdir can be one of 'asc', 'desc'.

Returns a JSON list of image data as in /getimage.

/publishimage

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked images.

parameter type required description
session_id bigint yes valid session id
id uint yes image id
is_published int yes image published flag

Publishes or unpublished a background image.

The image can only be published if it has an image file, or both image files if it is a stereo image.

If the image is unpublished, any media that is using the image, or project that is using the image as default background, is unpublished too.

/uploadimage

POST request with special formatting. disallowed on kleinhirn in mirrored projects. disallowed for locked projects or images.

Requires its parameters either as cookies or as request headers, and the upload data as binary body post data. Creates a new job and updates it while processing upload data.

cookie header type required description
session_id x-session-id bigint yes valid session id
id x-id uint yes image id
right x-right int no 0/1 right file of stereo image
filename x-filename string(100) no original filename

If right is 1 and the image is not stereo, replies with status 400 and x-error 8.

Note that ContentLength of the upload must be correctly set to size of uploaded file, and ContentType should be set to 'application/octet-stream'. The original filename must be url-encoded if it contains non-ascii characters.

The upload may fail if the total storage used by the project would exceed the project storage quota. This is checked after the upload since the image may need to be converted. In that case replies with status 400 and x-error 26.

The image must not be published to be able to upload.

11. Slideshow helper

While slideshows can be defined by explicitely setting slideshow_order on all images and media, the API calls here make it possible to set, get, or clear the overall slideshow definition for a project in one call.

/editslideshow

POST request. disallowed on kleinhirn in mirrored projects. disallowed for locked projects.

parameter type required description
session_id bigint yes valid session id
id uint yes project id
slideshow string yes slideshow order string see below

Updates slideshow order on all 2d images and media in the project.

slideshow must be given as a comma separated list of image and media ids (belonging to the given project), each prefixed with 'i' or 'm', p.ex.:

i123,m5342,i12

clears (sets to 0) slideshow_order on all images and media not included in the new order. pass an empty string as slideshow to remove all slideshow definition from the project.

/getslideshow

GET request.

parameter type required description
session_id bigint yes valid session id
id uint yes project id

Returns slideshow definition for the project (which can be empty) as plain text string, giving the slideshow as a comma separated list of image and media ids, each prefixed with 'i' or 'm', p.ex.:

i123,m5342,i12

12. Media files

File metadata, file binary data download, with optional load limiting.

Download is not allowed for locked files.

File upload happens in /upload* api calls.

Also includes kleinhirn-specific /kleinhirnfile api call.

TODO admin purposes /listdatafiles

/file

Disallowed for locked datafiles.

parameter type required description
id uint yes file id
session_id bigint (yes) valid session id
device_name string(36) (yes) client device name
queue int no if 1, use load management
filename string no content-disposition filename

Requires either session_id or device_name.

When accessed with a device name:

When accessed with a session id, and the file belongs to a project, the session user must have access to the project.

If queue is 1, may return status 429 if the network is already being used at capacity. In that case the client should retry the request after a reasonable delay.

Sends back binary file data, with unspecific binary content-type application/octet-stream. Also sets the content-disposition header so browsers can get a filename. If filename is given, that is used, otherwise the original filename of the upload.

Uses x-accel-redirect if running on nginx. Note that this requires sendfile=on and an alias to /uploads for the upload location in nginx config. It also requires the server to set x-server-type request header to "nginx".

Uses x-sendfile if running on apache2. Note that this requires mod_xsendfile installed and enabled in apache vhost config. It also requires the server to set x-server-type request header to "apache2". Note also that for apache sendfile the datafiles directory must be defined as an absolute path.

/getfile

GET request

parameter type required description
session_id string yes valid session id
id uint no datafile id

Returns datafile data for given id, only if the session user has access to the project the datafile is linked to.

If the datafile is not linked to a project (software), access is granted automatically.

TODO discuss - does access to software need to be restricted?

Reply is JSON-encoded project data

{
    "id": <image id>,
    "project_id": <project id>,
    "file_size": <file size in bytes>,
    "mime_type": "<null or guessed mime type>",
    "checksum": <null or crc32 checksum>,
    "original_filename": "originally uploaded file.ext",
    "created_at": <timestamp>,
    "updated_at": <timestamp>,
    "lock": <0 or id of locking job>
}

/kleinhirnfile

Disallowed for locked datafiles.

parameter type required description
id uint yes file id
kleinhirn_name string yes kleinhirn hostname

Note that this api call must be locked behind server-side http auth. The server must translate the authenticated user name into the value of the kleinhirn_name parameter. See the default nginx-server.conf.

Sends back binary file data, with unspecific binary content-type application/octet-stream.

Returns status 400 and x-error 38 if the file is locked.

Uses x-accel-redirect if running on nginx. Note that this requires sendfile=on and an alias to /uploads for the upload location in nginx config. It also requires the server to set x-server-type request header to "nginx".

Uses x-sendfile if running on apache2. Note that this requires mod_xsendfile installed and enabled in apache vhost config. It also requires the server to set x-server-type request header to "apache2". Note also that for apache sendfile the datafiles directory must be defined as an absolute path.

13. Progress bars

Jobs are created for uploads and movie processing tasks.

Jobs are linked to the objects of the operation and lock those while the job is running. Locked objects cannot be changed or removed.

If an api call is described as "disallowed for locked ...", then that api call will return status 400 and x-error 38 if it is attemped while the described lock is active.

/canceljob

POST request

parameter type required description
session_id bigint yes valid (admin) session id
id uint yes job id

If given the id of a RUNNING job, attempts to cancel it.

Admin users can cancel any job, other users can only cancel jobs in their projects.

The job will be set to status CANCELLED. This will prevent further updates of the job and force the job processes to abort and cleanup.

If the job is a chunked movie upload that is currently waiting, the cleanup is triggered explicitely since there is no other process currently using that job.

Returns status 204 on success.

/getjob

GET request

parameter type required description
session_id bigint yes valid session id
id uint no job id

Returns job data, if exists and user has access to it. If job id is not given, returns data of latest (by creation time) job for the user of that session. Note that the job returned may be in any status. If no such job exists, returns status 204.

Returns json data structure:

{
    "id": <job id>,
    "project_id": <null or project id>,
    "user_id": <user id>,
    "media_id": <null or media id>,
    "movie_id": <null or movie id>,
    "image_id": <null or image id>,
    "software_id": <null or software id>,
    "datafile_id": <null or datafile id>,
    "label": "human readable label",
    "data": "machine readable data, serialised json",
    "error": "null or error report for failed jobs,
    "total": <total of "things to do", p.ex. bytes to download>,
    "curr": <number of currently done "things to do">,
    "status": <job status, see job.STATUS_*>,
    "created_at": <timestamp>,
    "updated_at": <timestamp>
}

/listjobs

GET request

parameter type required description
session_id string yes valid (admin) session id
project_id uint no project filter
user_id uint no user filter
status int no job status see STATUS_*
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit or 0 for all

Lists jobs for given filter criteria.

Admin permission is required to list jobs that do not belong to the logged-in user or projects he can access.

If sort/sort_dir is given, the list is sorted accordingly, otherwise by newest first.

sort can be one of 'label', 'status', 'created_at', 'updated_at'. sdir can be one of 'asc', 'desc'.

Reply is JSON-encoded list of objects:

[
    {job data see /getjob }, 
    ...
]
reply header type description
x-count int total result count

14. Media view statistics

View statistics are collected by the playstat collector service and stored in the db. The API does not provide methods to create or edit stats except by upload from a kleinhirn.

On a kleinhirn, any playstats that are successfully uploaded to the main server are deleted from the local database.

/kleinhirnaddplaystat

POST request. disallowed on kleinhirn.

parameter type required description
media_id uint yes played media id
kleinhirn_name string yes kleinhirn hostname
play_time uint yes play timestamp
view_count uint yes view count

Note that this api call must be locked behind server-side http auth. The server must translate the authenticated user name into the value of the kleinhirn_name parameter. See the default nginx-server.conf.

Stores the given playstat row.

This is only used to submit playstats from kleinhirns to the main server. Local playstat data is written directly to the database.

Replies with status 200 on success.

/listplaystats

GET request.

parameter type required description
session_id string yes valid session id
project_id uint (yes) project selection
media_id uint (yes) movie selection
from uint no selected interval start
to uint no selected interval end
group string no interval grouping: "month", "day", "none"
show_hostname int no 0:ignore hostname, 1:split results by hostname
sort string no sort column
sdir string no sort direction
start int no paging: start
limit int no paging: limit, 0 for all

Returns list of play statistics, aggregated and sorted according to the selection.

Either project_id or media_id must be specified.

sort can one of 'hostname' (only with show_hostname=1 or group="none"), 'media_id', 'view_count', 'play_time'. sdir can be one of 'asc', 'desc'.

The data is returned as JSON-encoded list of objects:

[
    {
        "id": <playstat row id, only for _group_="none">,
        "hostname": "<hostname, only if _show_hostname_=1>",
        "month": <YYYYMM, only for _group_="month">,
        "day": <timestamp rounded to midnight, only for _group_="day">,

        "project_id": <project id>,
        "media_id": <media id>,
        "play_time": <timestamp; for _group_="day" rounded to
            midnight, for _group_="month" rounded to midnight on
            the 1st>,
        "view_count": <sum views for grouping>
    }, 
    ...
]

Note that show_hostname is implicitely 1 if group is "none".

Appendices

A. Error codes and messages

List of possible x-error header values

value message description
1 name in use attempt to set a screen name that is already in use.
2 email in use attempt to create new user with an email that is already in use.
3 invalid screen name screen name not good enough for us
4 account inactive attempt to login an inactive account
5 code in use attempt to create new project with duplicate code
6 already granted project/user access already granted
7 not granted attempt to clear non-existent access
8 mono image attempt to upload right file for mono image
9 invalid image PIL.Image couldn't open the image
10 project is full attempt to access project data with a client when project is already at client limit
11 nothing to block attempt to block a client that is not associated with the project
12 invalid invalid request parameter
13 check-in required project requires check-in with password
14 image incomplete attempt to publish incomplete image
15 media incomplete attempt to publish media without movies
16 unpublished links attempt to publish media with unpublished start/endimage
17 inconsistent languages attempt to publish media with inconsistent languages in its movies
18 already linked attempt to add software to project again
19 processing unavailable error communicating with movie processor
20 not linked attempt to unlink not linked software
21 software incomplete attempt to publish software with missing required data
22 already linked attempt to add kleinhirn/project link that already exists
23 not linked attempt to remove kleinhirn/project link that does not exist
24 hostname in use cannot create a new empty kleinhirn or change hostname of existing kleinhirn
25 project not visible project is not published or not licensed
26 storage quota exceeded upload cancelled because project's storage quota reached/exceeded
27 published is immutable attempt to modify a published entity - unpublish first
28 upload aborted file upload apparently aborted
29 not for you datafile access by kleinhirn when it does not belong to the kleinhirn's projects
30 movie quota reached attempt to create a movie when project is at max_movies
31 disallowed on kleinhirn attempt to access a function that is disabled on kleinhirn
32 default background attempt to delete default background for a project
33 project not presentable attempt to publish a project without logo or background
34 busy now try later file download cannot be served because the downling bandwith is already saturated
35 already uploading attempt to upload chunk of data to a job that is already uploading
36 not that kind of job attempt to use an unsuitable job for chunked movie upload
37 not yours attempt to use another user's job
38 locked by job attempt to modify an object or download a file that is locked by a running job
39 cancelled job was cancelled
40 processing unavailable movie processing is not available on this server, perhaps temporarily
41 invalid movie movie processing rejects the movie. this error is only visible in job output.
42 data locked kleinhirn data that would need to be synchronised is locked by running job(s).

B. Project data format versions

Description of the project data returned by /project api call.

version=legacy

This output emulates the static file oriented legacy vrsync configurations.

Datafiles are expressed as filenames that are later translated back into parametrised api calls in the the web config.

cubemaps, position, rotation are constant values in this format.

Stereo images are using an implicitely formed image file name that has not been fully tested in this emulation.

Description:

{
    "bg": "img_<image id>",
    "cubemaps": {},
    "logo": "img_<image id>",
    "movies": {
        "mov_<media id>": {
            "claim": "<movie subtitle>",
            "duration": <movie duration in seconds>,
            "file_size": <movie file size in bytes>,
            "keyframes": [<array of keyframe times in seconds>],
            "lang": "<movie language, iso language code>",
            "only_keyframes": <1 if every frame is a keyframe, 0 otherwise>,
            "path": "file_id_<datafile id>.mp4",
            "show_logo": <show logo flag from media>,
            "stereo": <1 if stereo top-down movie, 0 otherwise>,
            "thumb": "file_id_<datafile id>.png",
            "thumbsphere": <thumb sphere flag from movie>,
            "title": "<movie title>",
            "version": "<last update timestamp>"
        },
        ...
    },
    "position": [ 0.0, -1.5, 12.0 ],
    "rotation": [ 0.0, 0.0, 0.0 ],
    "textures": {
        "img_<image_id>": {
            "background": <1 if the image is a background, 0 otherwise>,
            "path": "file_id_<datafile id>.png",
            "stereo": <1 if the image is a stereo image and has a image_right; 0 otherwise>,
            "version": "<last update timestamp>"
        },
        ...
    }
}

Sample output:

{
    "bg": "img_1657",
    "cubemaps": {},
    "logo": "img_1656",
    "movies": {
        "mov_378": {
            "claim": "",
            "duration": 60.0,
            "file_size": 1824120,
            "keyframes": [ 0.0, 8.333333, 16.666667, 25.0, 33.333333, 41.666667, 50.0, 58.333333 ],
            "lang": "en",
            "only_keyframes": 0,
            "path": "file_id_4917.mp4",
            "show_logo": 0,
            "stereo": 0,
            "thumb": "file_id_3842.png",
            "thumbsphere": 0,
            "title": "Blinkenlights",
            "version": "1537864181"
        },
        "mov_483": {
            "claim": "",
            "duration": 368.136,
            "file_size": 224367108,
            "keyframes": [ 0.0, 5.966992, 6.966992, 15.300326, 23.633659, 31.966992, 40.300326, 48.633659, 56.966992, 65.300326, 68.300977, 69.300977, 70.300977, 78.63431, 86.967643, 95.300977, 103.63431, 111.967643, 120.300977, 128.63431, 136.967643, 145.300977, 153.63431, 161.967643, 170.300977, 178.63431, 186.967643, 195.300977, 203.63431, 211.967643, 220.300977, 228.63431, 236.967643, 245.300977, 253.63431, 261.967643, 270.300977, 278.63431, 285.435026, 286.435026, 287.435026, 295.768359, 304.101693, 308.935026, 309.935026, 316.56901, 324.902344, 333.235677, 341.56901, 349.902344, 358.235677, 366.56901 ],
            "lang": "en",
            "only_keyframes": 0,
            "path": "file_id_4468.mp4",
            "show_logo": 0,
            "stereo": 1,
            "thumb": "file_id_4467.png",
            "thumbsphere": 0,
            "title": "Invasion",
            "version": "1533295517"
        }
    },
    "position": [ 0.0, -1.5, 12.0 ],
    "rotation": [ 0.0, 0.0, 0.0 ],
    "textures": {
        "img_1656": {
            "background": 0,
            "path": "file_id_1656.png",
            "stereo": 0,
            "version": "1506589577"
        },
        "img_1657": {
            "background": 1,
            "path": "file_id_1657.png",
            "stereo": 0,
            "version": "1506589577"
        }
    }
}

version=1.0

This output version is the original evrsync data format, used by evrsync_client up to version 0.20.0

Note that all is_published flags will be 1 since only published content is output by this api call.

All created_at and updated_at values are integer unix timestamps with seconds precision.

Note that project_id is null for the image and datafile used by software since software can be assigned to multiple projects.

Description:

{
    "background": <image id>,
    "code": "<project code>",
    "created_at": <timestamp>,
    "has_password": <1 if project password is set; 0 otherwise>,
    "id": <project id>,
    "images": [
        {
            "caption": "<image caption, null for thumbnails>",
            "created_at": <timestamp>,
            "height": <pixel height>,
            "id": <image id>,
            "image_file": {
                "created_at": <timestamp>,
                "file_size": <file size in bytes>,
                "id": <datafile id>,
                "is_published": 1,
                "original_filename": "<filename of the uploaded file>",
                "project_id": <parent project id>,
                "updated_at": <timestamp>
            },
            "image_file_right": <null or data structure like "image_file">,
            "is_background": <1 if the image is a backgroun, 0 otherwise>,
            "is_published": 1,
            "is_stereo": <1 if image is stereo (has image_file_right), 0 otherwise>,
            "project_id": <parent project id>,
            "updated_at": <timestamp>,
            "width": <pixel width>
        },
        ...
    ],
    "is_checkin_required": <1 if devices must check in to the project, 0 otherwise>,
    "is_published": 1,
    "is_visible": <1 if the project is published and has a valid license, 0 otherwise>,
    "logo": <image id>,
    "logo_blend": null,
    "logo_position": "0,-1.5,12",
    "logo_rotation": "0,0,0",
    "max_clients": <max devices allowed in the project by currently valid licenses>,
    "max_movies": <max movies allowed in the project by currently valid licenses>,
    "media": [
        {
            "created_at": <timestamp>,
            "endimage": <null or image-id>,
            "id": <media id>,
            "is_published": 1,
            "movies": [
                {
                    "created_at": <timestamp>,
                    "duration": <duration in seconds>,
                    "height": <pixel height>,
                    "id": <movie id>,
                    "is_fully_keyframed": <if 1, all frames are keyframes, otherwise 0>,
                    "is_stereo": <if 1, top-down stereo; 0 otherwise>,
                    "keyframes": [ <array of keyframe times> ],
                    "lang": "<movie language>",
                    "media_id": <parent media id>,
                    "movie_file": {<datafile data, see image_file above>},
                    "project_id": <parent project id>,
                    "subtitle": "<movie subtitle>",
                    "title": "<movie title>",
                    "updated_at": <timestamp>,
                    "width": <pixel width>
                }
            ],
            "project_id": <parent project id>,
            "show_logo": <media show logo flag>,
            "startimage": <null or image id>,
            "thumb_sphere": <media thumb sphere flag>,
            "thumbnail": <image id>,
            "updated_at": <timestamp>
        },
        ...
    ],
    "movies_used": <number of movies in the project, for licensing purposes>,
    "name": "<project display name>",
    "software": [
        {
            "binary_file": <datafile id>,
            "build_type": <null or client software build type>,
            "created_at": <timestamp>,
            "description": <null or software description>,
            "download_url": <null or external download url>,
            "filename": "<suggested filename>",
            "id": <software id>,
            "is_custom": <1 if custom build, 0 otherwise>,
            "is_published": 1,
            "label": "<human readable label>",
            "platform": "<platform label>",
            "thumbnail": {<image and image_file data>}
            "updated_at": <thumbnail>,
            "version": "<software version string>"
        },
        ...
    ],
    "storage_quota": <max storage allowed in project, in million bytes>,
    "storage_used": <storage used in project, in million bytes>,
    "text_color": null,
    "updated_at": <timestamp>
}

Sample output:

{
    "background": 247,
    "code": "test",
    "created_at": 1506589577,
    "has_password": 0,
    "id": 15,
    "images": [
        {
            "caption": null,
            "created_at": 1506589577,
            "height": 2048,
            "id": 246,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 96295,
                "id": 1656,
                "is_published": 1,
                "original_filename": "default_logo.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": "[Default background]",
            "created_at": 1506589577,
            "height": 2048,
            "id": 247,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 1965328,
                "id": 1657,
                "is_published": 1,
                "original_filename": "default_background.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 1,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": null,
            "created_at": 1527675670,
            "height": 120,
            "id": 639,
            "image_file": {
                "created_at": 1527675670,
                "file_size": 1758,
                "id": 3842,
                "is_published": 1,
                "original_filename": "default_thumbnail.png",
                "project_id": 15,
                "updated_at": 1527675670
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1527675670,
            "width": 160
        }
    ],
    "is_checkin_required": 0,
    "is_published": 1,
    "is_visible": 1,
    "logo": 246,
    "logo_blend": null,
    "logo_position": "0,-1.5,12",
    "logo_rotation": "0,0,0",
    "max_clients": 1000,
    "max_movies": 1000,
    "media": [
        {
            "created_at": 1527675670,
            "endimage": null,
            "id": 345,
            "is_published": 1,
            "movies": [
                {
                    "bitrate": 243216,
                    "created_at": 1527675671,
                    "duration": 60.0,
                    "fps": 30.0,
                    "height": 960,
                    "id": 378,
                    "is_fully_keyframed": 0,
                    "is_stereo": 0,
                    "keyframes": [ 0.0, 8.333333, 16.666667, 25.0, 33.333333, 41.666667, 50.0, 58.333333 ],
                    "lang": "en",
                    "media_id": 345,
                    "movie_file": {
                        "created_at": 1537864181,
                        "file_size": 1824120,
                        "id": 4917,
                        "is_published": 1,
                        "original_filename": "blink-noaudio.mp4",
                        "project_id": 15,
                        "updated_at": 1537864181
                    },
                    "project_id": 15,
                    "subtitle": "",
                    "title": "Blinkenlights",
                    "updated_at": 1537864181,
                    "width": 1920
                }
            ],
            "project_id": 15,
            "show_logo": 0,
            "startimage": null,
            "thumb_sphere": 0,
            "thumbnail": 639,
            "updated_at": 1537864191
        },
    ],
    "movies_used": 1,
    "name": "test",
    "software": [
        {
            "binary_file": 4922,
            "build_type": "android_mono",
            "created_at": 1533574069,
            "description": null,
            "download_url": null,
            "filename": "evrsync_test-android_mono-0.0.6.apk",
            "id": 230,
            "is_custom": 0,
            "is_published": 1,
            "label": "evrsync_test-android_mono",
            "platform": "Android",
            "thumbnail": {
                "caption": null,
                "created_at": 1533574069,
                "height": 960,
                "id": 793,
                "image_file": {
                    "created_at": 1537865617,
                    "file_size": 89069,
                    "id": 4923,
                    "is_published": 1,
                    "original_filename": "evrsync_logo.png",
                    "project_id": null,
                    "updated_at": 1537865617
                },
                "image_file_right": null,
                "is_background": 0,
                "is_published": 1,
                "is_stereo": 0,
                "project_id": null,
                "updated_at": 1537865617,
                "width": 960
            },
            "updated_at": 1537865618,
            "version": "0.0.6"
        }
    ],
    "storage_quota": 50000,
    "storage_used": 2853,
    "text_color": null,
    "updated_at": 1533029442
}

version=1.1

The output is the same as version 1.0 but in the software list datafile details are included for binary_file instead of just the datafile id.

{
    "background": <image id>,
    "code": "<project code>",
    "created_at": <timestamp>,
    "has_password": <1 if project password is set; 0 otherwise>,
    "id": <project id>,
    "images": [
        {
            "caption": "<image caption, null for thumbnails>",
            "created_at": <timestamp>,
            "height": <pixel height>,
            "id": <image id>,
            "image_file": {
                "created_at": <timestamp>,
                "file_size": <file size in bytes>,
                "id": <datafile id>,
                "is_published": 1,
                "original_filename": "<filename of the uploaded file>",
                "project_id": <parent project id>,
                "updated_at": <timestamp>
            },
            "image_file_right": <null or data structure like "image_file">,
            "is_background": <1 if the image is a backgroun, 0 otherwise>,
            "is_published": 1,
            "is_stereo": <1 if image is stereo (has image_file_right), 0 otherwise>,
            "project_id": <parent project id>,
            "updated_at": <timestamp>,
            "width": <pixel width>
        },
        ...
    ],
    "is_checkin_required": <1 if devices must check in to the project, 0 otherwise>,
    "is_published": 1,
    "is_visible": <1 if the project is published and has a valid license, 0 otherwise>,
    "logo": <image id>,
    "logo_blend": null,
    "logo_position": "0,-1.5,12",
    "logo_rotation": "0,0,0",
    "max_clients": <max devices allowed in the project by currently valid licenses>,
    "max_movies": <max movies allowed in the project by currently valid licenses>,
    "media": [
        {
            "created_at": <timestamp>,
            "endimage": <null or image-id>,
            "id": <media id>,
            "is_published": 1,
            "movies": [
                {
                    "created_at": <timestamp>,
                    "duration": <duration in seconds>,
                    "height": <pixel height>,
                    "id": <movie id>,
                    "is_fully_keyframed": <if 1, all frames are keyframes, otherwise 0>,
                    "is_stereo": <if 1, top-down stereo; 0 otherwise>,
                    "keyframes": [ <array of keyframe times> ],
                    "lang": "<movie language>",
                    "media_id": <parent media id>,
                    "movie_file": {<datafile data, see image_file above>},
                    "project_id": <parent project id>,
                    "subtitle": "<movie subtitle>",
                    "title": "<movie title>",
                    "updated_at": <timestamp>,
                    "width": <pixel width>
                }
            ],
            "project_id": <parent project id>,
            "show_logo": <media show logo flag>,
            "startimage": <null or image id>,
            "thumb_sphere": <media thumb sphere flag>,
            "thumbnail": <image id>,
            "updated_at": <timestamp>
        },
        ...
    ],
    "movies_used": <number of movies in the project, for licensing purposes>,
    "name": "<project display name>",
    "software": [
        {
            "binary_file": {null or datafile data},
            "build_type": <null or client software build type>,
            "created_at": <timestamp>,
            "description": <null or software description>,
            "download_url": <null or external download url>,
            "filename": "<suggested filename>",
            "id": <software id>,
            "is_custom": <1 if custom build, 0 otherwise>,
            "is_published": 1,
            "label": "<human readable label>",
            "platform": "<platform label>",
            "thumbnail": {<image and image_file data>}
            "updated_at": <thumbnail>,
            "version": "<software version string>"
        },
        ...
    ],
    "storage_quota": <max storage allowed in project, in million bytes>,
    "storage_used": <storage used in project, in million bytes>,
    "text_color": null,
    "updated_at": <timestamp>
}

Sample output:

{
    "background": 247,
    "code": "test",
    "created_at": 1506589577,
    "has_password": 0,
    "id": 15,
    "images": [
        {
            "caption": null,
            "created_at": 1506589577,
            "height": 2048,
            "id": 246,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 96295,
                "id": 1656,
                "is_published": 1,
                "original_filename": "default_logo.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": "[Default background]",
            "created_at": 1506589577,
            "height": 2048,
            "id": 247,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 1965328,
                "id": 1657,
                "is_published": 1,
                "original_filename": "default_background.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 1,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": null,
            "created_at": 1527675670,
            "height": 120,
            "id": 639,
            "image_file": {
                "created_at": 1527675670,
                "file_size": 1758,
                "id": 3842,
                "is_published": 1,
                "original_filename": "default_thumbnail.png",
                "project_id": 15,
                "updated_at": 1527675670
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1527675670,
            "width": 160
        }
    ],
    "is_checkin_required": 0,
    "is_published": 1,
    "is_visible": 1,
    "logo": 246,
    "logo_blend": null,
    "logo_position": "0,-1.5,12",
    "logo_rotation": "0,0,0",
    "max_clients": 1000,
    "max_movies": 1000,
    "media": [
        {
            "created_at": 1527675670,
            "endimage": null,
            "id": 345,
            "is_published": 1,
            "movies": [
                {
                    "created_at": 1527675671,
                    "duration": 60.0,
                    "height": 960,
                    "id": 378,
                    "is_fully_keyframed": 0,
                    "is_stereo": 0,
                    "keyframes": [ 0.0, 8.333333, 16.666667, 25.0, 33.333333, 41.666667, 50.0, 58.333333 ],
                    "lang": "en",
                    "media_id": 345,
                    "movie_file": {
                        "created_at": 1537864181,
                        "file_size": 1824120,
                        "id": 4917,
                        "is_published": 1,
                        "original_filename": "blink-noaudio.mp4",
                        "project_id": 15,
                        "updated_at": 1537864181
                    },
                    "project_id": 15,
                    "subtitle": "",
                    "title": "Blinkenlights",
                    "updated_at": 1537864181,
                    "width": 1920
                }
            ],
            "project_id": 15,
            "show_logo": 0,
            "startimage": null,
            "thumb_sphere": 0,
            "thumbnail": 639,
            "updated_at": 1537864191
        }
    ],
    "movies_used": 1,
    "name": "test",
    "software": [
        {
            "binary_file": {
                "created_at": 1537865410,
                "file_size": 246954605,
                "id": 4922,
                "is_published": 1,
                "original_filename": "evrsync_test-android_mono-0.0.6.apk",
                "project_id": null,
                "updated_at": 1537865412
            },
            "build_type": "android_mono",
            "created_at": 1533574069,
            "description": null,
            "download_url": null,
            "filename": "evrsync_test-android_mono-0.0.6.apk",
            "id": 230,
            "is_custom": 0,
            "is_published": 1,
            "label": "evrsync_test-android_mono",
            "platform": "Android",
            "thumbnail": {
                "caption": null,
                "created_at": 1533574069,
                "height": 960,
                "id": 793,
                "image_file": {
                    "created_at": 1537865617,
                    "file_size": 89069,
                    "id": 4923,
                    "is_published": 1,
                    "original_filename": "evrsync_logo.png",
                    "project_id": null,
                    "updated_at": 1537865617
                },
                "image_file_right": null,
                "is_background": 0,
                "is_published": 1,
                "is_stereo": 0,
                "project_id": null,
                "updated_at": 1537865617,
                "width": 960
            },
            "updated_at": 1537865618,
            "version": "0.0.6"
        }
    ],
    "storage_quota": 50000,
    "storage_used": 4,
    "text_color": null,
    "updated_at": 1533029442
}

version=1.2

This adds the lock flag to project. This will be null for project data retrieved by the public API since locked projects are not published...

Description:

{
    "background": <image id>,
    "code": "<project code>",
    "created_at": <timestamp>,
    "has_password": <1 if project password is set; 0 otherwise>,
    "id": <project id>,
    "images": [
        {
            "caption": "<image caption, null for thumbnails>",
            "created_at": <timestamp>,
            "height": <pixel height>,
            "id": <image id>,
            "image_file": {
                "created_at": <timestamp>,
                "file_size": <file size in bytes>,
                "id": <datafile id>,
                "is_published": 1,
                "original_filename": "<filename of the uploaded file>",
                "project_id": <parent project id>,
                "updated_at": <timestamp>
            },
            "image_file_right": <null or data structure like "image_file">,
            "is_background": <1 if the image is a backgroun, 0 otherwise>,
            "is_published": 1,
            "is_stereo": <1 if image is stereo (has image_file_right), 0 otherwise>,
            "project_id": <parent project id>,
            "updated_at": <timestamp>,
            "width": <pixel width>
        },
        ...
    ],
    "is_checkin_required": <1 if devices must check in to the project, 0 otherwise>,
    "is_published": 1,
    "is_visible": <1 if the project is published and has a valid license, 0 otherwise>,
    "lock": <null or id of job locking the project>,
    "logo": <image id>,
    "logo_blend": null,
    "logo_position": "0,-1.5,12",
    "logo_rotation": "0,0,0",
    "max_clients": <max devices allowed in the project by currently valid licenses>,
    "max_movies": <max movies allowed in the project by currently valid licenses>,
    "media": [
        {
            "created_at": <timestamp>,
            "endimage": <null or image-id>,
            "id": <media id>,
            "is_published": 1,
            "movies": [
                {
                    "created_at": <timestamp>,
                    "duration": <duration in seconds>,
                    "height": <pixel height>,
                    "id": <movie id>,
                    "is_fully_keyframed": <if 1, all frames are keyframes, otherwise 0>,
                    "is_stereo": <if 1, top-down stereo; 0 otherwise>,
                    "keyframes": [ <array of keyframe times> ],
                    "lang": "<movie language>",
                    "media_id": <parent media id>,
                    "movie_file": {<datafile data, see image_file above>},
                    "project_id": <parent project id>,
                    "subtitle": "<movie subtitle>",
                    "title": "<movie title>",
                    "updated_at": <timestamp>,
                    "width": <pixel width>
                }
            ],
            "project_id": <parent project id>,
            "show_logo": <media show logo flag>,
            "startimage": <null or image id>,
            "thumb_sphere": <media thumb sphere flag>,
            "thumbnail": <image id>,
            "updated_at": <timestamp>
        },
        ...
    ],
    "movies_used": <number of movies in the project, for licensing purposes>,
    "name": "<project display name>",
    "software": [
        {
            "binary_file": {null or datafile data},
            "build_type": <null or client software build type>,
            "created_at": <timestamp>,
            "description": <null or software description>,
            "download_url": <null or external download url>,
            "filename": "<suggested filename>",
            "id": <software id>,
            "is_custom": <1 if custom build, 0 otherwise>,
            "is_published": 1,
            "label": "<human readable label>",
            "platform": "<platform label>",
            "thumbnail": {<image and image_file data>}
            "updated_at": <thumbnail>,
            "version": "<software version string>"
        },
        ...
    ],
    "storage_quota": <max storage allowed in project, in million bytes>,
    "storage_used": <storage used in project, in million bytes>,
    "text_color": null,
    "updated_at": <timestamp>
}

Sample output:

{
    "background": 247,
    "code": "test",
    "created_at": 1506589577,
    "has_password": 0,
    "id": 15,
    "images": [
        {
            "caption": null,
            "created_at": 1506589577,
            "height": 2048,
            "id": 246,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 96295,
                "id": 1656,
                "is_published": 1,
                "original_filename": "default_logo.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": "[Default background]",
            "created_at": 1506589577,
            "height": 2048,
            "id": 247,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 1965328,
                "id": 1657,
                "is_published": 1,
                "original_filename": "default_background.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 1,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": null,
            "created_at": 1527675670,
            "height": 120,
            "id": 639,
            "image_file": {
                "created_at": 1527675670,
                "file_size": 1758,
                "id": 3842,
                "is_published": 1,
                "original_filename": "default_thumbnail.png",
                "project_id": 15,
                "updated_at": 1527675670
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1527675670,
            "width": 160
        }
    ],
    "is_checkin_required": 0,
    "is_published": 1,
    "is_visible": 1,
    "lock": null,
    "logo": 246,
    "logo_blend": null,
    "logo_position": "0,-1.5,12",
    "logo_rotation": "0,0,0",
    "max_clients": 1000,
    "max_movies": 1000,
    "media": [
        {
            "created_at": 1527675670,
            "endimage": null,
            "id": 345,
            "is_published": 1,
            "movies": [
                {
                    "created_at": 1527675671,
                    "duration": 60.0,
                    "height": 960,
                    "id": 378,
                    "is_fully_keyframed": 0,
                    "is_stereo": 0,
                    "keyframes": [ 0.0, 8.333333, 16.666667, 25.0, 33.333333, 41.666667, 50.0, 58.333333 ],
                    "lang": "en",
                    "media_id": 345,
                    "movie_file": {
                        "created_at": 1537864181,
                        "file_size": 1824120,
                        "id": 4917,
                        "is_published": 1,
                        "original_filename": "blink-noaudio.mp4",
                        "project_id": 15,
                        "updated_at": 1537864181
                    },
                    "project_id": 15,
                    "subtitle": "",
                    "title": "Blinkenlights",
                    "updated_at": 1537864181,
                    "width": 1920
                }
            ],
            "project_id": 15,
            "show_logo": 0,
            "startimage": null,
            "thumb_sphere": 0,
            "thumbnail": 639,
            "updated_at": 1537864191
        }
    ],
    "movies_used": 1,
    "name": "test",
    "software": [
        {
            "binary_file": {
                "created_at": 1537865410,
                "file_size": 246954605,
                "id": 4922,
                "is_published": 1,
                "original_filename": "evrsync_test-android_mono-0.0.6.apk",
                "project_id": null,
                "updated_at": 1537865412
            },
            "build_type": "android_mono",
            "created_at": 1533574069,
            "description": null,
            "download_url": null,
            "filename": "evrsync_test-android_mono-0.0.6.apk",
            "id": 230,
            "is_custom": 0,
            "is_published": 1,
            "label": "evrsync_test-android_mono",
            "platform": "Android",
            "thumbnail": {
                "caption": null,
                "created_at": 1533574069,
                "height": 960,
                "id": 793,
                "image_file": {
                    "created_at": 1537865617,
                    "file_size": 89069,
                    "id": 4923,
                    "is_published": 1,
                    "original_filename": "evrsync_logo.png",
                    "project_id": null,
                    "updated_at": 1537865617
                },
                "image_file_right": null,
                "is_background": 0,
                "is_published": 1,
                "is_stereo": 0,
                "project_id": null,
                "updated_at": 1537865617,
                "width": 960
            },
            "updated_at": 1537865618,
            "version": "0.0.6"
        }
    ],
    "storage_quota": 50000,
    "storage_used": 4,
    "text_color": null,
    "updated_at": 1533029442
}

version=1.3

This adds the fps and bitrate information to movies. This is required by evrsync client version 0.23.1 and up.

Description:

{
    "background": <image id>,
    "code": "<project code>",
    "created_at": <timestamp>,
    "has_password": <1 if project password is set; 0 otherwise>,
    "id": <project id>,
    "images": [
        {
            "caption": "<image caption, null for thumbnails>",
            "created_at": <timestamp>,
            "height": <pixel height>,
            "id": <image id>,
            "image_file": {
                "created_at": <timestamp>,
                "file_size": <file size in bytes>,
                "id": <datafile id>,
                "is_published": 1,
                "original_filename": "<filename of the uploaded file>",
                "project_id": <parent project id>,
                "updated_at": <timestamp>
            },
            "image_file_right": <null or data structure like "image_file">,
            "is_background": <1 if the image is a backgroun, 0 otherwise>,
            "is_published": 1,
            "is_stereo": <1 if image is stereo (has image_file_right), 0 otherwise>,
            "project_id": <parent project id>,
            "updated_at": <timestamp>,
            "width": <pixel width>
        },
        ...
    ],
    "is_checkin_required": <1 if devices must check in to the project, 0 otherwise>,
    "is_published": 1,
    "is_visible": <1 if the project is published and has a valid license, 0 otherwise>,
    "lock": <null or id of job locking the project>,
    "logo": <image id>,
    "logo_blend": <null or "alpha", "add", "multiply">,
    "logo_position": "<logo position vector as string, x,y,z, default 0,-1.5,12>",
    "logo_rotation": "<logo rotation vector as string, u,v,w, default 0,0,0>",
    "max_clients": <max devices allowed in the project by currently valid licenses>,
    "max_movies": <max movies allowed in the project by currently valid licenses>,
    "media": [
        {
            "created_at": <timestamp>,
            "endimage": <null or image-id>,
            "id": <media id>,
            "is_published": 1,
            "movies": [
                {
                    "bitrate": <average bits per second>,
                    "created_at": <timestamp>,
                    "duration": <duration in seconds>,
                    "fps": <frames per second>,
                    "height": <pixel height>,
                    "id": <movie id>,
                    "is_fully_keyframed": <if 1, all frames are keyframes, otherwise 0>,
                    "is_stereo": <if 1, top-down stereo; 0 otherwise>,
                    "keyframes": [ <array of keyframe times> ],
                    "lang": "<movie language>",
                    "media_id": <parent media id>,
                    "movie_file": {<datafile data, see image_file above>},
                    "project_id": <parent project id>,
                    "subtitle": "<movie subtitle>",
                    "title": "<movie title>",
                    "updated_at": <timestamp>,
                    "width": <pixel width>
                }
            ],
            "project_id": <parent project id>,
            "show_logo": <media show logo flag>,
            "startimage": <null or image id>,
            "thumb_sphere": <media thumb sphere flag>,
            "thumbnail": <image id>,
            "updated_at": <timestamp>
        },
        ...
    ],
    "movies_used": <number of movies in the project, for licensing purposes>,
    "name": "<project display name>",
    "software": [
        {
            "binary_file": {null or datafile data},
            "build_type": <null or client software build type>,
            "created_at": <timestamp>,
            "description": <null or software description>,
            "download_url": <null or external download url>,
            "filename": "<suggested filename>",
            "id": <software id>,
            "is_custom": <1 if custom build, 0 otherwise>,
            "is_published": 1,
            "label": "<human readable label>",
            "platform": "<platform label>",
            "thumbnail": {<image and image_file data>}
            "updated_at": <thumbnail>,
            "version": "<software version string>"
        },
        ...
    ],
    "storage_quota": <max storage allowed in project, in million bytes>,
    "storage_used": <storage used in project, in million bytes>,
    "text_color": <null or string text color r,g,b,a>,
    "updated_at": <timestamp>
}

Sample output:

{
    "background": 247,
    "code": "test",
    "created_at": 1506589577,
    "has_password": 0,
    "id": 15,
    "images": [
        {
            "caption": null,
            "created_at": 1506589577,
            "height": 2048,
            "id": 246,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 96295,
                "id": 1656,
                "is_published": 1,
                "original_filename": "default_logo.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": "[Default background]",
            "created_at": 1506589577,
            "height": 2048,
            "id": 247,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 1965328,
                "id": 1657,
                "is_published": 1,
                "original_filename": "default_background.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 1,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": null,
            "created_at": 1527675670,
            "height": 120,
            "id": 639,
            "image_file": {
                "created_at": 1527675670,
                "file_size": 1758,
                "id": 3842,
                "is_published": 1,
                "original_filename": "default_thumbnail.png",
                "project_id": 15,
                "updated_at": 1527675670
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1527675670,
            "width": 160
        }
    ],
    "is_checkin_required": 0,
    "is_published": 1,
    "is_visible": 1,
    "lock": null,
    "logo": 246,
    "logo_blend": null,
    "logo_position": "0,-1.5,12",
    "logo_rotation": "0,0,0",
    "max_clients": 1000,
    "max_movies": 1000,
    "media": [
        {
            "created_at": 1527675670,
            "endimage": null,
            "id": 345,
            "is_published": 1,
            "movies": [
                {
                    "bitrate": 243216,
                    "created_at": 1527675671,
                    "duration": 60.0,
                    "fps": 30.0,
                    "height": 960,
                    "id": 378,
                    "is_fully_keyframed": 0,
                    "is_stereo": 0,
                    "keyframes": [
                        0.0,
                        8.333333,
                        16.666667,
                        25.0,
                        33.333333,
                        41.666667,
                        50.0,
                        58.333333
                    ],
                    "lang": "en",
                    "media_id": 345,
                    "movie_file": {
                        "created_at": 1537864181,
                        "file_size": 1824120,
                        "id": 4917,
                        "is_published": 1,
                        "original_filename": "blink-noaudio.mp4",
                        "project_id": 15,
                        "updated_at": 1537864181
                    },
                    "project_id": 15,
                    "subtitle": "",
                    "title": "Blinkenlights",
                    "updated_at": 1537864181,
                    "width": 1920
                }
            ],
            "project_id": 15,
            "show_logo": 0,
            "startimage": null,
            "thumb_sphere": 0,
            "thumbnail": 639,
            "updated_at": 1537864191
        }
    ],
    "movies_used": 1,
    "name": "test",
    "software": [
        {
            "binary_file": {
                "created_at": 1537865410,
                "file_size": 246954605,
                "id": 4922,
                "is_published": 1,
                "original_filename": "evrsync_test-android_mono-0.0.6.apk",
                "project_id": null,
                "updated_at": 1537865412
            },
            "build_type": "android_mono",
            "created_at": 1533574069,
            "description": null,
            "download_url": null,
            "filename": "evrsync_test-android_mono-0.0.6.apk",
            "id": 230,
            "is_custom": 0,
            "is_published": 1,
            "label": "evrsync_test-android_mono",
            "platform": "Android",
            "thumbnail": {
                "caption": null,
                "created_at": 1533574069,
                "height": 960,
                "id": 793,
                "image_file": {
                    "created_at": 1537865617,
                    "file_size": 89069,
                    "id": 4923,
                    "is_published": 1,
                    "original_filename": "evrsync_logo.png",
                    "project_id": null,
                    "updated_at": 1537865617
                },
                "image_file_right": null,
                "is_background": 0,
                "is_published": 1,
                "is_stereo": 0,
                "project_id": null,
                "updated_at": 1537865617,
                "width": 960
            },
            "updated_at": 1537865618,
            "version": "0.0.6"
        }
    ],
    "storage_quota": 50000,
    "storage_used": 4,
    "text_color": null,
    "updated_at": 1533029442
}

version=1.4

This adds the aux data to projects. This is used by the evrsync_solo player. aux is handled as string and not interpreted or checked. The client software must handle validation and graceful fallback.

Description:

{
    "aux": "<auxiliary client configuration>",
    "background": <image id>,
    "code": "<project code>",
    "created_at": <timestamp>,
    "has_password": <1 if project password is set; 0 otherwise>,
    "id": <project id>,
    "images": [
        {
            "caption": "<image caption, null for thumbnails>",
            "created_at": <timestamp>,
            "height": <pixel height>,
            "id": <image id>,
            "image_file": {
                "created_at": <timestamp>,
                "file_size": <file size in bytes>,
                "id": <datafile id>,
                "is_published": 1,
                "original_filename": "<filename of the uploaded file>",
                "project_id": <parent project id>,
                "updated_at": <timestamp>
            },
            "image_file_right": <null or data structure like "image_file">,
            "is_background": <1 if the image is a backgroun, 0 otherwise>,
            "is_published": 1,
            "is_stereo": <1 if image is stereo (has image_file_right), 0 otherwise>,
            "project_id": <parent project id>,
            "updated_at": <timestamp>,
            "width": <pixel width>
        },
        ...
    ],
    "is_checkin_required": <1 if devices must check in to the project, 0 otherwise>,
    "is_published": 1,
    "is_visible": <1 if the project is published and has a valid license, 0 otherwise>,
    "lock": <null or id of job locking the project>,
    "logo": <image id>,
    "logo_blend": <null or "alpha", "add", "multiply">,
    "logo_position": "<logo position vector as string, x,y,z, default 0,-1.5,12>",
    "logo_rotation": "<logo rotation vector as string, u,v,w, default 0,0,0>",
    "max_clients": <max devices allowed in the project by currently valid licenses>,
    "max_movies": <max movies allowed in the project by currently valid licenses>,
    "media": [
        {
            "created_at": <timestamp>,
            "endimage": <null or image-id>,
            "id": <media id>,
            "is_published": 1,
            "movies": [
                {
                    "bitrate": <average bits per second>,
                    "created_at": <timestamp>,
                    "duration": <duration in seconds>,
                    "fps": <frames per second>,
                    "height": <pixel height>,
                    "id": <movie id>,
                    "is_fully_keyframed": <if 1, all frames are keyframes, otherwise 0>,
                    "is_stereo": <if 1, top-down stereo; 0 otherwise>,
                    "keyframes": [ <array of keyframe times> ],
                    "lang": "<movie language>",
                    "media_id": <parent media id>,
                    "movie_file": {<datafile data, see image_file above>},
                    "project_id": <parent project id>,
                    "subtitle": "<movie subtitle>",
                    "title": "<movie title>",
                    "updated_at": <timestamp>,
                    "width": <pixel width>
                }
            ],
            "project_id": <parent project id>,
            "show_logo": <media show logo flag>,
            "startimage": <null or image id>,
            "thumb_sphere": <media thumb sphere flag>,
            "thumbnail": <image id>,
            "updated_at": <timestamp>
        },
        ...
    ],
    "movies_used": <number of movies in the project, for licensing purposes>,
    "name": "<project display name>",
    "software": [
        {
            "binary_file": {null or datafile data},
            "build_type": <null or client software build type>,
            "created_at": <timestamp>,
            "description": <null or software description>,
            "download_url": <null or external download url>,
            "filename": "<suggested filename>",
            "id": <software id>,
            "is_custom": <1 if custom build, 0 otherwise>,
            "is_published": 1,
            "label": "<human readable label>",
            "platform": "<platform label>",
            "thumbnail": {<image and image_file data>}
            "updated_at": <thumbnail>,
            "version": "<software version string>"
        },
        ...
    ],
    "storage_quota": <max storage allowed in project, in million bytes>,
    "storage_used": <storage used in project, in million bytes>,
    "text_color": <null or string text color r,g,b,a>,
    "updated_at": <timestamp>
}

Sample output:

{
    "aux": "{\"solo\":{\"hello\":true}}",
    "background": 247,
    "code": "test",
    "created_at": 1506589577,
    "has_password": 0,
    "id": 15,
    "images": [
        {
            "caption": null,
            "created_at": 1506589577,
            "height": 2048,
            "id": 246,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 96295,
                "id": 1656,
                "is_published": 1,
                "original_filename": "default_logo.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": "[Default background]",
            "created_at": 1506589577,
            "height": 2048,
            "id": 247,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 1965328,
                "id": 1657,
                "is_published": 1,
                "original_filename": "default_background.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_background": 1,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": null,
            "created_at": 1527675670,
            "height": 120,
            "id": 639,
            "image_file": {
                "created_at": 1527675670,
                "file_size": 1758,
                "id": 3842,
                "is_published": 1,
                "original_filename": "default_thumbnail.png",
                "project_id": 15,
                "updated_at": 1527675670
            },
            "image_file_right": null,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "updated_at": 1527675670,
            "width": 160
        }
    ],
    "is_checkin_required": 0,
    "is_published": 1,
    "is_visible": 1,
    "lock": null,
    "logo": 246,
    "logo_blend": null,
    "logo_position": "0,-1.5,12",
    "logo_rotation": "0,0,0",
    "max_clients": 1000,
    "max_movies": 1000,
    "media": [
        {
            "created_at": 1527675670,
            "endimage": null,
            "id": 345,
            "is_published": 1,
            "movies": [
                {
                    "bitrate": 243216,
                    "created_at": 1527675671,
                    "duration": 60.0,
                    "fps": 30.0,
                    "height": 960,
                    "id": 378,
                    "is_fully_keyframed": 0,
                    "is_stereo": 0,
                    "keyframes": [
                        0.0,
                        8.333333,
                        16.666667,
                        25.0,
                        33.333333,
                        41.666667,
                        50.0,
                        58.333333
                    ],
                    "lang": "en",
                    "media_id": 345,
                    "movie_file": {
                        "created_at": 1537864181,
                        "file_size": 1824120,
                        "id": 4917,
                        "is_published": 1,
                        "original_filename": "blink-noaudio.mp4",
                        "project_id": 15,
                        "updated_at": 1537864181
                    },
                    "project_id": 15,
                    "subtitle": "",
                    "title": "Blinkenlights",
                    "updated_at": 1537864181,
                    "width": 1920
                }
            ],
            "project_id": 15,
            "show_logo": 0,
            "startimage": null,
            "thumb_sphere": 0,
            "thumbnail": 639,
            "updated_at": 1537864191
        }
    ],
    "movies_used": 1,
    "name": "test",
    "software": [
        {
            "binary_file": {
                "created_at": 1537865410,
                "file_size": 246954605,
                "id": 4922,
                "is_published": 1,
                "original_filename": "evrsync_test-android_mono-0.0.6.apk",
                "project_id": null,
                "updated_at": 1537865412
            },
            "build_type": "android_mono",
            "created_at": 1533574069,
            "description": null,
            "download_url": null,
            "filename": "evrsync_test-android_mono-0.0.6.apk",
            "id": 230,
            "is_custom": 0,
            "is_published": 1,
            "label": "evrsync_test-android_mono",
            "platform": "Android",
            "thumbnail": {
                "caption": null,
                "created_at": 1533574069,
                "height": 960,
                "id": 793,
                "image_file": {
                    "created_at": 1537865617,
                    "file_size": 89069,
                    "id": 4923,
                    "is_published": 1,
                    "original_filename": "evrsync_logo.png",
                    "project_id": null,
                    "updated_at": 1537865617
                },
                "image_file_right": null,
                "is_background": 0,
                "is_published": 1,
                "is_stereo": 0,
                "project_id": null,
                "updated_at": 1537865617,
                "width": 960
            },
            "updated_at": 1537865618,
            "version": "0.0.6"
        }
    ],
    "storage_quota": 50000,
    "storage_used": 4,
    "text_color": null,
    "updated_at": 1533029442
}

version=1.5

This adds is_2d and slideshow_order to images and media. slideshow_order is guaranteed to be a sequential list of numbers across the media and images in the project, as long as only /editslideshow is used to change it.

Description:

{
    "aux": "<auxiliary client configuration>",
    "background": <image id>,
    "code": "<project code>",
    "created_at": <timestamp>,
    "has_password": <1 if project password is set; 0 otherwise>,
    "id": <project id>,
    "images": [
        {
            "caption": "<image caption, null for thumbnails>",
            "created_at": <timestamp>,
            "height": <pixel height>,
            "id": <image id>,
            "image_file": {
                "created_at": <timestamp>,
                "file_size": <file size in bytes>,
                "id": <datafile id>,
                "is_published": 1,
                "original_filename": "<filename of the uploaded file>",
                "project_id": <parent project id>,
                "updated_at": <timestamp>
            },
            "image_file_right": <null or data structure like "image_file">,
            "is_2d": <1 for 2d backgrounds, 0 otherwise>,
            "is_background": <1 if the image is a backgroun, 0 otherwise>,
            "is_published": 1,
            "is_stereo": <1 if image is stereo (has image_file_right), 0 otherwise>,
            "project_id": <parent project id>,
            "slideshow_order": <0 or 1-based index in slideshow>,
            "updated_at": <timestamp>,
            "width": <pixel width>
        },
        ...
    ],
    "is_checkin_required": <1 if devices must check in to the project, 0 otherwise>,
    "is_published": 1,
    "is_visible": <1 if the project is published and has a valid license, 0 otherwise>,
    "lock": <null or id of job locking the project>,
    "logo": <image id>,
    "logo_blend": <null or "alpha", "add", "multiply">,
    "logo_position": "<logo position vector as string, x,y,z, default 0,-1.5,12>",
    "logo_rotation": "<logo rotation vector as string, u,v,w, default 0,0,0>",
    "max_clients": <max devices allowed in the project by currently valid licenses>,
    "max_movies": <max movies allowed in the project by currently valid licenses>,
    "media": [
        {
            "created_at": <timestamp>,
            "endimage": <null or image-id>,
            "id": <media id>,
            "is_2d": <1 for 2d content, 0 otherwise>,
            "is_published": 1,
            "movies": [
                {
                    "bitrate": <average bits per second>,
                    "created_at": <timestamp>,
                    "duration": <duration in seconds>,
                    "fps": <frames per second>,
                    "height": <pixel height>,
                    "id": <movie id>,
                    "is_fully_keyframed": <if 1, all frames are keyframes, otherwise 0>,
                    "is_stereo": <if 1, top-down stereo; 0 otherwise>,
                    "keyframes": [ <array of keyframe times> ],
                    "lang": "<movie language>",
                    "media_id": <parent media id>,
                    "movie_file": {<datafile data, see image_file above>},
                    "project_id": <parent project id>,
                    "subtitle": "<movie subtitle>",
                    "title": "<movie title>",
                    "updated_at": <timestamp>,
                    "width": <pixel width>
                }
            ],
            "project_id": <parent project id>,
            "show_logo": <media show logo flag>,
            "slideshow_order": <0 or 1-based index in slideshow>,
            "startimage": <null or image id>,
            "thumb_sphere": <media thumb sphere flag>,
            "thumbnail": <image id>,
            "updated_at": <timestamp>
        },
        ...
    ],
    "movies_used": <number of movies in the project, for licensing purposes>,
    "name": "<project display name>",
    "software": [
        {
            "binary_file": {null or datafile data},
            "build_type": <null or client software build type>,
            "created_at": <timestamp>,
            "description": <null or software description>,
            "download_url": <null or external download url>,
            "filename": "<suggested filename>",
            "id": <software id>,
            "is_custom": <1 if custom build, 0 otherwise>,
            "is_published": 1,
            "label": "<human readable label>",
            "platform": "<platform label>",
            "thumbnail": {<image and image_file data>}
            "updated_at": <thumbnail>,
            "version": "<software version string>"
        },
        ...
    ],
    "storage_quota": <max storage allowed in project, in million bytes>,
    "storage_used": <storage used in project, in million bytes>,
    "text_color": <null or string text color r,g,b,a>,
    "updated_at": <timestamp>
}

Sample output:

{
    "aux": "{\"solo\":{\"hello\":true}}",
    "background": 247,
    "code": "test",
    "created_at": 1506589577,
    "has_password": 0,
    "id": 15,
    "images": [
        {
            "caption": null,
            "created_at": 1506589577,
            "height": 2048,
            "id": 246,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 96295,
                "id": 1656,
                "is_published": 1,
                "original_filename": "default_logo.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_2d": 0,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "slideshow_order": 0,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": "[Default background]",
            "created_at": 1506589577,
            "height": 2048,
            "id": 247,
            "image_file": {
                "created_at": 1506589577,
                "file_size": 1965328,
                "id": 1657,
                "is_published": 1,
                "original_filename": "default_background.png",
                "project_id": 15,
                "updated_at": 1506589577
            },
            "image_file_right": null,
            "is_2d": 1,
            "is_background": 1,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "slideshow_order": 1,
            "updated_at": 1506589577,
            "width": 2048
        },
        {
            "caption": null,
            "created_at": 1527675670,
            "height": 120,
            "id": 639,
            "image_file": {
                "created_at": 1527675670,
                "file_size": 1758,
                "id": 3842,
                "is_published": 1,
                "original_filename": "default_thumbnail.png",
                "project_id": 15,
                "updated_at": 1527675670
            },
            "image_file_right": null,
            "is_2d": 0,
            "is_background": 0,
            "is_published": 1,
            "is_stereo": 0,
            "project_id": 15,
            "slideshow_order": 0,
            "updated_at": 1527675670,
            "width": 160
        }
    ],
    "is_checkin_required": 0,
    "is_published": 1,
    "is_visible": 1,
    "lock": null,
    "logo": 246,
    "logo_blend": null,
    "logo_position": "0,-1.5,12",
    "logo_rotation": "0,0,0",
    "max_clients": 1000,
    "max_movies": 1000,
    "media": [
        {
            "created_at": 1527675670,
            "endimage": null,
            "id": 345,
            "is_2d": 0,
            "is_published": 1,
            "movies": [
                {
                    "bitrate": 243216,
                    "created_at": 1527675671,
                    "duration": 60.0,
                    "fps": 30.0,
                    "height": 960,
                    "id": 378,
                    "is_fully_keyframed": 0,
                    "is_stereo": 0,
                    "keyframes": [
                        0.0,
                        8.333333,
                        16.666667,
                        25.0,
                        33.333333,
                        41.666667,
                        50.0,
                        58.333333
                    ],
                    "lang": "en",
                    "media_id": 345,
                    "movie_file": {
                        "created_at": 1537864181,
                        "file_size": 1824120,
                        "id": 4917,
                        "is_published": 1,
                        "original_filename": "blink-noaudio.mp4",
                        "project_id": 15,
                        "updated_at": 1537864181
                    },
                    "project_id": 15,
                    "subtitle": "",
                    "title": "Blinkenlights",
                    "updated_at": 1537864181,
                    "width": 1920
                }
            ],
            "project_id": 15,
            "show_logo": 0,
            "slideshow_order": 0,
            "startimage": null,
            "thumb_sphere": 0,
            "thumbnail": 639,
            "updated_at": 1537864191
        }
    ],
    "movies_used": 1,
    "name": "test",
    "software": [
        {
            "binary_file": {
                "created_at": 1537865410,
                "file_size": 246954605,
                "id": 4922,
                "is_published": 1,
                "original_filename": "evrsync_test-android_mono-0.0.6.apk",
                "project_id": null,
                "updated_at": 1537865412
            },
            "build_type": "android_mono",
            "created_at": 1533574069,
            "description": null,
            "download_url": null,
            "filename": "evrsync_test-android_mono-0.0.6.apk",
            "id": 230,
            "is_custom": 0,
            "is_published": 1,
            "label": "evrsync_test-android_mono",
            "platform": "Android",
            "thumbnail": {
                "caption": null,
                "created_at": 1533574069,
                "height": 960,
                "id": 793,
                "image_file": {
                    "created_at": 1537865617,
                    "file_size": 89069,
                    "id": 4923,
                    "is_published": 1,
                    "original_filename": "evrsync_logo.png",
                    "project_id": null,
                    "updated_at": 1537865617
                },
                "image_file_right": null,
                "is_background": 0,
                "is_published": 1,
                "is_stereo": 0,
                "project_id": null,
                "updated_at": 1537865617,
                "width": 960
            },
            "updated_at": 1537865618,
            "version": "0.0.6"
        }
    ],
    "storage_quota": 50000,
    "storage_used": 4,
    "text_color": null,
    "updated_at": 1533029442
}

version=2.0