back to http://www.sandglaz.com

Sandglaz REST API Documentation

Introduction

Overview

Terms

API Base URI

Common HTTP Response Codes

Authorization through OAuth2

Terminology

Register your Client

Ask the user to authorize your app using a browser

Use the authorization code to get an access token.

Use the Refresh Token to get another Access Token without User intervention

How to make an API call

User

Actions

[GET ‘/users/current]

Attributes

Grids

Attributes

Actions

Index [GET ‘/grids’]

Show [GET ‘/grids/:id’]

Create [POST ‘/grids’]

Update [PUT ‘/grids/:id’]

Destroy [DELETE ‘/grids/:id’]

Tasks

Attributes

Actions

Index [GET ‘/grids/:grid_id/tasks’]

Show [GET ‘/tasks/:id’]

Create [POST ‘/tasks’]

Update [PUT ‘/tasks/:id’]

Destroy [DELETE ‘/tasks/:id’]

Tags

Actions

Index [GET ‘/grids/:grid_id/tags]

Labels

Actions

Index [GET ‘/grids/:grid_id/labels]

Update [PUT ‘/grids/:grid_id/labels

Destroy [DELETE ‘/grids/:grid_id/labels?label[importance]=:importance’]

UserNotifications

Attributes

Actions

Index [GET ‘/user_notifications]

Destroy [DELETE ‘/user_notifications/:id’]

Grids Sharing (GridsUsers)

Attributes

Actions

Index (for a grid) [GET ‘/grids/:grid_id/shares’]

Create_all [POST ‘/grids/:grid_id/shares/create_all’]

Update [PUT ‘/grids/:grid_id/shares/:id’]

Update_all [PUT ‘/grids/:grid_id/shares/update_all’]

Destroy [DELETE ‘/grids/:grid_id/shares/:id’]

Recurrences (for tasks)

Attributes

Actions

Create [POST ‘/recurrences?task_id=:task_id’]

Update [PUT ‘/recurrences/:id’]

Destroy [DELETE ‘/recurrences/:id’]

Milestones (intervals)

Actions

Index [GET ‘/grids/:grid_id/intervals/]

Goto [GET ‘/grids/:grid_id/intervals/goto?time=:time’]

Update [PUT ‘/grids/:grid_id/intervals/:id]

Abilities

Actions

Grid abilities [GET ‘/grids/:id/abilities’]

Task abilities [GET ‘/grids/:id/tasks/abilities’]

Introduction

Overview

The Sandglaz API allows you to write applications that interact with Sandglaz. For instance you can use the Sandglaz API to:

The Sandglaz API uses OAuth 2 for authorization. OAuth 2 is an open standard for allowing users to authorize 3rd Party applications without the need for those applications to request the user’s credentials (username/password) or requiring the user to copy & paste API Keys. This makes it simpler for both the client developer and the end user, while maintaining high security. The Sandglaz API is also always over https; it returns an error for any request not over https.

The Sandglaz API has a RESTful interface, providing programmatic access to most of its data in predictable urls. It accepts and returns JSON content.

If your agent does not support a PUT or DELETE requests, do a POST request and set the header to override as follows:

X-HTTP-Method-Override : PUT

X-HTTP-Method-Override: DELETE

Unless otherwise stated all datetimes are returned in the user’s time zone showing the time zone offset e.g. 2012-03-12T02:00:00-04:00

The Sandglaz API offers Abilities Actions that make it easy to query the user’s permissions. This gives you an easy way of offering a superior user experience by checking for permissions prior to attempting various actions.

This documentation assumes that you are already familiar with Sandglaz. If you are not, sign up for an account.

Terms

The Sandglaz API is free and the terms of use can be found here

API Base URI

https://www.sandglaz.com

Common HTTP Response Codes

200 OK

401 Unauthorized:         e.g. {"error":"Invalid authentication token."}

403 Forbidden                e.g. request on a grid not shared with you

404 Not Found         e.g. route not found

400 Bad request        e.g. creating a grid without a name. {"name":["can't be blank"]}

Authorization through OAuth2

Terminology

Client: Registered OAuth2 Client. E.g. Your App

Access_token: Short lived token used to perform subsequent requests. Valid for 15 minutes

Refresh_token: Long lived token used to request new access tokens without requiring user intervention to re-authorize. Valid for 1 month.

Authorization_code: Very short lived token created to allow you to request an access token after a user has gone through the authorization flow. Valid for 1 minute.

Register your Client

Each Client must have the following information

client_id

client_secret

redirect_uri: This is the url that Sandglaz will redirect the user to after they enter their credentials. See section Ask the user to authorize your app using a browser

website: This is your company’s website.

name: This is the name of your app

To obtain a client_id and client_secret, email info@sandglaz.com with details about your project proposal include your app name, website and redirect_uri if you know them.

Ask the user to authorize your app using a browser

Use a browser to do a GET request to allow the user authorize your app. The user will be asked to sign in to Sandglaz if they were not already signed in.

Using the "application/x-www-form-urlencoded" format, create a GET request to oauth2/authorize? with response_type code and client_id = {client_id}

Example:

GET https://www.sandglaz.com/oauth2/authorize?response_type=code&client_id={client_id}

Explanation:

This will redirect the browser to a page where the user will enter their credentials and click accept.

 

Upon clicking accept, it will redirect to your redirect_uri with the authorization code

Note: For installed apps, if possible you can have a listener in the users mobile/computer, and have the redirect_uri set to localhost:port. Otherwise, you can use www.sandglaz.com/pages/oauth2_code as your redirect path and detect the authorization code from the url or from the title and then close the window to hide the code. The code is only valid for 1 minute.

In the url the code be present will be as a param. for example: ?code=123456789

In the title it will be appended before “- Sandglaz”. for example: 123456789 - Sandglaz

Response:

the browser will redirect as follows:

{redirect_uri}?code={code}

e.g.

http://www.sandglaz.com/pages/oauth2_code?code=123456789

or

http://localhost:9393/oauth/callback?code=123456789

Use the authorization code to get an access token.

Create a POST request to https://www.sandglaz.com/oauth2/token

with Content-Type: application/x-www-form-urlencoded
Include the following parameters:
grant_type=authorization_code

code={code}

client_id={client_id}

client_secret={client_secret}

redirect_uri={redirect_uri}

Example:

curl --data "grant_type=authorization_code&code={code}&client_id={client_id}&client_secret={client_secret}&redirect_uri={redirect_uri}" https://www.sandglaz.com/oauth2/token

curl --data "grant_type=authorization_code&code=123456789&client_id=xxxxxx&client_secret=yyyyyy&redirect_uri=http://localhost:9393/oauth/callback" https://www.sandglaz.com/oauth2/token

Response:

{"access_token":"e873d59e726ef2f97720779ef27d52d2","token_type":"bearer","expires_in":899,"refresh_token":"a5486a1fb3a9264d9f5391f01026722b"}

Explanation:

The access_token and refresh_token are for the user whose credentials were entered in Step 1. Store the tokens in relation to that user for later use. The refresh token is valid for a month, so the user doesn’t have to authenticate for a month.

Use the Refresh Token to get another Access Token without User intervention

Create a POST request to https://www.sandglaz.com/oauth2/token

with Content-Type: application/x-www-form-urlencoded
Include the following parameters:
grant_type=refresh_token

client_id={client_id}

client_secret={client_secret}

refresh_token={refresh_token}

Example:

curl --data "grant_type=refresh_token&refresh_token={refresh_token}&client_id={client_id}&client_secret={client_secret}" https://www.sandglaz.com/oauth2/token

curl --data "grant_type=refresh_token&refresh_token=bb4f173e2299c14dd937e52cee24f64b&client_id=1xxxxxxx&client_secret=yyyyyyy" https://www.sandglaz.com/oauth2/token

Response:

{"access_token":"90bb5c763e12632b85afd122b005cf04","token_type":"bearer","expires_in":899,"refresh_token":"bb4f173e2299c14dd937e52cee24f64b"}

How to make an API call

Use a valid access token in the header of your API requests for authorization:

Authorization: Bearer {access_token}

You must also set the following headers

content-type: application/json

Accept: application/json

Example:

If your access_token is e873d59e726ef2f97720779ef27d52d2, you can do:

curl --header "Authorization: Bearer e873d59e726ef2f97720779ef27d52d2" --header "content-type: application/json" --header “Accept: application/json” https://www.sandglaz.com/grids/

User

Actions

[GET ‘/users/current]

returns a JSON collection of data that belongs to the authenticated user. The data includes, the user’s name, email, username, tasks in ‘My Tasks’, grids, and tags in ‘My Tasks’.

Attributes

id

integer

A unique identifier for the user.

email

string

email

name

string

name

username

string

@username

tasks

see tasks

an array of the user’s tasks in ‘My Tasks’

tags

an array of the user’s ‘My Tasks’ hash tags

grids

see grids

and array of the user’s grids

{"user":

{"id":1,

"name":"John",

"username":"@john",

"email":"john@example.com",

"tasks":

[{"id":241,

"grid_id":null,

"name":"and now lets see. oh it stopped happening. ",

"importance":0,

"interval":"2013-07-25T09:33:11-07:00",

"description":"",

"due_date":null,

"completed":false,

"position":1,

"created_at":"2013-07-24T14:38:06-07:00",

"updated_at":"2013-07-25T09:33:11-07:00",

"user_position":"1",

"user":true,

"recurrence":

{"unit":"days",

"rule":"completion",

"value":1}}],

"tags":

[{"hash_tag":{"name":"marketing"}}],

"grids":

[{"infinity_grid":

{"id":17,"name":"Business",

"description":null,

"num_important":2,

"grids_users_count":1,

"created_at":"2013-07-17T16:06:08-07:00",

"updated_at":"2013-07-21T22:40:29-07:00",

"position":"7",

"role":"owner",

"locked":"f",

"grid_abilities":

{"updatable":true,

"destroyable":true,

"locked":false},

"task_abilities":

{"updatable":true}}},

{"infinity_grid":

{"id":16,

"name":"Product",

"description":null,

"num_important":2,

"grids_users_count":1,

"created_at":"2013-07-17T16:02:06-07:00",

"updated_at":"2013-07-21T22:40:29-07:00",

"position":"6",

"role":"owner",

"locked":"f",

"grid_abilities":

{"updatable":true,

"destroyable":true,

"locked":false},

"task_abilities":{"updatable":true}}}]}}

Grids

Attributes

id

integer

A unique identifier for the grid. Use this value in the url of update and destroy requests.

description

string

not currently used

grids_users_count

integer

number of people sharing this grid

name

string

grid name

num_important

integer

number of rows

updated_at

datetime

time grid was last updated

created_at

datetime

time grid was created

Actions

Index [GET ‘/grids’]

returns a JSON collection of all grids for the authenticated user

reponse example:

200

[{"infinity_grid":

{"id":4,

"name":"marketing",

"description":null,

"num_important":2,

"grids_users_count":2,

"created_at":"2013-07-16T08:23:44-07:00",

"updated_at":"2013-07-21T22:40:28-07:00",

"position":"2",

"role":"owner",

"locked":"f"}},

{"infinity_grid":

{"id":17,

"name":"development",

"description":null,

"num_important":2,

"grids_users_count":1,

"created_at":"2013-07-17T16:06:08-07:00",

"updated_at":"2013-07-21T22:40:29-07:00",

"position":"1",

"role":"owner",

"locked":"f"}}]

Note: position, role and locked attributes are for the current user and is the same as the position, role and locked attributes in grids_users (sharing) objects. It is provided for convenience.

Show [GET ‘/grids/:id’]

return the JSON representation of the grid

response example:

200

{"infinity_grid":

{"id":2,

"name":"Work",

"description":null,

"num_important":2,

"grids_users_count":1,

"created_at":"2013-07-16T08:10:05-07:00",

"updated_at":"2013-07-21T22:40:29-07:00",

"grid_abilities":

{"updatable":true,

"destroyable":true,

"locked":false},

"task_abilities":

{"updatable":true}}}

Note: grid_abilities and task_abilities are for the current user and is the same as Abilities. It is provided for convenience.

Create [POST ‘/grids’]

create a grid

accepted data (all of them are optional):

grid[name]

maximum 30 character

grid[num_important]

1 to 3

defaults to 2

data example:

grid[name]=something&grid[num_important]=2

success response example:

200

{"infinity_grid":

{"created_at":"2012-02-16T11:38:27-05:00",

"description":null,

"grids_users_count":1,

"id":340,

"name":"something",

"num_important":2,

"updated_at":"2012-02-16T11:38:27-05:00"}}

error response example:

400        {"name":["can't be blank"]}

Update [PUT ‘/grids/:id’]

updates a grid

accepts same data parameters as create.

data example:

grid[name]=something&grid[num_important]=2

All data is optional, but must send at least one grid attribute.

success response example:

200

{"infinity_grid":

{"created_at":"2012-02-16T11:38:27-05:00",

"description":null,

"grids_users_count":1,

"id":340,

"name":"something",

"num_important":2,

"updated_at":"2012-02-16T11:38:27-05:00"}}

error response example:

400

{"num_important":["must be less than or equal to 3"]}

Destroy [DELETE ‘/grids/:id’]

destroy a grid along with all associated labels, actions, etc for all users of that grid.

success response example:

200

{}

error response example:

403

You cannot delete this grid.

Tasks

Attributes

id

integer

A unique identifier for the task. Use this value in the url of update and destroy requests.

grid_id

integer

id of grid the task belongs to

name

string

the text of the task

importance

integer

the row the task belongs to in its grid

interval

datetime

used to find the interval the task belongs to for Infinity grids

description

string

task description

due_date

datetime

task due date

completed

boolean

true for completed tasks, false for uncompleted tasks

position

integer

sort position of a task in the grids box

recurrence

recurrence attributes in json format. refer to recurrence section

created_at

datetime

time the task was created

updated_at

datetime

time the task was last updated

Actions

Index [GET ‘/grids/:grid_id/tasks’]

returns a JSON collection of all tasks in a grid

response example:

200

[{"task":

{"completed":false,

"created_at":"2012-02-16T17:27:24-05:00",

"description":"",

"due_date":"",

"grid_id":335,

"id":2685,

"importance":0,

"interval":"2012-02-20T00:00:00-05:00",

"name":"a task",

"position":1,

"updated_at":"2012-02-16T17:27:45-05:00",

"recurrence":""}},

{"task":{"completed":false,

"created_at":"2012-02-16T17:27:23-05:00",

"description":"",

"due_date":"",

"grid_id":335,

"id":2684,

"importance":0,

"interval":"2012-02-16T17:27:24-05:00",

"name":"something",

"position":1,

"updated_at":"2012-02-16T17:45:16-05:00",

"recurrence":""}}}]

