If you are new to AeroFS API, take a look at the overview and tutorials before diving into this reference manual.

API Reference

The AeroFS API supports reading and writing data to AeroFS Private Cloud instances. It is based on REST and uses JSON for both request and response bodies.

Encoding
All strings are encoded with UTF-8.
Encryption
All API access is done through HTTPS. Unencrypted connections will be rejected.
Read/Write
An attribute of a REST object that is readable will be included in a response containing that object. An attribute that is writable can be modified using PUT and POST methods. If an object attribute is not denoted as read-only or write-only, the attribute is both readable and writable.
On-Demand Fields
An attribute of a REST object that is on-demand will not be included by default in a response containing that object. Some requests accept a fields query parameter to specify which of these fields to include in the response.
Time format
All timestamps follow the ISO 8601 format with UTC time zone. For example, “2013-03-17T01:23Z” stands for March 17th, 2013 at 01:23 AM UTC.
Cross-origin
The API allows Cross-Origin Resource Sharing and is accessible from anywhere. That is, all responses include the header Access-Control-Allow-Origin: *.
Persistent connection
It is highly recommended to use persistent HTTP connections, which leverage HTTP pipelining and amortize the cost of SSL handshaking.
HTTP 1.0
To allow streaming responses of unknown size over a persistent connection, the API uses chunked transfer coding and therefore omits the Content-Length header. As a consequence, HTTP 1.0 clients are not supported.

Versioning

The API version follows semantic versioning in the form MAJOR.MINOR:

  • MAJOR is updated when we make incompatible API changes.

  • MINOR is updated when we add functionality in a backwards-compatible manner.

All API URLs include a version prefix in the form “vMAJOR.MINOR”. The API server will accept a request URL only if it supports the version specified in the URL.

This document describes API version 1.2.

$ curl https://host.name/api/v1.2/children

Authentication

API calls must be made using an OAuth token in the “Authorization” request header field.

The syntax for the header field is Authorization: Bearer <token> where <token> is a valid OAuth token. The Getting Started guide explains how to obtain an OAuth token.

$ curl -H "Authorization: Bearer dec6f0b1db98496fbd3f74d042a9a19d" ...

Errors

AeroFS uses standard HTTP status codes to indicate success or failure in serving API requests:

  • 2xx: The request was successful.

  • 4xx: The request could not be processed; presumably the request is invalid.

  • 5xx: A server-side failure occurred.


In addition, 4xx and 5xx responses have a JSON body with two fields:

  • type An AeroFS specific, machine readable error code.

  • message Human-readable description of the error.


Possible values of the type field include:

  • BAD_ARGS: Invalid request arguments

  • UNAUTHORIZED: Missing or invalid OAuth token

  • FORBIDDEN: Insufficient permissions to perform request

  • NOT_FOUND: Requested resource not found

  • CONFLICT: A conflict caused a write operation to fail

  • INTERNAL_ERROR: Unexpected server error

  • TOO_MANY_REQUESTS: The server is under load and declined to service the request

{
    "type": "BAD_ARGS",
    "message": "The required parameter file_id is missing"
}

Entity tags (ETags)

When appropriate, the server populates entity tags in API response headers and honor conditional requests for subsequent API requests.

Entity tags are omitted from responses to requests that include on-demand fields.

Entity tags are opaque strings. No assumptions should be made about their structure or length.

Entity tags are only valid for the resource for which they where obtained. Using an entity tag obtained from one resource in a conditional request for another resource is incorrect and the result is undefined.

For instance an entity tag obtained from the /files/0a1b2c... resource MUST NOT be used for a conditional request to the /files/0a1b2c.../content resource.

Weak ETags are used when resources are not guaranteed to be byte-for-byte equivalent between two requests but may still be semantically equivalent. This is typically the case for JSON objects.

Consistency policies

To make appropriate trade-offs between service availability and data consistency, AeroFS API clients can control the policy the load balancer applies when selecting computers to serve API requests. The table below summarizes how the client selects consistency policies. Read this article for more information on this topic.

 

Client behavior Resulting policy
Enable cookies Default: fall back to other computers if the original computer becomes unavailable.
Enable cookies and set Endpoint-Consistency: strict in request headers Consistency over availability: do not switch computer in a session.
Disable cookies Availability over consistency: select arbitrary computers across requests.

OAuth

OAuth 2.0 is a widely used protocol for users to authorize third-party applications to perform secure actions on their behalf. Every API call made to the AeroFS API Server requires an OAuth Access Token. This is preferable to basic authentication for three main reasons:

  1. A user can authorize an application to use their AeroFS data without giving their password to the application.

  2. A user can grant some permissions (e.g. read-only access) and withhold others (e.g. write access).

  3. A user can easily revoke authorization at any time.

This tutorial will guide you through the process of registering an app with an AeroFS Private Cloud Appliance, getting an OAuth Access Token by requesting authorization from a user, and using that token to make API calls.

This section of the API reference describes each call made to the AeroFS OAuth server.

OAuth Scopes

Each OAuth access token is issued for a particuler set of privileges, or “scopes”. The scopes are returned to the caller along with the access token.

The API supports the following scopes:

Scope name Permissions granted
files.read Can list and download files.
files.write Can create, rename, upload, and delete files.
files.appdata Read and write access to appdata folder
user.read Can view email and name.
user.write Can modify name.
user.password Can reset and modify password.
acl.read Can list shared folders and their members.
acl.write Can create shared folders and manage members.
acl.invitations Can list, accept, and ignore invitations.
organization.admin Can manage users, shared folders, and content.

Requested Scopes vs Granted Scopes

When an application requests an access token, the user may decide to only grant a subset of the requested scopes. Applications should handle such a case gracefully and clearly communicate to the users how partial scope grants will affect functionality.

If the granted scopes are insufficient for your application to work, you may show an error to the user and re-submit your authorization request.

