Copy.com API Documentation

Welcome to the online documentation for the Copy.com cloud storage API. This guide is for developers who would like to build Third-Party applications which integrate with the Copy platform.

We are still making exciting updates to the API, and will be adding more features all the time. We will strive to release backwards-compatible changes under the same API version (currently 1) and release breaking changes under another API version.

Synopsis

The following list of acronyms and buzzwords can be used to describe the Copy.com API:

  • OAuth 1.0a Authentication
  • RESTful URL Based Interface
  • HTTP Method Friendly
  • JSON Response Objects
  • JSON Request Bodies
  • Self-Hosted, Third-Party Applications

Copy.com is a cloud storage platform, complimented by both desktop and mobile applications which work together to keep files synchronized across multiple platforms on behalf of users. There is even a companion website which allows for administration of files and account settings.

The maximum storage capacity changes depending on bonuses and referrals said users have earned. Users can collaborate in the form of companies, and share files among employees, other Copy users, and even anonymous visitors throught the use of Public Links.

If any of the API documentation seems a bit too complex, you can always just grab one of our Client Libraries. Those libraries abstract the API layer and provide an interface that adheres to the language of your choice.

Applications you create will be hosted on your servers. We do not offer any sort of user-referral program for your applications; you'll need to refer your own users.

Developer Signup

In order to use the Copy.com API, you will need to perform the following steps:

Create a User Account

The first step to becoming a developer and creating an application is to visit the User Signup page. Even though you may have many different developers working on your application, there will be a single user account created for performing administrative actions for this applicaion. If you are a single-developer, just use your normal account.

Feel free to share the credentials of this account with other people within your organization. We recommend using an email address like support@example.com or team@example.com, to make it obvious this central developer account is not associated with a single person.

Create a Developer Account

The second step is to make your user account a developer account. You can do this by visiting the Developer Signup page. This page will ask for some information about you or your organization, such as name, phone number, address, and a URL.

The phone number and address will not be public. The name and URL of your company will be public. Users will see this information when they are authorizing your application.

This URL shouldnt be the URL to get to your application, it should instead be the URL to your company or developer informational website. A URL to go to your application can be entered once you're ready to create a particular application.

Create your First Applicaion

Now you're ready to create your first application. Do this by visiting the Create Application page. This page will ask you for some information about your application, notably the Name, a Description, and a URL to a page describing your application.

Once you create your application, you will be given a Consumer Key, and a Consumer Secret. These two strings are 32 and 48 characters long, respectively. They are used during the OAuth Authorization stages of the process, as well as being required for every request made to our API. You will need to store it somewhere in your application code. Keep them secure and never let your users have access to them. You can find the Key/Secret pair later by visiting the Created Applications screen.

Once your application has been created, it immediately works with the API and can be signed up for by anyone you allow.

Authentication

The Copy.com API uses the OAuth 1.0a specification for client authorization. This is a Three-Legged system allowing your application to work with many different users.

You will need to store your initial Consumer Secret and Consumer Token variables within your application, and then you'll probably want to store each users Access Secret and Access Token in a database along with applicable user data.

OAuth Handshake

Copy uses the OAuth 1.0a specification for performing User Authorization.

If at any point you'd like to see real OAuth requests happening, visit the API Console which will show you HTTP message in detail.

General Overview

First, you will want to get yourself a pair of Consumer Key and Consumer Secret values.

Second, each time a user wants to authorize your application to access their data, your app will need to ask for a Request Token and Request Secret pair from us, while at the same time specifying the permissions your application will require (if you require user-specific permissions, that is).

After that your application will need to send the user to our Authorize page where they will review the permissions you have requested before being returned back to your application, where your app will request a permanent pair of Access Token and Access Secret keys.

Subsequent requests will make use of the Consumer Key, Consumer Secret, Access Token, and Access Secret values for every API interaction. The Request Token and Request Secret values can now be discarded.

OAuth/API Endpoints

These URLs will need to stored within your applications configuration as they are used for the OAuth Handshaking process:

  • https://api.copy.com/oauth/request
    • This is where a Request Token is requested
  • https://www.copy.com/applications/authorize
    • This is where the user will be forwarded in order to authorize your application
  • https://api.copy.com/oauth/access
    • This is where an Access Token is requested
  • https://api.copy.com/rest/*
    • This is the root URL for making REST requests to our API

Detailed OAuth Steps

Here is a fairly in-depth overview of our implementation of OAuth. Before starting here you should have already created a developer account, created an application, and recorded your Consumer Key and Consumer Secret.

1: 3RD PARTY SERVER asks COPY API for a REQUEST TOKEN

The first step is for your application to make a request for a Request Token. When you make this request, you should have a user who is logged-in to your application, and you should know which permissions you are asking for. These tokens will expire shortly, so you probably want to request one right before sending your user through the authorization process.

Visit the RFC section on Temporary Credentials for more information.

According to the OAuth spec, the parameters you can provide can either be sent through the Authorization Header, or through GET parameters. Our optional scope parameter, which is non-standard to OAuth, must be provided as a GET variable. If it is sent through the Authorization Header, it will be disregarded.

Parameters
  • URL:
    • https://api.copy.com/oauth/request
  • GET:
    • scope?
  • GET or Authorization Header:
    • oauth_callback
    • oauth_consumer_key
    • oauth_signature_method
    • oauth_nonce
    • oauth_timestamp
    • oauth_version?
    • oauth_signature
  • RESPONSE BODY:
    • oauth_token
    • oauth_token_secret
    • oauth_callback_confirmed
Captured Network Traffic

Here is some example network traffic captured using Wireshark. The indented text on multiple lines are just linebreaks to make it easier to read. In this example, all of the OAuth parameters have been provided in the Authorization Header, and only the non-standard scope parameter has been provided via GET parameter.

Request