Show [GET ‘/tasks/:id’]

return the JSON representation of the task

response example:

{"task":

{"completed":false,

"created_at":"2012-02-16T17:45:16-05:00",

"description":"task description",

"due_date":"",

"grid_id":335,

"id":2691,

"importance":0,

"interval":null,

"name":"task 1",

"position":2,

"updated_at":"2012-02-16T17:45:48-05:00",

"recurrence":

{"created_at":"2012-02-16T17:46:24-05:00",

"id":88,

"rule":"completion",

"task_id":2691,

"unit":"days",

"updated_at":"2012-02-16T17:46:24-05:00",

"value":1}}}

Create [POST ‘/tasks’]

create a task

accepted data:

task[name]

max length 255 characters

mandatory field

task[importance]

0 to x-1 (where x is the num_important of the grid)

mandatory if task has a grid

task[grid_id]

the id of the grid the task belongs to

if nil then task[user] must be true.

task[interval]

date in the format of 2012-02-18 19:00:00 will be interpreted in the user’s time zone.

9999-12-01T00:00 will put the task in the LATER column

(Keep this abstracted from the end user to provide a good user experience)

task[description]

task[due_date]

task[position]

task[completed]

defaults to false.

task[user]

boolean. true if task belongs to MyTask for the current user

task[user_position]