Administrative Scope

The organization.admin scope will only be granted if the authenticated caller is an AeroFS organization admin.

Application data

The files.appdata scope gives an application the equivalent of files.read and files.write but restricted to a per-application sandbox folder.

Applications can use the special appdata Folder identifier to access this special location.

Two applications using this special folder will not be able to read or write each other’s data. However, applications that are given unrestricted read and/or write access through the files.read and files.write scopes can access the appdata folder of any other application.

user.read,acl.read

Grants read-only access to basic user information and shared folders.


files.read,files.write

Grants full read and write access to the entire AeroFS folder.

Getting authorization from the user

To get an authorization code, direct the user to the authorization page and wait for the code to be sent to the redirect_uri.

Definition
GET /authorize
Note: this is https://host.name/authorize, not https://host.name/api/v1.2/authorize.
Parameters
response_type Must be “code”.
client_id UUID of the app (generated upon app registration).
redirect_uri A URI to which authorization responses, including errors, will be redirected. This must match the Redirect URI entered during client registration.
scope Comma-separated list of requested scopes
state (optional) A string that will be roundtripped and passed back to the Redirect URI. Recommended: an unguessable string. This should be used to prevent cross-site request forgery attacks.
Returns
The user will be redirected to the redirect_uri with the following query parameters:
code Authorization code.
state (optional) If state was provided in the request, the same string will be included here. If it does not match the expected value, the app should abort the authorization process.
Errors
If client_id or redirect_uri is invalid, a 400 status code will be returned to the user.
If any other error occurs, the user will be directed to the redirect_uri with the following query parameters:
error One of the error codes in section 4.1.2.1 of the OAuth 2.0 spec.
error_description (optional) A human-readable description of the error.
state (optional) If state was provided in the request, the same string will be included here. If it is different, the callback is from a third-party and the app should abort the authorization process.

Send the user to the following URL:

https://host.name/authorize?response_type=code&client_id=afd42bcd-7604-4ce3-ae4b-1873bdbcd83c&redirect_uri=https://app.acme.com/auth_cb&state=9kcht6uiha9f23u7sskonb7cvy70ga3p6bnzrm18&scope=user.read

If the user authorizes the app, they will be redirected to:

https://app.acme.com/auth_cb?code=7ac4fe73a2df4095bd72580a4dcb8501&state=9kcht6uiha9f23u7sskonb7cvy70ga3p6bnzrm18

Getting an access token

Exchange a valid authorization code for an access token.

Definition
POST /auth/token
grant_type Must be “authorization_code”.
code Authorization code.
client_id UUID of the app (generated upon app registration).
client_secret Secret key of the app (generated upon app registration).
redirect_uri Redirect URI provided during client registration and when obtaining authorization code.
Returns
200 OK with the following JSON fields in the body:
access_token An OAuth access token. Keep it secret; keep it safe.
token_type The type of the granted token; today this is always “bearer”.
expires_in Number of seconds until expiry (or 0 for tokens that don’t expire).
scope Comma-separated list of scopes granted by the user.
curl -X POST https://host.name/auth/token --data "grant_type=authorization_code&code=7ac4fe73a2df4095bd72580a4dcb8501&client_id=afd42bcd-7604-4ce3-ae4b-1873bdbcd83c&client_secret=a543b4f7-6f54-427d-aba6-86d036f2883e&redirect_uri=https://app.acme.com/auth_cb"
{
  "access_token": "dec6f0b1db98496fbd3f74d042a9a19d",
  "token_type": "bearer",
  "expires_in": 0,
  "scope": "files.read,files.write"
}

Revoking an access token

Revoke an access token so that the app can no longer use it for API requests.

Definition
DELETE /auth/token/{token}
Returns
200 OK if the token is deleted successfully.
404 Not Found if the token was not found.
curl -X DELETE https://host.name/auth/token/dec6f0b1db98496fbd3f74d042a9a19d

The User object

The User object represents an AeroFS user.

Attributes
email (read-only) Email address of the member.
first_name First name of the member.
last_name Last name of the member.
{
  "email": "alice@foo.corp",
  "first_name": "Alice",
  "last_name": "Foo"
}

Retrieve user information

Return information for a given user.

Definition
GET /users/{email}
Requirements
A caller with the user.read scope can view their own details. To view other users, the caller must also have the organization.admin scope.
Parameters
email Email of the user to lookup.
Returns
200 OK with a User object in the body.
403 Forbidden the given OAuth token does not have the required privilege.
$ curl https://host.name/api/v1.2/users/bob%40foo.corp
{
  "email": "bob@foo.corp",
  "first_name": "Bob",
  "last_name": "Loblaw"
}

Create a user

Create a new AeroFS user.

The behavior of this action depends on the authentication type used by the AeroFS appliance.

If the appliance is configured to delegate signin to an external identity service (ActiveDirectory or LDAP), the first name and last name for the user will come from the external service. The user can sign in immediately using their email address and external credential.

When the appliance is configured for local credential management, or the given email address is not found in the external account store, the first_name and last_name parameters are used to create the user. The user will not be able to log in until their account is associated with a password - either by administrative action or using the password self-reset flow.

Definition
POST /users
Requirements
Caller’s access token must have the user.write and organization.admin scopes.
Request body
A User object.
Returns
201 Created with a User object in the body.
403 Forbidden the given OAuth token does not have the required privilege.
409 Conflict if the provided email is already assigned to a user in the system.
Response header
Location URL of the new resource (the newly-created user). See RFC2616.
$ curl -X POST https://host.name/api/v1.2/users \
    -d '{"email": "alice@foo.corp", "first_name": "Alice", "last_name": "Adams"}'
{
  "email": "alice@foo.corp",
  "first_name": "Alice",
  "last_name": "Adams"
}

Update a user

Update existing user information. See creating a user to create a new AeroFS user.