GET /oauth/request?scope=%7B%22profile%22%3A%7B%22read%22%3A+true%2C%22write%22%3A+true%7D%7D HTTP/1.1
User-Agent: PECL-OAuth/1.2.3
Host: api.copy.com
Accept: */*
Authorization: OAuth
    oauth_callback="http://copy-oauth.local/get_access_token.php"
    oauth_consumer_key="g4rotFX8k1MNcXsZY5zBkE5L5QlEmTJg",
    oauth_signature_method="HMAC-SHA1",
    oauth_nonce="147648019951a8be1b1b7a37.94665308",
    oauth_timestamp="1370013211",
    oauth_version="1.0",
    oauth_signature="aAar8Fy1%2FgIh7xLhGIn7nDTPtYQ%3D"

Response

HTTP/1.1 200 OK
Server: nginx/1.2.6
Date: Fri, 31 May 2013 15:13:29 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Cache-Control: no-cache

oauth_token=2NXCMeG771vhoWY5vhGIm4xbGuxqUvbH&
    oauth_token_secret=6EM7NJuMm4LDAzoQv86dNkiTFEtM0QpwYvWNwcNlsciLcIOX&
    oauth_callback_confirmed=true
2a: USER AGENT asks COPY WEB for a AUTHORIZE SCREEN

Once you've received the Request Token and Request Secret, you are ready to send your user off to our Authorize screen. In the response, you received a piece of data called the oauth_token. When redirecting the users User Agent (Browser) to us, make sure you add that parameter as a GET variable with the same parameter name. When the Request Token was created earlier, you provided Copy a callback URL. When the user is done authorizing, Copy will redirect them to your callback URL. If you provided a user-specific scope parameter, we'll display those permissions to the user instead of the application default.

Visit the RFC section on Resource Owner Authorization for more information.

Parameters
  • URL:
    • https://www.copy.com/applications/authorize
  • GET:
    • oauth_token
  • ACTIONS:
    • Display permissions
    • If not already logged in, user will login or create an account
    • User may choose which companies to allow access to (not yet supported)
    • If user denies, process is cancelled
    • If user allows, the user is attached to the request token, and redirectd to oauth_callback from earlier
2b: USER AGENT is redirected to 3RD PARTY

Once the user has clicked the Authorize button, we connected the Copy user to the Request Token. Copy then redirects the user back to your application, by way of the oauth_callback URL you provided earlier.

Copy appended two GET parameters to the URL before redirecting the user to your callback URL. The first one is the oauth_token from before (so that you can do a lookup), and the second one is oauth_verifier, which is going to be used for verification purposes in the next step.

Parameters
  • URL:
    • oauth_callback
  • GET:
    • oauth_token
    • oauth_verifier
3: 3RD PARTY asks COPY API for an ACCESS TOKEN

Now that the user is back to your server, it is time for your application to ask for an Access Token. Chances are, the callback URL page of yours will immediately request an Access Token from us. Technically, your application could wait before performing this action, but the verifier will expire and your user may grow impatient.

Visit the RFC section on Token Credentials for more information.

Parameters
  • URL:
    • https://api.copy.com/oauth/access
  • GET or Authorization Header:
    • oauth_verifier
    • oauth_consumer_key
    • oauth_signature_method
    • oauth_nonce
    • oauth_timestamp
    • oauth_version?
    • oauth_token
    • oauth_signature
  • RESPONSE BODY:
    • oauth_token
    • oauth_token_secret
Captured Network Traffic

Once again, the longer lines have been broken up onto multiple lines. In this example, all of the parameters are being sent using the Authorization Header, but again, they could have been sent via GET parameters.

Once the request succeeds, you'll want to keep track of the oauth_token and oauth_token_secret values, which are your users Access Token and Access Secret values. Since these are unique for each of your/our users, you'll probably want to keep them in a database alongside your users data. These values are used for every single API request you make later.

Request

GET /oauth/access HTTP/1.1
User-Agent: PECL-OAuth/1.2.3
Host: api.copy.com
Accept: */*
Authorization: OAuth
    oauth_verifier="66aac376b9eeaa01f17448c7923bd3c9",
    oauth_consumer_key="g4rotFX8k1MNcXsZY5zBkE5L5QlEmTJg",
    oauth_signature_method="HMAC-SHA1",
    oauth_nonce="146033336251a8be1e459232.95602358",
    oauth_timestamp="1370013214",
    oauth_version="1.0",
    oauth_token="2NXCMeG771vhoWY5vhGIm4xbGuxqUvbH",
    oauth_signature="1Pp0VQNIXiSNZGer%2B3m0SlDfm4s%3D"

Response

HTTP/1.1 200 OK
Server: nginx/1.2.6
Date: Fri, 31 May 2013 15:13:32 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Cache-Control: no-cache

oauth_token=GUNPB60RyUIdDfW9Am2DEicVBDQJL4AM&oauth_token_secret=VhnIFZjubab8cCFmSLD8wm6mx1HQCrEtEv3afS495ZA825Bd
4: 3RD PARTY asks COPY API for some DATA

This step of the process will happen many, many times. Since you've now got all the tokens required to make requests, the only part really changing are the nonce, timestamps, and signatures.

Parameters
  • URL:
    • https://api.copy.com/rest/user
  • GET or Authorization Header:
    • oauth_consumer_key
    • oauth_signature_method
    • oauth_nonce
    • oauth_timestamp
    • oauth_version
    • oauth_token
    • oauth_signature
Captured Network Traffic

Again, this example is making use of the Authorization Header for these requests, and long lines have been broken up.

Request

GET /rest/user HTTP/1.1
User-Agent: PECL-OAuth/1.2.3
Host: api.copy.com
X-Api-Version: 1
Accept: application/json
Authorization: OAuth
    oauth_consumer_key="g4rotFX8k1MNcXsZY5zBkE5L5QlEmTJg",
    oauth_signature_method="HMAC-SHA1",
    oauth_nonce="102850354651a8be236b3413.56739262",
    oauth_timestamp="1370013219",
    oauth_version="1.0",
    oauth_token="GUNPB60RyUIdDfW9Am2DEicVBDQJL4AM",
    oauth_signature="iLZyyUx%2F6ZLVqVn6zGF4mghF8z0%3D"