float. sort position in MyTasks for the current user. Applicable only if task[user] is true

data example:

task[name]=blah&task[interval]=2012-02-18T19:00:00&task[importance]=0&task[grid_id]=338

response example:

200

{"task":

{"completed":true,

"created_at":"2012-02-17T13:06:44-05:00",

"description":null,

"due_date":"",

"grid_id":338,

"id":2711,

"importance":0,

"interval":"2012-02-17T13:06:44-05:00",

"name":"blah",

"position":18,

"updated_at":"2012-02-17T13:06:44-05:00",

"recurrence":""}}

error response example

400

{"name":["can't be blank"],"importance":["can't be blank","is outside of bounds of grid"]}

Update [PUT ‘/tasks/:id’]

update the task

accepted data:

Same as the data accepted for the task create request.

Notes:

1- All task attributes not provided will stay at their original values.

2- All data is optional but new data must keep the task valid.

(For example. if you move a task with importance=2 to a grid with only one level of importance, you must provide a valid importance level along with the grid_id)

example data:

task[completed]=true

response data:

json format for task and next_task

task: is the updated task

next_task: a new task created due to a recurrence. This value will be null unless the update request was completing a task that has a recurrence.

response example:

200

{"task":

{"completed":true,

"created_at":"2012-02-20T12:40:57-05:00",

"description":"",

"due_date":"2012-02-21T12:40:57-05:00",

"grid_id":71,

"id":2761,

"importance":0,

"interval":"2012-02-21T12:00:00-05:00",

"name":"buy oranges",

"position":26,

"updated_at":"2012-02-20T12:49:04-05:00",

"recurrence":""},

"next_task":

{"completed":false,

"created_at":"2012-02-20T12:49:04-05:00",

"description":"",

"due_date":"2012-02-21T12:49:04-05:00",

"grid_id":71,

"id":2762,

"importance":0,

"interval":"2012-02-20T12:00:00-05:00",

"name":"buy oranges",

"position":1,

"updated_at":"2012-02-20T12:49:04-05:00",

"recurrence":

{"created_at":"2012-02-17T14:23:15-05:00",

"id":89,

"rule":"completion",

"task_id":2762,

"unit":"days",

"updated_at":"2012-02-20T12:49:04-05:00",

"value":1}}}