Definition
PUT /users/{email}
Requirements
A caller with the user.write scope can update their own details. To manage other users, the caller must also have the organization.admin scope.
Request body
A User object.
Parameters
email Email of the user account.
Returns
200 OK with a User object in the body.
403 Forbidden the given OAuth token does not have the required privilege.
$ curl -X PUT https://host.name/api/v1.2/users/alice%40foo.corp \
    -d '{"first_name": "Alice", "last_name": "Updated"}'
{
  "email": "alice@foo.corp",
  "first_name": "Alice",
  "last_name": "Updated"
}

Delete a user

Removes the user account from AeroFS. All outstanding certificates for this user will be revoked, and their devices will be unable to sync.

Definition
DELETE /users/{email}
Requirements
Caller’s access token must have the user.write and organization.admin scopes.
Parameters
email Email of the user account to delete.
Returns
200 OK with an empty body.
403 Forbidden the given OAuth token does not have the required privilege.
$ curl -X DELETE https://host.name/api/v1.2/users/alice%40foo.corp

Change user password

Change the AeroFS password for the given user. For this command to succeed, the targeted user must have a locally-managed credential - i.e., not authenticated by ActiveDirectory or LDAP.

This action may only be taken by administrators and users targeting their own credentials.

Definition
PUT /users/{email}/password
Request Body
A JSON string containing the new password value.
Requirements
Caller’s access token must have the user.password scope. If the caller is targeting a different user’s account, the caller must also have the organization.admin scope.
Parameters
email Email of the user account to modify.
Returns
200 OK with an empty body.
403 Forbidden the given OAuth token does not have the required privilege.
404 Not Found the given account does not exist, or does not have a locally-managed password.
$ curl -X PUT https://host.name/api/v1.2/users/bob%40foo.corp/password \
    -H 'Content-Type: application/json' \
    -d '"newsecret"'

Disable user password

Disables the AeroFS password for the given user. The user will be unable to sign in new devices, or to use the AeroFS web admin console, until they have successfully reset their password.

For this command to succeed, the targeted user must have a locally-managed credential - i.e., not authenticated by ActiveDirectory or LDAP.

This action may only be taken by administrators and users targeting their own credentials.

Definition
DELETE /users/{email}/password
Requirements
Caller’s access token must have the user.password scope. If the caller is targeting a different user’s account, the caller must also have the organization.admin scope.
Parameters
email Email of the user account to modify.
Returns
200 OK with an empty body.
403 Forbidden the given OAuth token does not have the required privilege.
404 Not Found the given account does not exist, or does not have a locally-managed password.
$ curl -X DELETE https://host.name/api/v1.2/users/bob%40foo.corp/password

The Folder object

A Folder object represents a folder in AeroFS. You may use methods on this object to manipulate folders and retrieve their information.

For convenience, the following special values can be used as folder identifiers:

  • root: root AeroFS folder of the authenticated user
  • appdata: special per-application data folder (see files.appdata scope)
  • any SharedFolder identifier
Attributes
id (read-only) Identifier of this folder.
name Human-readable base name of this folder.
parent Identifier of this folder’s parent folder.
is_shared (read-only) Boolean. Whether this folder is a shared folder.
sid (read-only) SharedFolder identifier, if is_shared is true.
path (read-only, on-demand) ParentPath object.
children (read-only, on-demand) Children object.
{
  "id": "0a1b2c...",
  "name": "Example Folder",
  "parent": "9f8e7d...",
  "is_shared": false
}

Retrieve folder metadata

Given a folder’s identifier, return its metadata such as the name and parent folder ID.

Definition
GET /folders/{id}
Parameters
id Identifier of the folder.
Query Parameters
fields Comma-separated list of on-demand field names to include in response.
Returns
A Folder object.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl https://host.name/api/v1.2/folders/0a1b2c...
{
  "id": "0a1b2c...",
  "name": "Example Folder",
  "is_shared": false
}

Retrieve folder path

Given a folder’s identifier, return the list of parent folders that form its path. The returned object does NOT include the folder for which the request is made.

Definition
GET /folders/{id}/path
Parameters
id Identifier of the folder.
Returns
200 OK with ParentPath object in the body.
$ curl https://host.name/api/v1.2/folders/f00/path
{
  "folders": [{
    "id": "9f8e7d...",
    "name": "AeroFS",
    "is_shared": false
  }, {
    "id": "0a1b2c...",
    "name": "My shared folder",
    "is_shared": true
  }]
}

Listing children of a folder

List files and folders under a given folder.

Definition
GET /folders/{id}/children
Parameters
id (optional) Identifier of the parent folder.
Returns
A Children object.
$ curl https://host.name/api/v1.2/folders/9f8e7d.../children
{
  "folders": [{
    "id": "0a1b2c...",
    "name": "Folder 1",
    "is_shared": false
  }]
}

Create a folder

Create a new, empty folder.

Definition
POST /folders
Request body
A Folder object.
Returns
201 Created with a Folder object in the response body.
409 Conflict if a folder or file with the same name already exists.
Response header
Location URL of the new resource. See RFC2616.
ETag Entity tag for subsequent conditional requests.
$ curl -X POST https://host.name/api/v1.2/folders \
    -H 'Content-Type: application/json' \
    -d '{"parent": "9f8e7d...", "name": "Foo"}'
{
  "id": "0a1b2c...",
  "name": "Foo",
  "is_shared": false
}

Move a folder

Rename a folder and/or move it to a new parent.

The result is undefined if two clients try to move or rename the same folder at the same time without passing an If-Match header.

Movement across shared folder boundary

When a folder is moved across a shared folder boundary, e.g. from the AeroFS root folder into a shared folder or from a shared folder to another shared folder, its identifier will change.

To safely handle such moves the caller should retrieve the new identifier from the response body and update any cached occurence of the old identifier.