Response

HTTP/1.1 200 OK
Server: nginx/1.2.6
Date: Fri, 31 May 2013 15:13:37 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Cache-Control: no-cache

{"id": "1381231", "first_name": "Thomas", "last_name": "Hunter", "more": "stuff"}

Permissions

Permissions specify which parts of the API your application can interact with. Permissions are set on a per-token basis. While creating a new application, you can specify default permissions. These will be used if you do not specify a set of permissions at the time of creating a Request Token.

NOTE: Permissions are currently disregarded by the current version of the API. This means all requests can be made and complete responses will always be returned.

When the user views the Authorize screen, a listing of the permissions your application is requesting will be displayed. If the user feels your application is requesting too much, they can deny the authorize request. Be sure to only request the permissions you need.

Throughout the API Calls section of this document, you will see each API call documented with a list of Required Permissions for the call. These are in the format xx.yy.zz, and follow the conventions one would normally use for accessing the applicable JSON document element. For example, if a permission requires the ability to read the users Copy files, it will be displayed as filesystem.read.

Token-Specific Permissions

If your application will always require the same permissions for every user authorization, you can disregard this section.

When requesting a Request Token from our server, you can specify a scope GET parameter. This parameter is a JSON encoded document representing the same permission fields you have entered on the application create screen.

Here is a sample document showing all permissions which your application can request. Note that every one of these permissions uses a boolean value. If any of these permissions are omitted, they will be assumed to be false.

{
  "profile": {
    "read": true,
    "write": true,
    "email": {
      "read": true
    }
  },
  "inbox": {
    "read": true
  },
  "links": {
    "read": true,
    "write": true
  },
  "filesystem": {
    "read": true,
    "write": true
  }
}

This JSON document is sent via a GET parameter during the Request Token generation step, that is, when you are hitting the /oauth/request endpoint. This data, as it is outside of the scope of OAuth, will not affect your hash generation. You will need to make your scope document fits on one line, remove any superfluous whitespace, and URL Encode the data. For example, the PHP code to do this looks like so:

<?php
$scope = json_encode(array(
    'profile' => array(
        'read' => true,
        'write' => true
    )
));

$request_token_url = 'http://api.copy.local/oauth/request?scope=' . urlencode($scope);

You can read more about this step in the Temporary Credentials section of the OAuth spec. While this scope attribute is outside the scope of OAuth, reading that section will let you know at which point in the process you need to set this scope parameter.

Permissions Explained

Here's some high-level information about each of the permission sections. For more details, you'll want to look at each API call and see which permissions are required.

Profile

This gives access to the users account information, and can give read access to the users email address. Do not use this to send unsolicited emails to users! You will want to have an unsubscribe link in the footer of any emails your application generates. Applications which are found to abuse user emails will be deleted, and your developer account will be deleted as well.

If you do not need to access the users profile, you can omit this section of the scope JSON document.

Inbox

This gives you read access to the users inbox items, e.g. the items which has been shared with the user.

Links

This gives you access to files that the user has shared or files which have been shared with the user. This will often truncate JSON responses you see later in this documentation. Specifically, if you see sections of response documents which contain token or links[] attributes, those will likely be missing without links.read. You can also use this to create links to files, which will give you URLs that the files can be shared with others.

If you do not need to access shared link information, you can omit this section of the scope JSON document.

Filesystem

This will give you access to the users Copy files.

If you do not need to access the users Copy files, you can omit this section of the scope JSON document.

API Calls

In the URLs of the sample requests, words that appear in all CAPITAL letters represent the part of the URL which is meant to be changed depending on the intention of the request.

Request Headers

You will need to provide the following two HTTP request headers while talking to the Copy API:

  • X-Api-Version: 1
  • Accept: application/json
Response Headers

Here's a list of some typical response headers to expect from our server, many of which can be ignored.

  • Access-Control-Allow-Headers: X-AUTHORIZATION-ANON, X-AUTHORIZATION, X-CLIENT-VERSION, X-CLIENT-TYPE, X-CLIENT-OSVERSION, X-CLIENT-HWID, X-CLIENT-NAME, X-API-VERSION, X-REQUESTED-WITH, X-HTTP-METHOD-OVERRIDE, CONTENT-TYPE
  • Access-Control-Allow-Methods: POST, GET, PUT, PATCH, DELETE, OPTIONS
  • Access-Control-Allow-Origin: *.copy.com
  • Access-Control-Max-Age: 1728000
  • Cache-Control: no-cache
  • Connection: keep-alive
  • Date: Wed, 27 Mar 2013 21:19:39 GMT
  • Server: nginx/1.2.7
  • Transfer-Encoding: chunked
  • X-Powered-By: PHP/5.4.12
  • X-Copy-Time: 1364419178
  • Content-Type: application/json

X-Copy-Time: The current time, according to the Copy server. This is useful for comparing timestamp times and keeping relative time working as expected.

Content-Type: Since we have a RESTful JSON api, most of our responses should be JSON encoded. A notable exception is performing a GET request on the files section of the API for a file, or the thumbs section of the API for an image thumbnail.

HTTP Verbs

The Copy.com API makes use of the four and a half primary HTTP verbs. Here's an overview of what each one does:

  • GET: Returns information about the resource at the specified URL
  • POST: Creates a new entry at the specified resource collection URL
  • PUT: Updates an entry at the specified resource URL
  • PATCH: Updates a partial entry at the specified resource URL
  • DELETE: Deletes an entry at the specified resource URL

When performing a GET or DELETE request, the URL typically specifies the particular item being retreived or removed. When performing a PUT or PATCH request, the URL also specifies the item being acted upon, and the data to be sent is in the form of a JSON document as the body of the request. When performing a POST request, the URL doesn't mention the specific resource ID in it, but actually the parent collection, as we are actually acting upon the collection and not an existing record. The POST body is also a JSON document.

