Ayvri V2 REST API
The Ayvri API let’s you create new Ayvri activities and scenes to use in your application.
It is a REST based API with predictable, resource-oriented URLs, and uses HTTP response codes to indicate API errors. We use built-in HTTP features, like HTTP authentication and HTTP verbs, which are understood by off-the-shelf HTTP clients. JSON is returned by all API responses, including errors.
We offer two methods for integrating with the Ayvri API. The Connect style of integration uses the Authorization Code Flow from OAuth2 to allow a user to authorize you to act on their behalf to upload activities and scenes. There are some limitations to what can be done using the Connect integration method. The Commercial integration style gives you access to all api features.
Connect Authentication
Ayvri Connect lets your app act on behalf of Ayvri users. Ayvri attempts to follow the OAuth2 Authorization Code Flow as closely as possible.
Before you start you will need to:
- Visit https://ayvri.com/oauth2 to sign up for an Ayvri Connect api account
- Generate a pair of keys. These are you Client ID and Client Secret
- Configure one or more Redirect Urls
Get an Authorization Code for a User
For Ayvri to issue you an Authorization Code, you must send an Ayvri user to Ayvri’s authorization endpoint.
The Ayvri authorization endpoint is https://ayvri.com/authorize
The authorization endpoint will be expecting the following query parameters:
- client_id - Issued when you generated your key pair
- redirect_uri - Must be one of the Redirect Urls configured on your api account configuration page
- response_type - must be set to the string “code”
- state - (optional) any application specific state you need to track the process. We won’t use this and will return it to you in the second stage of the flow.
An example authorization request might look like https://ayvri.com/authorize?client_id=myclient134&redirect_uri=https://my-cool-app.com/oauth2_callback&response_type=code&state=req123
Ayvri will validate the supplied redirect_uri against the supplied client_id and present the user the option to grant your app authorization.
Success
When a user authorizes your app, Ayvri will redirect the user to the redirect_uri
supplied with the authorization request. Ayvri will supply the following query parameter with the response:
- code - the Authorization Code issued for this request. This can be used to get an access token and refresh token
- state - if supplied with the request, state is returned with the response.
An example successful response might look like https://my-cool-app.com/oauth2_callback?code=894759329&state=req123
.
Failure
If Ayvri deems it safe to do so, the user may return to you with an error response. Ayvri will provide the following query parameters with the error response:
- error - will be either
invalid_request
if the request was invalid, oraccess_denied
if the user refused authorization - error_description - a human readable description
If the redirect_uri could not be validated, no error will be reported to you.
Exchange the Authorization Code for Access and Refresh Tokens
Exchange Authorization Code for Access and Refresh Tokens
var request = require('request');
var base64_credentials = new Buffer(CLIENT_ID + ":" + PASSWORD).toString('base64')
request.post({
url: 'https://api.ayvri.com/2.0/token',
headers: { Authorization: 'Basic ' + base64_credentials },
body: encodeUri('grant_type=authorization_code&code={$code}'),
function(err, res, body) {
grant = JSON.parse(body);
console.log(grant);
}
);
curl -X POST https://api.ayvri.com/2.0/token \
-u $CLIENT_ID:$PASSWORD \
-d 'grant_type=authorization_code&code={$code}'
Example Response
{
access_token: '80a0jacec98f0ace0acacca.098f89cjca09ec0aca0j.0accejcs',
refresh_token: '08aecjaca0aec0jacejiecjac.09ec8acjce0a0ecjaaciajc.0acjc0',
token_type: 'bearer',
expires_in: 604800,
}
An authorization_code must be exchanged for access and refresh tokens by issuing an authenticated request to the token endpoint. Ayvri’s token endpoint is https://api.ayvri.com/2.0/token
. Ayvri will authenticate the request using HTTP Basic access authentication and expects a URI encoded body with the grant_type and code parameters set.
Once an access token has been recovered it may be used as a normal bearer token with other endpoints on the API. The access token gives you authorization to upload activities and scenes on behalf of the user that authorized you to do so. Scenes uploaded will be added to their profile and may be removed by them without your knowledge. The tokens issued through the Connect integration style have the following limitations:
- Only a single activity may be added per scene
- No
media
may be attached to the scene.
Commercial Authentication
Getting an API access token
var request = require('request');
var base64_credentials = new Buffer(CLIENT_ID + ':' + PASSWORD).toString('base64');
request.post(
{
url: 'https://api.ayvri.com/2.0/auth',
headers: { Authorization: 'Basic ' + base64_credentials },
form: { grant_type: 'client_credentials' },
},
function(err, res, body) {
grant = JSON.parse(body);
console.log(grant);
},
);
curl -X POST https://api.ayvri.com/2.0/auth \
-u $CLIENT_ID:$PASSWORD \
-d 'grant_type=client_credentials'
Example Response
{
access_token: '80a0jacec98f0ace0acacca.098f89cjca09ec0aca0j.0accejcs'
refresh_token: '08aecjaca0aec0jacejiecjac.09ec8acjce0a0ecjaaciajc.0acjc0'
token_type: 'bearer',
expires_in: 604800,
}
Getting a new access token using a refresh token
var request = require('request');
var base64_credentials = new Buffer(CLIENT_ID + ':' + PASSWORD).toString('base64');
request.post(
{
url: 'https://api.ayvri.com/2.0/auth',
headers: { Authorization: 'Basic ' + base64_credentials },
form: { grant_type: 'refresh_token', refresh_token: grant.refresh_token },
},
function(err, res, body) {
grant = JSON.parse(body);
console.log(grant);
},
);
curl -X POST https://api.ayvri.com/2.0/auth \
-u $CLIENT_ID:$PASSWORD \
-d "grant_type=refresh_token&refresh_token=$REFRESH_TOKEN"
Ayvri v2 api uses JWT authentication. In order to get a valid auth token, you will need to pass your api account details to the authentication endpoint. This will return a grant which includes an access_token
and a refresh_token
. Tokens expire after 1 week. When your token expires,you can retrieve a new access_token
by passing the refresh_token
from your original grant to the
The auth endpoint will return a json object which contains the access_token
and refresh_token
.
Getting Started : 101
With an access token, create a simple activity & scene
var request = require('request');
request.post(
{
url: 'https://api.ayvri.com/2.0/activity',
headers: { Authorization: YOUR_ACCESS_TOKEN },
body: JSON.stringify({ activityType: ADD_ACTIVITY_TYPE }),
},
function(err, res, body) {
var resBody = JSON.parse(body);
var uploadUrl = resBody.uploadUrl;
var activityId = resBody.activityId;
request.post(
{
url: uploadUrl,
body: YOUR_GPX_OR_IGC_DATA,
},
function(err, res, body) {
request.post(
{
url: 'https://api.ayvri.com/2.0/scene',
headers: { Authorization: YOUR_ACCESS_TOKEN },
body: JSON.stringify({ activities: [activityId] }),
},
function(err, res, body) {
console.log(JSON.parse(body));
var embedUrl = JSON.parse(body).embedUrl;
},
);
},
);
},
);
Once you have an authentication token, you can get started uploading activities and creating scenes.
Creating a scene is a 3 step process
- Create the activity - which will return a url to upload the GPX or IGC track
- Upload the track
- Create a Scene including the activityId[s] you want to include in the scene.
The example here shows how to create a simple scene using the javascript request library, and is used as a simple example to get you started.
Once you can create an activity and a scene, see below for more details and capabilities to customize your scene.
Create Activity
Creating a new activity
var request = require('request');
var activityDetails = {
activityId: 'UTMB_2017',
title: 'UTMB 2017',
activityType: 'Run',
avatar: {
name: 'Brook Martin',
},
color: 'rgb(40,15,110)',
};
request.post(
{
url: 'https://api.ayvri.com/2.0/activity',
headers: { Authorization: grant.access_token },
body: JSON.stringify(activityDetails),
},
function(err, res, body) {
var uploadUrl = JSON.parse(body).uploadUrl;
console.log(body);
},
);
curl -X POST https://api.ayvri.com/2.0/activity \
-H "Authorization: accesss_token" \
-H "Accept: application\json"
-d "{\"activityType\": \"Run\"}"
Example Response
Creating an activity will save the activity details to your api account and return an upload URL to use to push a GPS or IGC file up to the server to be processed.
Activity has the following attributes
- activityId: string | optional, must be unique - if not provided a unique ID will be created
- title: string | optional
- activityType: string | required - the activity type defines some base properties in initializing the player. The list of activityTypes can be found at https://api.ayvri.com/2.0/activityTypes
- live: boolean | optional - for creating an activity which will be live tracked. Live tracks will not return an upload url
- avatar: optional object
** avatar object {
name: string | optional - name to associated with the activity
image: string | options - image url
} - color: string | optional - accepts rgb or hex in the format ‘rgb(r,g,b)’ or ‘#RRGGBB’
- opacity: number between 0 - 1 - sets the avatar path to be opaque.
Upload Activity
Upload a gps or igc file
var require('fs');
fs.readFile(file, function(err, data) {
request.put({
url: uploadUrl,
body: data
},(err, res, body) => {
//should return a 200 response
});
})
curl -X PUT -T file uploadUrl \
Use the upload url in the response of the created activity to push the activity gps to ayvri. The upload url is valid for 3 minutes.
When the url is uploaded, ayvri will process the tracks to prepare them to be displayed in the viewer. This processing takes only a few seconds.
When a file has been uploaded, ayvri will process the track to get it ready to be displayed in a Scene.
Create Scene
Create a Scene containing activities
var request = require('request');
var sceneDetails = {
title: 'choose a unique title',
activities: [{activityId: activityId_1}, {activityId: activityId_2}...]
}
request.post({
url: 'https://api.ayvri.com/2.0/scene',
headers: { Authorization: access_token },
body: JSON.stringify(sceneDetails)
},
function(err, res, body) {
var embedUrl = JSON.parse(body).embedUrl
});
curl -X POST https://api.ayvri.com/2.0/scene \
-H "Authorization: access_token"
-d '{"activities":[{activityId: activityId_1}, {activityId: activityId_2}...]}'
When the activities have been uploaded and processed, create a Scene to display the activity or activities.
A Scene has the following root attributes, see below for child object details
- sceneId: string | optional, must be unique - if not provided a unique ID will be created
- title: string | optional - a title to use to refrence your Scene
- activities: array | required if live attribute is false
- live: boolean | optional - set to true if this Scene is to recieve live tracking points via the client-side API
- loading: object | optional/recommended - set a background image and image to use for the play/start button
- media: array | optional - array of media items to be included in the scene
- exitScreen: array | optional - an array of images and links to show on the exit screen
- distanceMarkers: object | optional - an object to define the frequency and color of distance markers along a track
Activities
A Scene’s activities array is a subset of the activity details
- activity: object | required, subset of activity attributes
- activityId: string | required - the activityId you want to include in this scene
Custom Loading Screen
var request = require('request');
var sceneDetails = {
title: 'choose a unique title',
activities: [...],
loading: {
background: "https://url.to/background-image",
startImg: "https://url.to/image-to-replace-play-button"
}
}
request.post({
url: 'https://api.ayvri.com/2.0/scene',
headers: { Authorization: access_token },
body: JSON.stringify(sceneDetails)
},
function(err, res, body) {
var embedUrl = JSON.parse(body).embedUrl
});
curl -X POST https://api.ayvri.com/2.0/scene \
-H "Authorization: access_token"
-d '{"activities":[...], "loading":{"background":"https://url.to/background-image"}}'
You can set a custom loading screen on your Scene. ayvri does not provide a background image for your loading screen (yet). The loading screen will not be used if the Scene is set to autoplay.
- background: url string | optional/recommended - an image to use as the background before a user presses play
- startImg: url string | optional - an image to use as the play button on top of the background image
Sharing Image
Set an image to be used by social networks when your Scene is shared.
- shareImg: url string | optional/recommended
Default Speed & Distance
For fine control over player speed and the follow distance of an activity, use the Segments
, however you can set a simple default which will override the built in defaults
- defaultSpeed: number | optional - a multiple of real-time
- defaultTargetDistance: number | optional - the distance (zoom) the camera will follow an activity through the scene
Autoplay
The Scene can be set to autoplay when loaded, so the loading page which not be seen.
- autoplay: boolean | optional
Stats
var request = require('request');
var sceneDetails = {
title: 'choose a unique title',
activities: [...],
stats: ["speed","altitude","gradient","localTime"],
defaultStatsVisible: 3
}
request.post({
url: 'https://api.ayvri.com/2.0/scene',
headers: { Authorization: access_token },
body: JSON.stringify(sceneDetails)
},
function(err, res, body) {
var embedUrl = JSON.parse(body).embedUrl
});
curl -X POST https://api.ayvri.com/2.0/scene \
-H "Authorization: access_token"
-d '{"activities":[...], "stats": ["speed","altitude","gradient","localTime"], "defaultStatsVisible": 3 }'
The Scene can display stats specific to the target activity such as speed, altitude, local time and more.
valid stats are"speed","airSpeed","boatSpeed","distance","altitude","gradient","climbrate","glideRatio","elevation","altitude","altitudeFromStart","localTime","gmtTime"
- stats: array of strings | optional
By default only the first stat in the array is displayed and other stats can be viewed by the user by click a tab which opens the other stats to view.
The number of stats visible by default can be set with defaultStatsVisible
- defaultStatsVisible: number | optional, default = 1
Media
Photos and other types of media can be added into the scene by defining them in the Scene’s media
array.
Media items have the following common properties:
- mediaId: string | recommended - a unique ID to reference this media item
- mediaType: string | required - [“photo”]
Photo
var request = require('request');
var sceneDetails = {
title: 'choose a unique title',
activities: [{ activityId: activityId_1 }],
media: [
{
mediaType: 'photo',
source: 'https://url.to/image-file',
target: {
activityId: 'activityId_1',
time: '2017-06-02T12:11:00Z',
},
},
],
};
request.post(
{
url: 'https://api.ayvri.com/2.0/scene',
headers: { Authorization: access_token },
body: JSON.stringify(sceneDetails),
},
function(err, res, body) {
var embedUrl = JSON.parse(body).embedUrl;
},
);
curl -X POST https://api.ayvri.com/2.0/scene \
-H "Authorization: access_token"
-d '{"activities":[{ "activityId": "activityId_1" }], "media": [{"mediaType": "photo", "source": "https://url.to/image-file", "target": { "activityId": "activityId_1", "time": "2017-06-02T12:11:00Z"} }] }'
A photo that will be displayed at a point along a specific activity. When the scene reaches the specified time, the camera will focus on the photo for several seconds. The target activity must be in this scene, and the target time must be between the start and end time of the target activity.
In addition to the common media properties listed above, Photo also has the following properties:
- source: string | required - the url of the image
- target: object | required
- time: string | required - in ISO8601 format eg: “2017-06-02T12:11:00Z”
- activityId: string | required - the activity along which this photo will be placed
- accountId: string | optional - if the target activity belongs to another account, specify it here
Segments
var request = require('request');
var sceneDetails = {
title: 'choose a unique title',
activities: [...],
segments: [
{
time: "2017-11-03T12:11:00Z",
target: "activity",
targetId: "uniqueTargetId",
speed: 300,
targetDistance: 2000
},
{
time: "2017-11-03T12:18:00Z",
target: "activity",
targetId: "uniqueTargetId",
speed: 100,
targetDistance: 1200
}
]
}
request.post({
url: 'https://api.ayvri.com/2.0/scene',
headers: { Authorization: access_token },
body: JSON.stringify(sceneDetails)
},
function(err, res, body) {
var embedUrl = JSON.parse(body).embedUrl
});
curl -X POST https://api.ayvri.com/2.0/scene \
-H "Authorization: access_token"
-d '{"activities":[...], "segments": [{"time":"2017-11-03T12:11:00Z", "target": "activity":, "targetId":"uniqueTargetId", "speed": 300, "targetDistance": 2000}, {"time":"2017-11-03T12:18:00Z", "target": "activity":, "targetId":"uniqueTargetId", "speed": 100, "targetDistance": 1200}] }'
Segments define the activity of the camera in the scene including which activity is followed, how closely the camera follows the activity, and how quickly time passes.
There are multiple uses for segments, for instance if there is a less interesting portion of a track, a larger followDistance
and higher speed
automatically gives the user a quick view of this area. Over a more interesting portion of the track, a closer followDistance
and slower speed
will bring a larger focus on that segment.
Additionally, for scenes with multiple tracks or activities, like a triathlon, adventure race or other multi-track scenes, a segment lets the scene creator define when to switch from viewing one activity to move to view another. For instance, viewing the swim portion of a triathlon, and then at a specified time, switching to follow the bike portion.
If setting segments, it is required that the first segment be defined at the beginning of the scene. If no segments are defined, the player will use follow the first activity at the pre-defined default speed and target distance.
- segments: Array of segment objects | optional
- time: number | required - in ISO8601 format eg: “2017-06-02T12:11:00Z”
- target: string | valid value “activity” - required if the target is an activity (other target types will be added in the future)
- targetId: string | id of the target to follow
- targetDistance: number | optional - this distance in metres to follow the target
- speed: number | optional - the speed to set for this segment as a multiplier of real-time
Exit Screen
var request = require('request');
var sceneDetails = {
title: 'choose a unique title',
activities: [...],
exitScreen: [
{
"image": "https://url.to/image",
"link": "https://url.to/your-page"
}
]
}
request.post({
url: 'https://api.ayvri.com/2.0/scene',
headers: { Authorization: access_token },
body: JSON.stringify(sceneDetails)
},
function(err, res, body) {
var embedUrl = JSON.parse(body).embedUrl
});
curl -X POST https://api.ayvri.com/2.0/scene \
-H "Authorization: access_token"
-d '{"activities":[...], "exitScreen":[{"image": "https"://url.to/image", "link":"https://url.to/your-page"}] }'
When the scene is finished playing, you can add an optional 3 links with images to the exit screen. These images and links will be displayed above the sharing icons. The images will be evenly spaced across the screen and are fit into a 1:1 aspect ratio taking up 20% of the screeen width.
- exitScreen: array
- image: string | required - url to the image
- link: string | required - url to the go to when the user clicks the image
Distance Markers
var request = require('request');
var sceneDetails = {
title: 'choose a unique title',
activities: [{ activityId: 'someActivity' }],
distanceMarkers: {
frequency: 5,
color: '#4286f4',
height: 200,
},
};
request.post(
{
url: 'https://api.ayvri.com/2.0/scene',
headers: { Authorization: access_token },
body: JSON.stringify(sceneDetails),
},
function(err, res, body) {
var embedUrl = JSON.parse(body).embedUrl;
},
);
curl -X POST https://api.ayvri.com/2.0/scene \
-H "Authorization: access_token"
-d '{"activities":[{"activityId": "someActivityId"}], "distanceMarkers": {"frequency": 5, "color": "#4286f4"}}'
A scene can have distance markers evenly distributed across the length of a scene.
If the frequency is 5 and the users is viewing in metric, the markers will display at each 5 kilometer. If the user is viewing in imperial, the markers will be displayed at each 5 miles.
- distanceMarkers: Object
- frequency: number | optional - the distance between markers
- color: string | optional - the color of the marker in ‘rgb(r,g,b)’ or ‘#RRGGBB’
- height: number | optional - defines the height of distance markers in relative meters in the scene
Client API
With the Ayvri Client API, your scene can be updated in real-time via Javascript. This API is currently in beta. Contact us to enable the Client API on your API account and for access to the documentation.