Definition
PUT /folders/{id}
Parameters
id Identifier of the folder to be renamed and/or moved.
Request body
A Folder object that specifies the new location.
Request header (optional)
If-Match One or more ETags. The request will fail if the current ETag of the resource does not match any of the given values. See RFC2616.
Returns
200 OK with a Folder object in the response body.
409 Conflict if a folder or file with the same name already exists.
412 Precondition Failed if the conditions specified via the If-Match header could not be satisfied.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl -X PUT https://host.name/api/v1.2/folders/0a1b2c... \
    -H 'Content-Type: application/json' \
    -d '{"parent": "9f8e7d...", "name": "Foo"}'
{
  "id": "0a1b2c...",
  "name": "Foo",
  "is_shared": false
}

Delete a folder

Delete a folder and all its children.

Definition
DELETE /folders/{id}
Parameters
id Identifier of the folder to be deleted.
Request header (optional)
If-Match One or more ETags. The request will fail if the current ETag of the resource does not match any of the given values. See RFC2616.
Returns
204 No Content if the folder was successfully deleted.
412 Precondition Failed if the conditions specified via the If-Match header could not be satisfied.
$ curl -X DELETE https://host.name/api/v1.2/folders/0a1b2c...

The Children object

A Children object contains a list of files and folders under a parent folder.

Attributes
folders (read-only) List of Folder objects under the parent folder.
files (read-only) List of File objects under the parent folder.
{
  "folders": [{
    "id": "0a1b2c...",
    "name": "Folder 1",
    "is_shared": false
  }, {
    "id": "0a1b2c...",
    "name": "Folder 2",
    "is_shared": true
  }],
  "files": [{
    "id": "0a1b2c...",
    "name": "File.doc",
    "last_modified": 123456,
    "size": 123,
    "mime_type": "application/octet-stream"
  }]
}

The ParentPath object

A ParentPath object contains a list folders that form a path, starting from the root down to the innermost folder.

Attributes
folders (read-only) List of Folder objects, starting from the root
{
  "folders": [{
    "id": "9f8e7d...",
    "name": "AeroFS",
    "is_shared": false
  }, {
    "id": "0a1b2c...",
    "name": "My shared folder",
    "is_shared": true
  }]
}

The File object

A File object represents a file in AeroFS. You may use methods on this object to manipulate files and retrieve their information.

Files without content

It is possible for a file to exist without having any content. Usually, this is a placeholder for a file upload that is in progress. It can also happen if an upload fails partway through and is not resumed or if Selective Sync is used on a desktop client.

In such cases, the size and timestamp fields will be missing.

Trying to download content for such a file will result in a 404 response, however in most cases uploading new content will be possible.

It is always possible to rename, move, or delete a file with no content.

Attributes
id (read-only) Identifier of this file.
name Human-readable base name of this file.
parent Identifier of this folder’s parent folder.
last_modified (read-only) Last modified time of this file, in ISO 8601 format.
size (read-only) Long type. Size of this file, in bytes.
mime_type (read-only) MIME type of the file, default to application/octet-stream.
etag (read-only) ETag of the content.
path (read-only, on-demand) ParentPath object.
{
  "id": "0a1b2c...",
  "name": "Hello.doc",
  "parent": "9f8e7d...",
  "last_modified": "2013-03-17T01:23Z",
  "size": 1234,
  "mime_type": "application/octet-stream",
  "etag": "707a1e..."
}

Retrieve file metadata

Given a file’s identifier, return its metadata such as the name and parent folder ID.

Definition
GET /files/{id}
Parameters
id Identifier of the file.
Query Parameters
fields Comma-separated list of on-demand field names to include in response.
Returns
A File object.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl https://host.name/api/v1.2/files/0a1b2c...
{
  "id": "0a1b2c...",
  "name": "Hello.doc",
  "last_modified": "2013-03-17T01:23Z",
  "size": 1234,
  "mime_type": "application/octet-stream"
}

Retrieve file path

Given a file’s identifier, return the list of parent folders that form its path. The returned object does NOT include the file for which the request is made.

Definition
GET /files/{id}/path
Parameters
id Identifier of the file.
Returns
200 OK with ParentPath object in the body.
$ curl https://host.name/api/v1.2/files/deadbe.../path
{
  "folders": [{
    "id": "9f8e7d...",
    "name": "AeroFS",
    "is_shared": false
  }, {
    "id": "0a1b2c...",
    "name": "My shared folder",
    "is_shared": true
  }]
}

Retrieve file content

Given a file’s identifier, return its content.

If the file is modified on the source computer during the transfer, the connection will be closed before transfer completes.

Definition
GET /files/{id}/content
Parameters
id Identifier of the file.
Request header (optional)
Range One or more ranges of bytes for partial requests. See RFC2616.
If-Range An ETag. The Range header will be ignored if the current ETag of the resource does not match the given value. See RFC2616.
If-None-Match One or more ETags. A 304 Not Modified response with an empty body will be returned if the current ETag of the resource matches any of the given values. See RFC2616.
Returns
200 OK with file content in the body.
206 Partial Content with file content in the body. Return a multipart response if multiple subranges are included.
304 Not Modified with an empty body. See the If-None-Match request header above.
416 Requested Range Not Satisfiable with empty body if the request included a Range header and none of the ranges overlap the current extent of the file content.
Response header
Content-Type MIME type of the file or, for subrange response, either application/octet-stream or multipart/byteranges. See RFC2616.
Content-Disposition Suggested filename when save the file to disk. Not available for partial responses. See RFC2616.
Content-Range Location of the returned content in the full file cotent. Available only for partial responses. See RFC2616.
ETag Entity tag for subsequent conditional requests.
$ curl https://host.name/api/v1.2/files/0a1b2c.../content

downloads the entire content of a file.

$ curl https://host.name/api/v1.2/files/0a1b2c.../content \
    -H 'If-None-Match: "50c17c41b533033c73f1eb99d80bbb32"'