Time

When retrieving timestamp information, the API will always send a UNIX timestamp, which is the number of seconds since the UNIX epoch on January 1st, 1970. An example time may look like this:

1365103180

This timestamp is based on UTC time. Converting this timestamp into a format consumable by your language should be relatively easy. Here are a few examples:

PHP
<?php
echo date('Y m d H:i:s', 1365103180);
# 2013 04 04 15:19:40
JavaScript
var date = new Date(1365103180 * 1000);
console.log(date);
// Thu Apr 04 2013 15:19:40 GMT-0400 (EDT)

User Profile API Calls

These calls deal with reading and writing a users profile information. Notably, for setting and getting the users name, account storage quotas, and getting their email.

Read User Profile

GET https://api.copy.com/rest/user
Permissions
  • profile.read: Required
  • profile.email.read: Required to get the .emails[].email and .email attributes

This API call will give you basic information about a users account. The contents of the returned document will vary depending on what permissions you have access to.

The storage attribute contains storage information in bytes. The used attribute is the number of bytes currently being used by the user. The quota attribute is the total number of bytes the user can use. The saved attribute is the number of bytes the user is saving from using against their quota by using our Fair Storage calculation.

Example Response (200 OK)
{
  "id": "1381231",
  "storage":{
    "used": 9207643837,
    "quota": 1100585369600,
    "saved": 14557934927
  },
  "first_name": "Thomas",
  "last_name": "Hunter",
  "developer": true,
  "created_time": 1358175510,
  "email": "thomashunter@example.com",
  "emails": [
    {
      "primary": true,
      "confirmed": true,
      "email": "thomashunter@example.com",
      "gravatar": "eca957c6552e783627a0ced1035e1888"
    },
    {
      "primary": false,
      "confirmed": true,
      "email": "thomashunter@example.net",
      "gravatar": "c0e344ddcbabb383f94b1bd3486e55ba"
    }
  ]
}

Update User Profile

PUT https://api.copy.com/rest/user
Permissions
  • profile.write: Required
  • profile.read: Required to get the response back from the server
  • profile.email.read: Required to get the .emails[].email and .email attributes in the response back from the server

This request can be used to update the users name.

The response from this request is the same as the corresponding GET request above, however, the storage attribute will be missing.

Example Request
{
  "first_name": "Thomas",
  "last_name": "Hunter"
}
Example Response (200 OK)
{
  "id": "1381231",
  "first_name": "Thomas",
  "last_name": "Hunter",
  "developer": true,
  "created_time": 1358175510,
  "push_token": "33eda843fbcfed0522bdb0a704984425b553dbe9",
  "email": "thomashunter@example.com",
  "emails": [
    {
      "primary": true,
      "confirmed": true,
      "email": "thomashunter@example.com",
      "gravatar": "eca957c6552e783627a0ced1035e1888"
    },
    {
      "primary": false,
      "confirmed": true,
      "email": "thomashunter@example.net",
      "gravatar": "c0e344ddcbabb383f94b1bd3486e55ba"
    }
  ]
}

Filesystem API Calls