error response example:

400

{"name":["can't be blank"],"urgency":["is outside of bounds of grid"]}

Destroy [DELETE ‘/tasks/:id’]

destroy the task

Note: if the task didn’t exist to begin with, this will return 200

response example:

200

{}

Tags

Actions

Index [GET ‘/grids/:grid_id/tags]

returns a JSON collection of all hash tags in a grid.

[{“hashtag”:

        {“name”:”Marketing”}},

{“hashtag”:

{“name”:”Business”}}]

Labels

Actions

Index [GET ‘/grids/:grid_id/labels]

returns a JSON collection of all labels in a grid.

The default labels will be returned if no label values were set by the user.

response example:

[{"label":

{"name":"Now",

"importance":0}},

{"label":

{"name":"Next",

"importance":0}},

{"label":

{"name":"Later",

"importance":0}}]

response example for an infinity grid:

[{"label":

{"name":"gardening",

"importance":0}},

{"label":

{"name":"Not Important",

"importance":1}}]

Update [PUT ‘/grids/:grid_id/labels

label[name]

max length 30 characters

mandatory

label[importance]

0 to x-1 (where x is the num_important of the grid)

mandatory

data example:

label[name]=gardening&label[importance]=0

response example:

200

{"label":

{"importance":0,

"name":"gardening"}}

error response example:

400

{"importance":["can't be blank","is outside of bounds of grid"]}

Destroy [DELETE ‘/grids/:grid_id/labels?label[importance]=:importance’]

destroy the label at importance :importance

Note: If the label didn’t exist to begin with, this will return 200

Important Note: Deleting a label will make it go back to it’s default value (Now, Next, Important, Not Important ...etc.) If you wish to make it empty, you can do an UPDATE request with label[name] set to spaces.

response example:

200

{}

UserNotifications

Attributes

created_at

datetime created

id

The user_notification id. This is the unique id to use in the destroy function

updated_at

datetime last updated

user_id

the current user’s id

notification

expiry

the date this notification expires

message

the message, may contain html

severity

info, alert, error, notice, success

Actions

Index [GET ‘/user_notifications]

returns a JSON collection of all notifications for the current user

example response:

200

[{"user_notification":

{"created_at":"2012-02-21T16:32:00-05:00",

"id":368,

"updated_at":"2012-02-21T16:32:00-05:00",

"user_id":2,

"notification":

{"expiry":"2013-02-21",

"message":"Grid share has been shared with you. <a href='#' id='close_and_reload'>Reload?</a>",

"severity":"info"}}},

{"user_notification":

{"created_at":"2012-02-21T16:33:58-05:00",

"id":369,

"updated_at":"2012-02-21T16:33:58-05:00",

"user_id":2,

"notification":

{"expiry":"2013-02-21",

"message":"Grid Tutorial has been shared with you. <a href='#' id='close_and_reload'>Reload?</a>",

"severity":"info"}}}]

Destroy [DELETE ‘/user_notifications/:id’]

destroy a user_notification

Note: If the user_notificationl didn’t exist to begin with, this will return 200

example response:

200

{}

Grids Sharing (GridsUsers)

Attributes

id

integer

A unique identifier for the grids_users. Use this value in the url of update and destroy requests.

grid_id

integer

the grid id

locked

boolean

whether the grid is locked (not accessible to the user because they are over their plan’s limit)

position

integer

the sort position of the grid for that user

role

string

reader, editor or owner

updated_at

datetime

time grids_users was last updated

created_at

datetime

time grids_users was created

user

email

users email

id

users id

name

name

username

the user’s @username

Actions

Index (for a grid) [GET ‘/grids/:grid_id/shares’]

returns a JSON collection of grids_users. ie. the relationship between the grid and the users it’s shared with.

response example:

[{"grids_users":

{"created_at":"2012-01-02T08:36:38-10:00",

"grid_id":1,

"id":1,

"position":1,

"role":"owner",

“locked”:false,

"updated_at":"2012-01-02T08:36:38-10:00",

"user":

{"email":"john@example.com",

"id":1,

“username”:”@john”,

"name":"John Smith"}}},

{"grids_users":

{"created_at":"2012-01-02T08:36:39-10:00",

"grid_id":1,

"id":11,

"position":2,

"role":"editor",

“locked”:”false”,

"updated_at":"2012-01-02T08:36:39-10:00",

"user":

{"email":"josh@example.com",

"id":8,

“username”:”@josh”,

"name":"Josh"}}}]

Create_all [POST ‘/grids/:grid_id/shares/create_all’]

Shares a grid with other users using their email address. This creates a new grids_users object per email address.

role

owner, editor or reader

emails

Comma separated emails

e.g. “email@example.com, firstname lastname <email@example.com>”

optional_message

An optional message to be sent to the newly added e-mails

data examples:

role=editor&emails=Zaid Zawaideh <zaid@example.com>

role=reader&emails=zaid@example.com, firstname lastname <email@example.com>

response example:

200

[{"grids_users":

{"created_at":"2012-01-02T08:36:38-10:00",

"grid_id":2,

"id":2,

"position":1,

“locked”:false,

"role":"owner",

"updated_at":"2012-01-02T08:36:38-10:00",

"user":

{"email":"john@example.com",

"id":2,

“username”:”@john”

"name":"John Smith"}}},

{"grids_users":

{"created_at":"2012-01-16T07:58:07-10:00",

"grid_id":2,

"id":24,

"position":6,

“locked”:false,

"role":"owner",

"updated_at":"2012-02-23T14:19:38-10:00",

"user":

{"email":“rob@example.com",

“username”:“@rob”,

"id":1,

"name":"Rob"}}}]

error response examples:

400

["Emails are not properly formatted"]

400

["Role is not one of the valid values.","Role is not one of the valid values."]

Update [PUT ‘/grids/:grid_id/shares/:id’]

update a grid’s user role

data example:

role=reader

response example:

{"grids_users":

{"created_at":"2012-02-23T13:02:13-10:00",

"grid_id":1,

"id":177,

"position":2,

“locked”:false,

"role":"owner",

"updated_at":"2012-02-23T13:03:23-10:00",

"user":

{"email":"john@example.com",

"id":53,

“username”:”@john”

"name":"John"}}}

error response example:

400

{"role":["for grid must include at least one owner"]}

{"role":["is not one of the valid values."]}

Update_all [PUT ‘/grids/:grid_id/shares/update_all’]

update a collection of user roles for the grid, and returns all grids_users of that grid.

grids_users[:id][role]

can be owner, editor, or reader

The :id is the grids_users id to update. Only need to send the grids_users that you want to update.

data example:

grids_users[158][role]=owner?grids_users[160][role]=editor?grids_users[2018][role]=reader

response example:

[{"grids_users":

{"created_at":"2012-01-02T08:36:38-10:00",

"grid_id":2,

"id":2,

"position":1,

"role":"owner",

“locked”:false,

"updated_at":"2012-01-02T08:36:38-10:00",

"user":

{"email":"john@example.com",

“username”:”@john”,

"id":2,

"name":"John Smith"}}},

{"grids_users":

{"created_at":"2012-01-16T07:58:07-10:00",

"grid_id":2,

"id":24,

"position":6,

"role":"owner",

“locked”:false,

"updated_at":"2012-02-23T14:19:38-10:00",

"user":

{"email":"rob@example.com",

"id":1,

“username”:”@rob”,

"name":"Rob"}}}]

error response example:

400

["","Role for grid must include at least one owner"]

Destroy [DELETE ‘/grids/:grid_id/shares/:id’]

Unshare a grid

response example:

200

error response example:

403

Cannot unshare grid. Owner permission required.

Recurrences (for tasks)

Attributes

id

A unique identifier for the recurrence. Use this value in the url of update and destroy requests.

value

together with unit indicates how often the task repeats.

e.g. every 5 days

unit

days, weeks, weekday, months, years

rule

completion, due_date, due_date_by_day_of_week

task_id

the task id the recurrence belongs to

created_at

time recurrence created

updated_at

time recurrence updated

Actions

Create [POST ‘/recurrences?task_id=:task_id’]

adds a recurrence rule to a task

accepted data:

task_id

the tasks’s id to create a recurrence for

recurrence[value]

together with recurrence[unit] it indicates how often the task repeats.

-must be 1 for a recurrence[unit]=weekday. other values will be ignored.

-must be an integer >0 and <500 for all other recurrence[unit].

recurrence[unit]

can be: days, weeks, weekday, months, years

recurrence[rule]

can be: completion, due_date or due_date_by_day_of_week

completion: this is the default value

due_date: this is only valid for a task with a due date

due_date_by_day_of_week: this is only valid for a task with a due date and a monthly recurrence

Example1:

recurrence[value] = 5

recurrence[unit] = days

recurrence[rule] = completion

Task repeats every 5 days after completing the task

Example2:

recurrence[value] = 3

recurrence[unit] = weeks

recurrence[rule] = due_date

Task repeats every 3 weeks after the task’s due date.

Example3:

recurrence[value] = 1

recurrence[unit] = months

recurrence[rule] = due_date_by_day_of_week

Task repeats every month after the task’s due date on the same weekday

e.g. Repeats every 2nd Tuesday of the month if the task’s due date is on the 2nd Tuesday.

example data:

task_id=2798&recurrence[value]=10&recurrence[unit]=days&recurrence[rule]=completion

response example:

200

{"recurrence":

{"created_at":"2012-02-22T12:17:39-05:00",

"id":136,

"rule":"completion",

"task_id":2798,

"unit":"days",

"updated_at":"2012-02-22T12:17:39-05:00",

"value":10}}

error response example:

400

{"value":["must be less than or equal to 500"],

"unit":["is not a valid time unit"],

"rule":["is not a valid recurrence rule"]}

Update [PUT ‘/recurrences/:id’]

updates a recurrence rule in a task

recurrence[value]

together with recurrence[unit] it indicates how often the task repeats.

-must be 1 for a recurrence[unit]=weekday. other values will be ignored.

-must be an integer >0 and <500 for all other recurrence[unit].

recurrence[unit]

can be: days, weeks, weekday, months, years

recurrence[rule]

can be: completion, due_date or due_date_by_day_of_week

completion: this is the default value

due_date: this is only valid for a task with a due date

due_date_by_day_of_week: this is only valid for a task with a due date and a monthly recurrence

All attributes are optional, attributes not sent will stay what they were.

example data:

recurrence[value]=6&recurrence[rule]=completion

example response:

200

{"recurrence":

{"created_at":"2012-02-22T13:34:52-05:00",

"id":141,

"rule":"completion",

"task_id":2798,

"unit":"months",

"updated_at":"2012-02-22T13:36:34-05:00",

"value":6}}

error example response:

400

{"unit":["is not a valid time unit"]}

Destroy [DELETE ‘/recurrences/:id’]

removes a recurrence rule from a task

Note: If the recurrence didn’t exist to begin, this will return 403 Forbidden

example response:

200

{}

Milestones (intervals)

only applicable to Infinity Grids

Actions

Index [GET ‘/grids/:grid_id/intervals/]

returns the first interval of each frequency. For example if a grid has a weekly interval for 5 weeks starting 20th february and then daily intervals after that, this request will return two intervals indicating the start_time and interval_length of each. All other intervals can be deduced from those two values.

interval[start_time]

the start time of the interval

interval[interval_length]

time in seconds of interval

example response:

[{"interval":

{"start_time":"2012-02-20T01:00:00-05:00",

"interval_length":604800}},

{"interval":

{"start_time":"2012-03-26T02:00:00-04:00",

"interval_length":86400}}]

Goto [GET ‘/grids/:grid_id/intervals/goto?time=:time’]

interval[start_time]

the start time of this interval

interval[interval_length]

time in seconds of current interval

is_current

true for today’s interval, false otherwise

tasks

an array of all tasks in given interval in json format

You can calculate the end_time of the interval using start_time + interval_length

data example:

time=2012-03-12

The time must be a date only. Do not add hours, minutes seconds to it.

To get all tasks that are in the LATER column. ie. not assigned to a specific interval (milestone)

use time=9999-12-01. This will return a fake interval (ignore the interval values) and you can use the tasks array to obtain all tasks in the later column.

(To provide a good user experience 9999-12-01 should be abstracted from end user)

example response:

{"interval":

{"start_time":"2012-03-12T02:00:00-04:00",

"interval_length":604800},

"is_current":false,

"tasks":

[{"task":

{"completed":false,

"created_at":"2011-12-10T09:24:59-05:00",

"description":"from 1234 avenue st",

"due_date":"",

"grid_id":338,

"id":2738,

"importance":0,

"interval":"2012-03-16T12:00:00-04:00",

"name":"buy oranges",

"position":1,

"updated_at":"2012-02-23T13:34:51-05:00",

"recurrence":""}}]}

example error response:

400

{"error":"Sorry, can't go back this far in time!"}

Update [PUT ‘/grids/:grid_id/intervals/:id]

The interval is identified by it’s start_time.

E.g. If the start_time of an interval you want to update is “2012-03-12T02:00:00-04:00”, use this as the id in the url above.

Any time greater or equal than the start_time and less than the end_time will also work.

There are three available update actions

1- splitting an interval

2- changing the length of an interval and all future intervals after

3- changing the start date and end date of an interval

You can only do one of these per request. Each request requires it’s own data parameters.

accepted data:

1.Splitting an interval data

split

true

Any value will be evaluated as true. If you don’t want to split don’t send this parameter

2.Changing the interval length for current and all future intervals

length

in seconds

Must be an increment of days between 1 and 365 days

3. Changing the start date and end date of an interval

starts_on

date

The new start date of the interval e.g. 2012-03-26

ends_on

date

The new end date of the interval e.g. 2012-03-27

*Both data parameters are optional, but must provide atleast one.

Important Note: Should not provide data parameters for more than one of the above methods.

url example:

/grids/338/intervals/2012-03-12T02:00:00-04:00

data examples:

split=true

length=864000

ends_on=2012-03-26

starts_on=2012-03-12

starts_on=2012-03-12&ends_on=2012-03-26

response examples:

On success split returns an array of the two resulting intervals e.g:

[{"interval":

{"start_time":"2012-03-01T01:00:00-05:00",

"interval_length":432000}}

,{"interval":

{"start_time":"2012-03-06T01:00:00-05:00",

"interval_length":432000}}]

Length and starts_on/ends_on requests return the resulting interval e.g:

{"interval":

{"start_time":"2012-02-26T01:00:00-05:00",

"interval_length":259200}}

error response examples:

400

{"error":"Can not split columns that are less than 2 days long"}

{"error":"no time information in \"invalid\""}

{"error":"Validation failed: Interval length must be in increment of days"}

{"error":"grid must be of type Infinity"}

Abilities

Use the abilities request to find out the actions a user is authorized to perform on a grid.

This is necessary to offer the user a good experience. For example if the user has only read permissions to tasks in a grid, make sure to display the tasks as read-only, rather than have them attempt to update it and get error messages.

You can save the result of this request and use it when displaying your UI.

The are several factors that affect the user’s abilities, it includes which plan the user is on, and whether the user has owner, editor, or reader permissions to the grid. This API is meant to abstract all this complexity for you.

Actions

Grid abilities [GET ‘/grids/:id/abilities’]

:id is the grids id.

response example:

{"abilities":{"updatable":true,"destroyable":true,“locked”:false}}

Explanation:

if abilities[updatable]=true then the user has the ability to:

- update the grid (ie. grid name, size)

- update/destroy grid intervals

- share/unshare the grid with others and update their roles (owner, editor, reader)

if abilities[destroyable]=true then the user has the ability to delete the grid.

Task abilities [GET ‘/grids/:id/tasks/abilities’]

:id is the grids id.

response example:

{"abilities":{"updatable":true}}

Explanation:

if abilities[updatable]=true, then the user has the ability to create/update and delete tasks (and labels) in that grid