downloads the content of the given file only if the entity tag has changed.

$ curl https://host.name/api/v1.2/files/0a1b2c.../content \
    -H 'If-Range: "50c17c41b533033c73f1eb99d80bbb32"' \
    -H 'Range: bytes=0-99'

downloads the first 100 bytes of the given file if the entity tag has not changed, otherwise downloads the whole file.

Create a file

Create a new file, with no content.

Definition
POST /files
Request body
A File object.
Returns
201 Created with a File object in the response body.
409 Conflict if a folder or file with the same name already exists.
Response header
Location URL of the new resource. See RFC2616.
ETag Entity tag for subsequent conditional requests.
$ curl -X POST https://host.name/api/v1.2/files \
    -H 'Content-Type: application/json' \
    -d '{"parent": "9f8e7d...", "name": "Hello.doc"}'
{
  "id": "0a1b2c...",
  "name": "Hello.doc",
  "last_modified": "2013-03-17T01:23Z",
  "size": 0,
  "mime_type": "application/octet-stream"
}

Upload file content

Overwrite an existing file’s content with new content.

Conditional upload

In cases where unconditional overwrite is not acceptable, the client should first make a GET request on the content. (Alternatively, use a HEAD request with the same parameters as GET to fetch response headers only.) The entity tag in the response header can then be passed to this method in the If-Match header.

If two clients try to upload new content to the same file at the same time without passing an If-Match header, the result is undefined.

Chunked upload

To start a chunked upload, make a PUT request including a Content-Range request header, whose value should be consistent with the size of the chunk being uploaded. See RFC2616 for the precise syntax of that header.

The server response will include an Upload-ID header which can be used to upload the next chunk(s) of the file. To obtain the upload identifier without actually starting the upload, make that first request with an empty body and Content-Range: bytes */*.

Multiple chunks may not be uploaded concurrently or out-of-order for the same upload identifier:

  • a PUT request for a range starting before the last received byte will result in the already received data being truncated
  • a PUT request for a range starting after the last received byte will result in a 416 Requested Range Not Satisfiable response

The upload is considered complete when the amount of bytes uploaded at the end of a request matches the value specified in the instance-length of the Content-Range request header.

If the total size of the file being uploaded is not known ahead of time, instance-length can remain unspecified, i.e. *, until the very last chunk. The last chunk may also have an empty body, in which case the byte-range-resp-spec of the Content-Range header should be *.

Resumable upload

When the connection is broken before a chunk is fully uploaded, it is possible to determine the amount of bytes successfully uploaded by sending PUT request with an empty body, a valid Upload-ID and Content-Range: bytes */*. If the upload is still ongoing, the server will respond with 200 OK and a Range header indicating the amount of bytes successfully uploaded so far.

Note that the API currently does not distinguish between invalid, unused and completed upload identifiers: in all of these cases the above request will result in a 400 Bad Request response.

Upload identifiers are not valid forever. The expiration policy can differ among AeroFS desktop clients but in general an upload identifier is expected to remain valid for roughly 24 hours. When the upload identifier expires, any uploaded data will be discarded and subsequent requests including it will be rejected.

Consistency considerations

Each upload identifier is only valid for a single AeroFS desktop client. As outlined in the section on consistency policies, cookies MUST be enabled and the Endpoint-Consistency: strict request header SHOULD be used to ensure that subsequent requests will be routed to the same computer.

Definition
PUT /files/{id}/content
Parameters
id Identifier of the file to upload new content to.
Request body
File content.
Request header (optional)
If-Match One or more ETags. The request will fail if the current ETag of the resource does not match any of the given values. See RFC2616.
Upload-ID Opaque identifier used for incremental upload. Will be ignored unless a Content-Range header is also present.
Content-Range Location of the chunk being uploaded within the file.
Returns
200 OK if successful.
400 Bad Request if given invalid Upload-ID, if the Content-Range header is not consistent with the length of the body or if the request is otherwise inavlid.
412 Precondition Failed if the conditions specified via the If-Match header could not be satisfied.
416 Requested Range Not Satisfiable with empty body if the request included Content-Range and Upload-ID headers and the upload cannot be resumed from the requested position.
429 Too Many Requests if the desktop client servicing the request reached the maximum allowed number of concurrent uploads.
Response header
ETag Entity tag for subsequent conditional requests.
Upload-ID Opaque identifier of ongoing chunked download, if appropriate.
Range Range of bytes upload so far if using chunked upload
$ curl -X PUT https://host.name/api/v1.2/files/0a1b2c.../content \
    -T Hello.doc

Uploads the content of local file Hello.doc to a file with ID 0a1b2c….

$ curl -X PUT https://host.name/api/v1.2/files/0a1b2c.../content \
    -H "Content-Range: bytes */*" \
    -H "Content-Length: 0"

Obtains a fresh upload identifier to be used for a chunked upload to a file with ID 0a1b2c….

$ curl -X PUT https://host.name/api/v1.2/files/0a1b2c.../content \
    -H "Upload-ID: 123456" \
    -H "Content-Range: bytes */*" \
    -H "Content-Length: 0"

Retrieve amount of bytes successfully uploaded to file with ID 0a1b2c… as part of upload 123456.

$ curl -X PUT https://host.name/api/v1.2/files/0a1b2c.../content \
    -H "Upload-ID: 123456" \
    -H "Content-Range: bytes 1000-1199/*" \
    -T chunk.bin

Upload the contents of local file chunk.bin as a chunk of 200 bytes starting at position 1000 to file with ID 0a1b2c… as part of upload 123456.

$ curl -X PUT https://host.name/api/v1.2/files/0a1b2c.../content \
    -H "Upload-ID: 123456" \
    -H "Content-Range: bytes */1200" \
    -H "Content-Length: 0"

Completes chunked upload 123456 to file with ID 0a1b2c….

Move a file

Rename a file and/or move it to a new parent.