This section of the API deals with reading and writing to the users Copy files. Note that there are a few different top-level API entrypoints for fulfilling these tasks. The meta endpoint is used for browsing the users Copy filesystem. The files endpoint is used for reading file contents, creating and updating files and directories. The thumbs endpoint is used for loading thumbnailed versions of image files (so that you don't need to resize them yourself).

Filesizes (size attributes) are measured in bytes. If an item displayed in the filesystem is a directory or an otherwise special location which doesn't represent a file, the size attribute will be null.

Types

Different objects in the filesystem will have different type attributes associated with them.

  • file: This is a file. It might have been shared (check the links attribute)
  • dir: This is a normal directory
  • root: This is the Root entrypoint into the filesystem, think of it as "My Computer"
  • copy: This is the top level item in the filesystem which can be written, think of it as "C:\"
  • inbox: This is a location where items shared with the user will be visible
  • share: This is a directory which has been shared
Link Attribute

A link is what is created if a user has shared an item. Links can have recipients, permissions, and most importantly, a public URL which can be shared with other people. See the Links Section of this document for more information regarding manipulating them.

User Experience

You'll notice below how we have inbox information in the root of a users filesystem. The reason for this is we want Third Party developers to give our users a consistent browsing experience. If you ever find yourself building a file browser for the users Copy files, please keep inbox items accessible in the root, even if the users default entry point into the filesystem is below the root. This will ensure users recognize their filesystem no matter what application they use.

Read Root Directory

GET https://api.copy.com/rest/meta
Permissions
  • filesystem.read: Required
  • inbox.read: Required for the /inbox child element

The /rest/meta entrypoint into the API represents the top-most level of the filesystem. This level sort of shows a listing of the real filesystems below it. Think of this location as the My Computer of a Windows environment. You can't write to this location, just browse it.

Example Response (200 OK)
{
  "id": "/",
  "path": "/",
  "name": "Copy",
  "type": "root",
  "stub": false,
  "children": [
    {
      "id": "/copy",
      "path": "/",
      "name": "Copy Folder",
      "type": "copy",
      "stub": true
    },
    {
      "id": "/inbox",
      "path": "/",
      "name": "Shared With Me",
      "type": "inbox",
      "stub": true,
      "counts": {
        "count_new": 7,
        "count_viewed": 30,
        "count_hidden": 95
      }
    }
  ]
}

The stub attribute you see on all of the nodes represents if the specified node is incomplete, that is, if the children have not all been delivered to you. Basically, they will always be a stub, unless you are looking at that item directly.

Read Root Copy Directory

GET https://api.copy.com/rest/meta/copy
Permissions
  • filesystem.read: Required
  • links.read: Required for children[].token, children[].links[] attributes to be available

This call represents the first level of the real filesystem. You can add more levels of depth to the URL in accordance with the users filesystem. For example, if you would like to read data from a top level directory named Applications, the URL would be https://api.copy.com/rest/meta/copy/Applications.

NOTE: This call may incorrectly return a 404 if the user has no files. However, the resource can still have content added to it.

Example Response (200 OK)
{
  "id": "/copy",
  "path": "/",
  "name": "Copy Folder",
  "type": "copy",
  "size": null,
  "date_last_synced": null,
  "stub": false,
  "children": [
    {
      "id": "/copy/Applications",
      "path": "/Applications",
      "name": "Applications",
      "link_name": "",
      "token": "",
      "permissions": "",
      "public": false,
      "type": "dir",
      "size": null,
      "date_last_synced": 1360079517,
      "stub": true,
      "counts": [
      ],
      "recipient_confirmed": false,
      "object_available": true,
      "links": [
        {
          "id": "hPTBeqqN9Bg9",
          "public": true,
          "expires": false,
          "expired": false,
          "url": "https://copy.com/hPTBeqqN9Bg9/Applications",
          "url_short": "https://copy.com/hPTBeqqN9Bg9",
          "recipients": [
          ],
          "creator_id": "1381231",
          "confirmation_required": false
        }
      ],
      "url": "https://copy.com/web/Applications",
      "thumb": false
    },
    {
      "id": "/copy/binding-of-isaac.png",
      "path": "/binding-of-isaac.png",
      "name": "binding-of-isaac.png",
      "link_name": "",
      "token": "",
      "permissions": "",
      "public": false,
      "type": "file",
      "size": 164850,
      "date_last_synced": 1363573463,
      "stub": true,
      "counts": [
      ],
      "recipient_confirmed": false,
      "object_available": true,
      "links": [
      ],
      "url": "https://copy.com/web/bad-binding-of-isaac.png",
      "revision_id": 2897,
      "thumb": "https://copy.com/thumbs/bad-binding-of-isaac.png",
      "thumb_original_dimensions": {
        "width": 800,
        "height": 620
      }
    }
  ]
}

List File Revisions (Currently Unfunctional)

GET https://api.copy.com/rest/meta/copy/PATH/TO/FILE/@activity
Permissions
  • filesystem.read: Required
  • profile.email.read: Required to get the revisions.creator.email attributes

This returns a listing of the different revisions that have been made to the file. Whenever you see a revision_id value, it is a unique number to identify a revision. These numbers are not sequential per file. You will need to make this request to get a listing of the different revisions on a file (although a normal request can get you the most current revision_id of the file).

A revision will have the conflict attribute set to true if a client uploads a file more recently than a different file while having an older timestamp.

If different users are editing the same file (e.g., the file is within a SYNC'd share directory), different revisions will have different users attached to them.

Example Response (200 OK)
{
  "id": "/copy/Big%20API%20Changes/API-Changes.md/@activity",
  "path": "/Big API Changes/API-Changes.md",
  "name": "Activity",
  "token": null,
  "permissions": null,
  "syncing": false,
  "public": false,
  "type": "file",
  "size": 12670,
  "date_last_synced": 1365543105,
  "stub": false,
  "recipient_confirmed": false,
  "url": "https://copy.com/web/Big%20API%20Changes/API-Changes.md",
  "revision_id": "5000",
  "thumb": null,
  "share": null,
  "counts": [
  ],
  "links": [
  ],
  "revisions": [
    {
      "revision_id": "5000",
      "modified_time": 1365543105,
      "size": 12670,
      "latest": true,
      "conflict": false,
      "id": "/copy/Big%20API%20Changes/API-Changes.md/@activity/@time:1365543105",
      "type": "revision",
      "creator": {
        "user_id": "1381231",
        "created_time": 1358175510,
        "email": "thomashunter@example.com",
        "first_name": "Thomas",
        "last_name": "Hunter",
        "confirmed": true
      }
    },
    {
      "revision_id": "4900",
      "modified_time": 1365542000,
      "size": 12661,
      "latest": false,
      "conflict": true,
      "id": "/copy/Big%20API%20Changes/API-Changes.md/@activity/@time:1365542000",
      "type": "revision",
      "creator": {
        "user_id": "1381231",
        "created_time": 1358175510,
        "email": "thomashunter@example.com",
        "first_name": "Thomas",
        "last_name": "Hunter",
        "confirmed": true
      }
    },
    {
      "revision_id": "4800",
      "modified_time": 1365543073,
      "size": 12658,
      "latest": false,
      "conflict": false,
      "id": "/copy/Big%20API%20Changes/API-Changes.md/@activity/@time:1365543073",
      "type": "revision",
      "creator": {
        "user_id": "1381231",
        "created_time": 1358175510,
        "email": "thomashunter@example.com",
        "first_name": "Thomas",
        "last_name": "Hunter",
        "confirmed": true
      }
    }
  ]
}

Get Specific File Revision (Currently Unfunctional)

GET https://api.copy.com/rest/meta/copy/PATH/TO/FILE/@activity/@time:TIME
Permissions
  • filesystem.read: Required
  • links.read: Required for links attribute to be available

Returns information about the file at the specified revision. You can get the URL for this from the above request.

Example Response (200 OK)
{
  "id": "/copy/Big%20API%20Changes/API-Changes.md/@activity/@time:1365532651",
  "path": "/Big API Changes/API-Changes.md",
  "name": "API-Changes.md",
  "token": null,
  "permissions": null,
  "syncing": false,
  "public": false,
  "type": "file",
  "size": 12666,
  "date_last_synced": 1365532651,
  "stub": false,
  "recipient_confirmed": false,
  "url": "https://copy.com/web/Big%20API%20Changes/API-Changes.md?revision=4898",
  "revision_id": 4898,
  "thumb": null,
  "share": null,
  "counts": [
  ],
  "links": [
  ]
}

Download Raw File Contents

GET https://api.copy.com/rest/files/PATH/TO/FILE
Required Permissions
  • filesystem.read: Required
  • links.read: Required for children[].token, children[].links[] attributes to be available

This request is useful for getting the binary contents of a file.

The path is similar to the /rest/meta/copy requests, except that the copy url segment isn't required. For example, the following two URLs correlate to the same file:

  • https://api.copy.com/rest/meta/copy/subdirectory/file.txt
  • https://api.copy.com/rest/files/subdirectory/file.txt

The response to this request is NOT a JSON representation of data, it is instead the actual contents of the file, as if it were being downloaded. Please note that if you are building a third party web application, you will need to have a local proxy for this before downloading data to your user, as requesting the file directly will return an error since the OAuth headers will not be present.

Note that when making the meta requests above, there is an attribute called object_available. If this attribute is set to false, it means the file is still being uploaded, and making this files request will fail as the file will not yet be available.

Delete File or Directory

DELETE https://api.copy.com/rest/files/PATH/TO/FILE
Permissions
  • filesystem.write: Required

Deletes a file at the provided location. Returns a 204 No Content on success. Returns a 404 File Not Found if the file does not exist.

You can run a delete either on a directory or a file. All files below the directory being deleted will be removed.

Rename File or Directory

PUT https://api.copy.com/rest/files/PATH/TO/FILE.TXT?name=NEWFILE.TXT&overwrite=true
Permissions
  • filesystem.write: Required

Performs a rename of the file, where the file stays in the same directory but its name has changed. Think of this as a relative move. Returns a 200 status code on success.

The overwrite parameter is optional and defaults to true, which will cause a file rename to overwrite any existing file named name. If it is set to false, the filename will have some text appended automatically to prevent an overwrite. For example, TPS_Report.pdf would be renamed TPS_Report (1).pdf. If an item exists with the same number in parenthesis, the next available number will be chosen. The resulting filename will be returned in the response.

Move File or Directory

PUT https://api.copy.com/rest/files/PATH/TO/FILE.TXT?path=/NEW/PATH/TO/FILE.TXT&overwrite=true
Permissions
  • filesystem.write: Required

Performs a move of the file, where the file moves into a new directory. The path variable is an absolute path. Returns a 200 status code on success.

The overwrite parameter is optional and defaults to true, which will cause a file move to overwrite any existing file at path. If it is set to false, the filename will have some text appended automatically to prevent an overwrite. For example, Docs/TPS_Report.pdf would be renamed Docs/TPS_Report (1).pdf. If an item exists with the same number in parenthesis, the next available number will be chosen. The resulting filename will be returned in the response.

Create File or Directory

POST https://api.copy.com/rest/files/PATH/TO/FILE?overwrite=true
Permissions
  • filesystem.write: Required

Creating a Directory: If you perform a POST to the URL, provide the name of the directory within the URL that you would like to create. For example, if you wanted to create a new directory Phylum within an existing directory Kingdom located at the root of the users Copy directory, you would do POST files/Kingdom/Phylum with an empty body. NOTE: The overwrite flag does not work with directory creation.

Creating a File: If you perform a POST to the URL, provide the name of the existing directory that the file will live in. The filename is not a part of the URL, but is instead derived from the upload form. For example, to add a file named Thomas.txt to a directory named Employees in the root of the uers Copy directory, you would do POST files/Employees with a content type of multipart/form-data and the filename in the Content Disposition area of the upload body (see example below). It may sound complex, but it's a simple standardized HTTP file upload.

The maximum filesize of an upload is 1GB and the maximum time an HTTP request can take is 5 Hours. An API endpoint supporting chunked file uploading is planned for circumventing this limitation.

The overwrite parameter is optional and defaults to true, which will cause a new file to overwrite any existing file at the same location. If it is set to false, the filename will have some text appended automatically to prevent an overwrite. For example, Docs/TPS_Report.pdf would be renamed Docs/TPS_Report (1).pdf. If an item exists with the same number in parenthesis, the next available number will be chosen. The resulting filename will be returned in the response. This doesn't apply to creating a new folder with the same name as an existing folder, the existing folder will be returned.

Example Response (201 Created) [Creating a Folder]
{
  "id": "/copy/test/test",
  "path": "/test/test",
  "name": "test",
  "token": null,
  "permissions": null,
  "syncing": false,
  "public": false,
  "type": "dir",
  "size": null,
  "date_last_synced": 1366986969,
  "stub": false,
  "recipient_confirmed": false,
  "counts": [
  ],
  "url": "https://copy.com/web/test/test",
  "links": [
  ],
  "thumb": null,
  "share": null
}
Example Request [Creating a File]
Content-Type:multipart/form-data; boundary=----BOUNDARYRn6JpCAjA8RWn

------BOUNDARYRn6JpCAjA8RWn
Content-Disposition: form-data; name="file"; filename="animation.gif"
Content-Type: image/gif

RAW-BINARY-FILE-CONTENT
------BOUNDARYRn6JpCAjA8RWn--
Example Response (201 Created) [Creating a File]
{
  "objects": [
    {
      "id": "/copy/test/test/animation.gif",
      "path": "/test/test/animation.gif",
      "name": "animation.gif",
      "token": null,
      "permissions": null,
      "syncing": false,
      "public": false,
      "type": "file",
      "size": 456357,
      "date_last_synced": 1366987166,
      "stub": false,
      "recipient_confirmed": false,
      "counts": [
      ],
      "url": "https://copy.com/web/test/test/animation.gif",
      "links": [
      ],
      "revision": 5293,
      "thumb": null,
      "share": null
    }
  ]
}

Download Image File Thumbnail

GET https://api.copy.com/rest/thumbs/PATH/TO/FILE?size=SIZE
Permissions
  • filesystem.read: Required

This request will return a thumbnail image of the image at the specified path. The returned image will be at MOST the provided size. For example, if you have an image which is 200x320 pixels, and you load the image with the size paramater set to 32, the returned image will be 20x32 pixels.

If you plan on serving these images up to your client in a browser, you will need to create a proxy, since you need to pass along the OAuth paramaters with this request. Attempting to load the image from the browser directly will cause an error.

The response will be a binary image file. The content type will be either image/jpeg for JPEG images, or image/png for all other types of image formats.

Valid Sizes

If a size attribute different than the ones listed here is provided, a thumbnail of size 32 will be returned.

  • 32
  • 64
  • 128
  • 256
  • 512
  • 1024

Link API Calls

Links represent files and directories which have been shared by a user. These links can be either public or private. When they are private, there are potentially multiple recipients who will have access to the link, as decided by the user. These API calls will allow you to read information about said links, as well as creating links on behalf of your user.

A link ID, which we also refer to as a token, is a random collection of uppercase and lowercase letters as well as numbers, e.g., hJjXGcSfC20G. This Token is used in URLs when sharing links between people online. For example, the above token can be seen by going to the URL https://copy.com/hJjXGcSfC20G.

Link Access Permissions
  • read: This user only has access to read the information in the share
  • sync: This user can syncronize the files into their Copy folder, as well as edit the files

Get a Specific Link

GET https://api.copy.com/rest/links/TOKEN
Permissions
  • links.read: Required

This call will retreive information about the identified link.

Example Response (200 OK)
{
  "id": "wFIK8aMIDvh2",
  "name": "Such a cool Link",
  "public": false,
  "url": "https://copy.com/wFIK8aMIDvh2",
  "url_short": "https://copy.com/wFIK8aMIDvh2",
  "creator_id": "110660",
  "created_time": 1366825349,
  "object_count": 1,
  "confirmation_required": false,
  "status": "viewed",
  "permissions": "sync",
  "recipients": [
    {
      "contact_type": "user",
      "contact_id": "user-1381231",
      "contact_source": "link-3514165",
      "user_id": "1381231",
      "email": "thomashunter@example.com",
      "first_name": "Thomas",
      "last_name": "Hunter",
      "permissions": "sync",
      "emails": [
        {
          "primary": true,
          "confirmed": true,
          "email": "thomashunter@example.com",
          "gravatar": "eca957c6552e783627a0ced1035e1888"
        },
        {
          "primary": false,
          "confirmed": true,
          "email": "thomashunter@example.net",
          "gravatar": "c0e344ddcbabb383f94b1bd3486e55ba"
        }
      ]
    }
  ]
}
Example Response (404 Not Found)
{
  "error": 1021,
  "message": "Cannot find link InVaL1Dt0k3n"
}

List All Links

GET https://api.copy.com/rest/links
Permissions
  • links.read: Required

This call will return a comprehensive listing of every link created by the user. This call can be slow, so it is recommended that you cache the results. Note that the recipients array will always be empty (otherwise the request would be even slower). The name attribute is used when the user chooses to share the link with people via email address, and they enter a subject line for the email.

Example Response (200 OK)
[
  {
    "id": "sCxkowlraze1",
    "name": "Big API Changes",
    "public": true,
    "url": "https://copy.com/sCxkowlraze1",
    "url_short": "https://copy.com/sCxkowlraze1",
    "creator_id": "1381231",
    "created_time": 1366825349,
    "object_count": 1,
    "confirmation_required": false,
    "status": "viewed",
    "permissions": "read",
    "recipients": [
    ]
  },
  {
    "id": "f6e4fNxrtwY8",
    "name": "to",
    "public": true,
    "url": "https://copy.com/f6e4fNxrtwY8",
    "url_short": "https://copy.com/f6e4fNxrtwY8",
    "creator_id": "1381231",
    "created_time": 1366817886,
    "object_count": 1,
    "confirmation_required": false,
    "status": "viewed",
    "permissions": "read",
    "recipients": [
    ]
  },
  {
    "id": "MBzss3r2GXk4",
    "name": "",
    "public": true,
    "url": "https://copy.com/MBzss3r2GXk4",
    "url_short": "https://copy.com/MBzss3r2GXk4",
    "creator_id": "1381231",
    "created_time": 1366817750,
    "object_count": 1,
    "confirmation_required": false,
    "status": "viewed",
    "permissions": "read",
    "recipients": [
    ]
  }
]

Create a New Link

POST https://api.copy.com/rest/links
Permissions
  • links.write: Required

The paths attribute is required, and is an array of paths to be included in a link. You can have multiple files and directories specified when you create a link, the only rule is that they need to all be siblings in the filesystem (e.g., all exist within the same directory). This starts beneath the /copy entry point in the filesystem.

NOTE: Any provided path attributes which can't be discovered in the users filesystem will still be "added" to the share, however, they will appear as being transparent in the user interface and un-clickable. If a file is created after the link was created at the path specified, it will automatically get attached to the link. If any of the files are deleted, they will disappear from the link.

The public attribute specifies whether or not the generated link can be viewed by anybody with the URL. It is optional and defaults to true.

The response for this request will be a complete document, as if you had made the GET request above. You'll probably want to parse this response to get the new token ID / URL for the link.

NOTE: Recipients cannot be set during a POST, they need to be set later during a PUT.

Example Request
{
  "public": true,
  "name": "My Cool Shared Files",
  "paths": [
    "/copy/path/to/file.txt"
  ]
}
Example Response (200 OK)

NOTE: Currently the status is 200 OK although it should be 201 Created. This will likely change in the future, so check for a 2XX for success.

{
  "id": "MBrss3roGDk4",
  "name": "My Cool Shared Files",
  "public": true,
  "url": "https://copy.com/MBrss3roGDk4",
  "url_short": "https://copy.com/MBrss3roGDk4",
  "creator_id": "1381231",
  "company_id": null,
  "confirmation_required": false,
  "status": "viewed",
  "permissions": "read"
}
Example Response (400 Bad Request)
{
  "error": 1303,
  "message": "No path(s) specified."
}

Update Basic Link Data

PUT https://api.copy.com/rest/links/TOKEN
Permissions
  • links.write: Required

Performing a PUT against a link will allow you to update some of the attributes, as seen below:

Example Request
{
  "public": true,
  "name": "New Name",
  "paths": [
    "/copy/bad-binding-of-isaac.png"
  ]
}

Update Link Recipient Data

PATCH https://api.copy.com/rest/links/TOKEN
Permissions
  • links.write: Required

When performing a PATCH operation on a link, having the recipient attribute set as an array with a recipient object within the array will append that user to the list of recipients.

To remove contacts set a remove attribute to true within each individual recipient object.

NOTE: Eventually the functionality of the PUT and PATCH request for this endpoint will be combined. Currently it is a bit confusing and not very RESTful.

Example Request
{
  "recipients": [
    {
      "email": "thomashunter@example.com",
      "permissions": "read"
    },
    {
      "email": "alexkinnee@example.com",
      "remove": true
    }
  ]
}

Delete a Link

DELETE https://api.copy.com/rest/links/TOKEN
Permissions
  • links.write: Required

Use this to delete a link the user owns. A response of 204 No Content signifies a successful delete.

Example Response (400 Bad Request)
{
  "error": 1021,
  "message": "Cannot find link YyvXNbkDgSvI"
}

Get Files attached to Link

GET https://api.copy.com/rest/meta/links/TOKEN
Permissions
  • links.read: Required

This API call will return a listing of files attached to a link. Notice that the URL begins with /meta/. When getting deeper into the links subdirectories, they are appended to the URL, like the rest of the filesystem calls.

Example Response (200 OK) [Token Root]
{
  "id": "/links/e6aauE2WodKj",
  "path": "/e6aauE2WodKj",
  "name": "Artwork",
  "token": "e6aauE2WodKj",
  "creator_id": "1381231",
  "permissions": "read",
  "syncing": false,
  "public": true,
  "type": "link",
  "size": null,
  "date_last_synced": null,
  "stub": false,
  "recipient_confirmed": false,
  "counts": [
  ],
  "children_count": null,
  "mime_type": "",
  "url": "http://copy.local/e6aauE2WodKj",
  "links": [
  ],
  "thumb": null,
  "share": null,
  "children": [
    {
      "id": "/links/e6aauE2WodKj/Artwork",
      "path": "/e6aauE2WodKj/Artwork",
      "name": "Artwork",
      "link_name": null,
      "token": null,
      "creator_id": null,
      "permissions": null,
      "public": false,
      "type": "dir",
      "size": null,
      "date_last_synced": null,
      "stub": true,
      "recipient_confirmed": false,
      "object_available": true,
      "counts": [
      ],
      "mime_type": "",
      "list_index": 0,
      "url": "http://copy.local/e6aauE2WodKj/Artwork",
      "revision": 0,
      "thumb": null,
      "links": [
      ]
    }
  ]
}
Example Response (200 OK) [Token Subdirectory]
{
  "id": "/links/e6aauE2WodKj/Artwork",
  "path": "/e6aauE2WodKj/Artwork",
  "name": "Artwork",
  "token": null,
  "creator_id": null,
  "permissions": null,
  "syncing": false,
  "public": false,
  "type": "dir",
  "size": null,
  "date_last_synced": null,
  "stub": false,
  "recipient_confirmed": false,
  "counts": [
  ],
  "children_count": null,
  "mime_type": "",
  "url": "http://copy.local/e6aauE2WodKj/Artwork",
  "links": [
  ],
  "thumb": null,
  "share": null,
  "children": [
    {
      "id": "/links/e6aauE2WodKj/Artwork/blue-eyes.jpg",
      "path": "/e6aauE2WodKj/Artwork/blue-eyes.jpg",
      "name": "blue-eyes.jpg",
      "link_name": null,
      "token": null,
      "creator_id": null,
      "permissions": null,
      "public": false,
      "type": "file",
      "size": 253839,
      "date_last_synced": null,
      "stub": true,
      "recipient_confirmed": false,
      "object_available": true,
      "counts": [
      ],
      "mime_type": "image/jpeg",
      "list_index": 0,
      "url": "http://copy.local/e6aauE2WodKj/Artwork/blue-eyes.jpg",
      "revision": 1160,
      "thumb": "http://copy.local/thumbs_public/e6aauE2WodKj/Artwork/blue-eyes.jpg",
      "thumb_original_dimensions": {
        "width": 1262,
        "height": 1365
      },
      "links": [
      ]
    },
    {
      "id": "/links/e6aauE2WodKj/Artwork/emma-stone.png",
      "path": "/e6aauE2WodKj/Artwork/emma-stone.png",
      "name": "emma-stone.png",
      "link_name": null,
      "token": null,
      "creator_id": null,
      "permissions": null,
      "public": false,
      "type": "file",
      "size": 228618,
      "date_last_synced": null,
      "stub": true,
      "recipient_confirmed": false,
      "object_available": true,
      "counts": [
      ],
      "mime_type": "image/png",
      "list_index": 1,
      "url": "http://copy.local/e6aauE2WodKj/Artwork/emma-stone.png",
      "revision": 1161,
      "thumb": "http://copy.local/thumbs_public/e6aauE2WodKj/Artwork/emma-stone.png",
      "thumb_original_dimensions": {
        "width": 1100,
        "height": 1164
      },
      "links": [
      ]
    }
  ]
}

Download Public Share Thumbnail

GET https://copy.com/thumbs_public/TOKEN/PATH/TO/IMAGE.JPG?revision=RID&size=SIZE

If a share is public, the thumbnail image can be downloaded anonymously from any user agent, without the need to make an API request or even be an authenticated Copy user.

Valid Sizes

If a size attribute different than the ones listed below is provided, the API will attempt to return the next smallest thumbnail from the size specified, or return an empty response.

  • 32
  • 64
  • 128
  • 256
  • 512
  • 1024