The result is undefined if two clients try to move or rename the same file at the same time without passing an If-Match header.

Movement across shared folder boundary

When a file is moved across a shared folder boundary, e.g. from the AeroFS root folder into a shared folder or from a shared folder to another shared folder, its identifier will change.

To safely handle such moves the caller should retrieve the new identifier from the response body and update any cached occurence of the old identifier.

Definition
PUT /files/{id}
Parameters
id Identifier of the folder to be renamed and/or moved.
Request body
A File object that specifies the new location.
Request header (optional)
If-Match One or more ETags. The request will fail if the current ETag of the resource does not match any of the given values. See RFC2616.
Returns
200 OK with a File object in the response body.
409 Conflict if a folder of file with the same name already exists.
412 Precondition Failed if the conditions specified via the If-Match header could not be satisfied.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl -X PUT https://host.name/api/v1.2/files/0a1b2c... \
    -H 'Content-Type: application/json' \
    -d '{"parent": "9f8e7d...", "name": "Hello.doc"}'
{
  "id": "0a1b2c...",
  "name": "Hello.doc",
  "last_modified": "2013-03-17T01:23Z",
  "size": 1234,
  "mime_type": "application/octet-stream"
}

Delete a file

Definition
DELETE /files/{id}
Parameters
id Identifier of the file to be deleted.
Request header (optional)
If-Match One or more ETags. The request will fail if the current ETag of the resource does not match any of the given values. See RFC2616.
Returns
204 No Content if the file was successfully deleted.
412 Precondition Failed if the conditions specified via the If-Match header could not be satisfied.
$ curl -X DELETE https://host.name/api/v1.2/files/0a1b2c...

The SharedFolder object

A SharedFolder object represents a folder shared by several AeroFS users. You may use methods on this object to retrieve and update the list of members as well as the permissions of each member.

External shared folders

A shared folder in AeroFS can be either “internal” or “external”. An internal shared folder resides under the AeroFS folder, whereas an external one can reside at an arbitrary location on the user’s device.

This property has per-user granularity, i.e. a shared folder can be internal for Alice and external for Bob.

Users that create an external shared folder (or accept an invitation and specify they want to join the folder as external) need to specify the location of this folder on each of their devices.

It is not currently possible to convert an internal folder into an external one or vice-versa.

Attributes
id (read-only) Identifier of this folder.
name Human-readable base name of this folder.
members (read-only) List of Member objects.
pending (read-only) List of PendingMember objects.
is_external (read-only) Whether the shared folder is external for the user making the request.
caller_effective_permissions (read-only) List of the caller’s permissions in this folder.

Note: The name attribute is the name of the folder for the user making the request. It may not reflect the name of the actual folder for other members: each member can independently rename the shared folder on their own devices and this change will NOT propagate to other members.

Note: The effective permissions returned in caller_effective_permissions may not match the permissions that the user was originally invited to the folder with. For example, if a user is invited as a Viewer but is also in a group that is an Editor they would have effective permissions of ["WRITE"].

{
  "id": "0a1b2c...",
  "name": "Alice and Bob",
  "external": false,
  "members": [{
      "email": "alice@foo.corp",
      "first_name": "Alice",
      "last_name": "Foo",
      "permissions": ["WRITE", "MANAGE"]
    }, {
      "email": "bob@foo.corp",
      "first_name": "Bob",
      "last_name": "Foo",
      "permissions": ["WRITE"]
    }, {
      "email": "eve@foo.corp",
      "first_name": "Eve",
      "last_name": "Foo",
      "permissions": []
    }
  ],
  "pending": [{
      "email": "viewer@external.corp",
      "invited_by": "alice@foo.corp",
      "permissions": []
    }
  ],
  "caller_effective_permissions": ["WRITE", "MANAGE"]
}

List shared folders

List all shared folders for a given user.

Definition
GET /users/{email}/shares
Parameters
email Email of the user.
Request header (optional)
If-None-Match One or more ETags. A 304 Not Modified response with an empty body will be returned if the current ETag of the resource matches any of the given values. See RFC2616.
Returns
200 OK with a list of SharedFolder objects in the body.
304 Not Modified with an empty body. See the If-None-Match request header above.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl https://host.name/api/v1.2/alice@foo.corp/shares
[{
    "id": "0a1b2c...",
    "name": "Alice and Bob",
    "external": false,
    "members": [{
        "email": "alice@foo.corp",
        "first_name": "Alice",
        "last_name": "Foo",
        "permissions": ["WRITE", "MANAGE"]
      }, {
        "email": "bob@foo.corp",
        "first_name": "Bob",
        "last_name": "Foo",
        "permissions": ["WRITE"]
      }
    ],
    "pending": [{
        "email": "viewer@external.corp",
        "invited_by": "alice@foo.corp",
        "permissions": []
      }
    ],
    "caller_effective_permissions": ["WRITE", "MANAGE"]
  }
]

Retrieve shared folder metadata

Return metadata for a given shared folder.

Definition
GET /shares/{id}
Parameters
id Identifier of the shared folder.
Request header (optional)
If-None-Match One or more ETags. A 304 Not Modified response with an empty body will be returned if the current ETag of the resource matches any of the given values. See RFC2616.
Returns
200 OK with a SharedFolder object in the body.
304 Not Modified with an empty body. See the If-None-Match request header above.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl https://host.name/api/v1.2/shares/0a1b2c...
{
  "id": "0a1b2c...",
  "name": "Alice and Bob",
  "external": false,
  "members": [{
      "email": "alice@foo.corp",
      "first_name": "Alice",
      "last_name": "Foo",
      "permissions": ["WRITE", "MANAGE"]
    }, {
      "email": "bob@foo.corp",
      "first_name": "Bob",
      "last_name": "Foo",
      "permissions": ["WRITE"]
    }
  ],
  "pending": [{
      "email": "viewer@external.corp",
      "invited_by": "alice@foo.corp",
      "permissions": []
    }
  ],
  "caller_effective_permissions": ["WRITE", "MANAGE"]
}

Create shared folder

Create a new shared folder.

The calling user is the only initial member and is granted WRITE and MANAGE permissions.

Definition
POST /shares
Request body
A SharedFolder object.
Returns
201 Created with a SharedFolder object in the body.
Response header
Location URL of the new resource (the newly-created shared folder). See RFC2616.
$ curl -X POST https://host.name/api/v1.2/shares \
    -d '{"name": "Shareme"}'
{
  "id": "0a1b2c...",
  "name": "Shareme",
  "members": [{
      "email": "alice@foo.corp",
      "first_name": "Alice",
      "last_name": "Foo",
      "external": false,
      "permissions": ["WRITE", "MANAGE"]
    }
  ],
  "pending": [
  ],
  "caller_effective_permissions": ["WRITE", "MANAGE"]
}

Permissions

Access to shared folders is governed by a set of permissions. An initial set of permissions is granted as part of the invitation and can later be changed.

The following permissions are currently defined:

  • WRITE: The member is allowed to create, modify and delete files.

  • MANAGE: The member is allowed to invite new members to the shared folder and update permissions of other members.

Notes:

  • Permissions are independent from one another, i.e. it is possible to have MANAGE but not WRITE.

  • There is no explicit READ permission. Membership in a shared folder implies read access.

["WRITE", "MANAGE"]

Read, Write, and Manage access to a given folder, equivalent to the “Owner” role in the UI.

["WRITE"]

Read and Write access to a given folder, equivalent to the “Editor” role in the UI.

[]

Read access to a given folder, equivalent to the “Viewer” role in the UI.

The Member object

The Member object represents a member of a shared folder, i.e. an AeroFS user and the set of permissions they have been granted within the shared folder.

Attributes
email (read-only) Email address of the member.
first_name (read-only) First name of the member.
last_name (read-only) Last name of the member.
permissions List of permissions granted to the member.
{
  "email": "alice@foo.corp",
  "first_name": "Alice",
  "last_name": "Foo",
  "permissions": ["WRITE", "MANAGE"]
}

List members of a shared folder

List all the members of a given shared folder.

Definition
GET /shares/{id}/members
Parameters
id Identifier of the shared folder.
Request header (optional)
If-None-Match One or more ETags. A 304 Not Modified response with an empty body will be returned if the current ETag of the resource matches any of the given values. See RFC2616.
Returns
200 OK with a list of Member objects in the body.
304 Not Modified with an empty body. See the If-None-Match request header above.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl https://host.name/api/v1.2/shares/0a1b2c.../members
[{
    "email": "alice@foo.corp",
    "first_name": "Alice",
    "last_name": "Foo",
    "permissions": ["WRITE", "MANAGE"]
  }, {
    "email": "bob@foo.corp",
    "first_name": "Bob",
    "last_name": "Foo",
    "permissions": ["WRITE"]
  }
]

Retrieve member information

Retrieve information for one member of a shared folder.

Definition
GET /shares/{id}/members/{email}
Parameters
id Identifier of the shared folder.
email Email of the member.
Request header (optional)
If-None-Match One or more ETags. A 304 Not Modified response with an empty body will be returned if the current ETag of the resource matches any of the given values. See RFC2616.
Returns
200 OK with a Member object in the body.
304 Not Modified with an empty body. See the If-None-Match request header above.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl https://host.name/api/v1.2/shares/0a1b2c.../members/alice%40foo.corp
{
  "email": "alice@foo.corp",
  "first_name": "Alice",
  "last_name": "Foo",
  "permissions": ["WRITE", "MANAGE"]
}

Add a member to a shared folder

Add a user to a given shared folder, bypassing the invitation flow.

Definition
POST /shares/{id}/members
Parameters
id Identifier of the shared folder.
Requirements
Invited user must already have an AeroFS account.
Calling user must have MANAGE permission on the given shared folder.
Caller’s OAuth access token must have the acl.write scope.
Request body
A Member object.
Returns
201 Created with a Member object in the response body.
409 Conflict if the user is already a member of the shared folder.
Response header
Location URL of the new Member resource. See RFC2616.
$ curl -X POST https://host.name/api/v1.2/shares/0a1b2c.../members \
    -H 'Content-Type: application/json' \
    -d '{"email": "alice@foo.corp", "permissions": ["WRITE"]}'
{
  "email": "alice@foo.corp",
  "first_name": "Alice",
  "last_name": "Foo",
  "permissions": ["WRITE"]
}

Set member permissions

Update permissions for one member of a given shared folder.

Definition
PUT /shares/{id}/members/{email}
Parameters
id Identifier of the shared folder.
email Email of the member.
Requirements
Calling user must have MANAGE permission on the given shared folder.
Caller’s OAuth access token must have the acl.write scope.
Request body
A Member object that specifies the new location.
Request header (optional)
If-Match One or more ETags. The request will fail if the current ETag of the resource does not match any of the given values. See RFC2616.
Returns
200 OK with a Member object in the response body.
412 Precondition Failed if the conditions specified via the If-Match header could not be satisfied.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl -X PUT https://host.name/api/v1.2/shares/0a1b2c.../members/alice%40foo.corp \
  -H "Content-Type: application/json" \
  -d '{"permissions": []}'

Will revoke WRITE and MANAGE permissions on shared folder 0a1b2c… for alice@foo.corp

Remove a member from a shared folder

Remove a member from a given shared folder.

Definition
DELETE /shares/{id}/members/{email}
Parameters
id Identifier of the shared folder.
email Email of the member to remove.
Requirements
Calling user must have MANAGE permission on the given shared folder.
Caller’s OAuth access token must have the acl.write scope.
Request header (optional)
If-Match One or more ETags. The request will fail if the current ETag of the resource does not match any of the given values. See RFC2616.
Returns
204 No Content with an empty body.
412 Precondition Failed if the conditions specified via the If-Match header could not be satisfied.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl -X DELETE https://host.name/api/v1.2/shares/0a1b2c.../members/alice%40foo.corp

The PendingMember object

The PendingMember object represents an AeroFS user that has been invited to a shared folder but has not yet accepted the invitation.

Attributes
email (read-only) Email address of the pending member.
first_name (read-only) First name of the pending member.
last_name (read-only) Last name of the pending member.
invited_by (read-only) Email address of the user who extended the invitation.
permissions List of permissions granted to the member within the shared folder.
note (write-only) Message sent to the invited user.

Notes: The first_name and last_name field may not be present if the user was invited to the shared folder before creating an AeroFS account.

{
  "email": "viewer@external.corp",
  "invited_by": "alice@foo.corp",
  "permissions": []
}

List pending members of a shared folder

List all pending members of a given shared folder.

Definition
GET /shares/{id}/pending
Parameters
id Identifier of the shared folder.
Request header (optional)
If-None-Match One or more ETags. A 304 Not Modified response with an empty body will be returned if the current ETag of the resource matches any of the given values. See RFC2616.
Returns
200 OK with a list of PendingMember objects in the body.
304 Not Modified with an empty body. See the If-None-Match request header above.
Response header
ETag Entity tag for subsequent conditional requests.
$ curl https://host.name/api/v1.2/shares/0a1b2c.../pending
[{
    "email": "viewer@external.corp",
    "invited_by": "alice@foo.corp",
    "permissions": []
  }
]

Retrieve pending member information

Retrieve information relative to a pending member of a given shared folder.

Definition
GET /shares/{id}/pending/{email}
Parameters
id Identifier of the shared folder.
email Email of the member.
Returns
200 OK with a PendingMember object in the body.
$ curl https://host.name/api/v1.2/shares/0a1b2c.../pending/viewer%40external.corp
{
  "email": "viewer@external.corp",
  "invited_by": "alice@foo.corp"
  "permissions": []
}

Invite a user to a shared folder

Invite an user to join a given shared folder.

If the user had already been invited the existing invitation will be updated to reflect the new permissions, if needed.

The note field is sent in an email informing the user of the pending invitation. Each invitation will result in a separate email.

Definition
POST /shares/{id}/pending
Parameters
id Identifier of the shared folder.
Requirements
Calling user must have MANAGE permission on the given shared folder.
Caller’s OAuth access token must have the acl.write scope.
Request body
A PendingMember object.
Returns
201 Created with a PendingMember object in the response body.
409 Conflict if the user is already a member of the shared folder.
Response header
Location URL of the new resource. See RFC2616.
$ curl -X POST https://host.name/api/v1.2/shares/0a1b2c.../pending \
    -H 'Content-Type: application/json' \
    -d '{"email": "writer@external.corp", "permissions": ["WRITE"], "note": "Put your report here"}'
{
  "email": "writer@external.corp",
  "invited_by": "alice@foo.corp",
  "permissions": ["WRITE"]
}

Remove pending member

Remove a pending member from a given shared folder, revoking their invitation.

Definition
DELETE /shares/{id}/pending/{email}
Parameters
id Identifier of the shared folder.
email Email of the member.
Returns
204 No Content with an empty body.
$ curl -X DELETE https://host.name/api/v1.2/shares/0a1b2c.../pending/viewer%40external.corp

The Invitation object

The Invitation object represents a pending invitation to a shared folder. You may use methods on this object to list, accept or dismiss pending invitations.

Attributes
share_id (read-only) Identifier of the shared folder.
share_name (read-only) Name of the shared folder.
invited_by (read-only) Email address of the user who extended the invitation.
permissions (read-only) List of permissions granted by the invitation.
{
  "share_id": "0a1b2c...",
  "share_name": "Meeting notes",
  "invited_by": "alice@foo.corp",
  "permissions": []
}

List shared folder invitations

List all pending shared folder invitations for a given user.

There cannot be multiple invitations to the same shared folder.

Definition
GET /users/{email}/invitations
Parameters
email Email of the user.
Returns
200 OK with a list of Invitation objects in the body.
$ curl https://host.name/api/v1.2/users/alice%40foo.corp/invitations
[{
    "share_id": "0a1b2c...",
    "share_name": "Meeting notes",
    "invited_by": "alice@foo.corp",
    "permissions": []
  }
]

View shared folder invitation

View a pending shared folder invitation for a given user.

Definition
GET /users/{email}/invitations/{share_id}
Parameters
email Email of the user.
share_id Identifier of the SharedFolder
Returns
200 OK with an Invitation object in the body.
$ curl https://host.name/api/v1.2/users/alice%40foo.corp/invitations/0a1b2c...
{
  "share_id": "0a1b2c...",
  "share_name": "Meeting notes",
  "invited_by": "alice@foo.corp",
  "permissions": []
}

Accept a shared folder invitation

Accept a shared folder invitation.

Definition
POST /users/{email}/invitations/{share_id}
Parameters
email Email of the user.
share_id Identifier of the SharedFolder
Query Parameters
external Whether to join the folder as external. Valid values are 0 and 1, with 0 being the default.
Returns
201 Created with a SharedFolder object in the body.
Response header
Location URL of the SharedFolder resource. See RFC2616.
$ curl -X POST https://host.name/api/v1.2/users/alice%40foo.corp/invitations/0a1b2c...

Ignore a shared folder invitation

Ignore a pending shared folder invitation for a given user.

Definition
DELETE /users/{email}/invitations/{share_id}
Parameters
email Email of the user.
share_id Identifier of the SharedFolder
Returns
204 No Content with an empty body.
$ curl -X DELETE https://host.name/api/v1.2/users/alice%40foo.corp/invitations/0a1b2c...