Skip to content

Table of contents

Shared drives

A shared drive is a folder that is shared between several cozy instances. A member doesn’t have the files in their Cozy, but can access them via the stack playing a proxy role.

Creating a shared drive

There are two ways to create a shared drive:

Simple method: Convert an existing folder

Use the POST /sharings/drives endpoint to convert any existing folder into a shared drive. This is the recommended approach as it handles all validation and setup automatically.

Manual method

To create a shared drive manually (typically on the organization Cozy), follow these steps:

  1. Ensure that the /Drives folder exists in the cozy instance with the POST /files/shared-drives route.
  2. Create a folder inside it, with the name of shared drive.
  3. Create a sharing with the drive: true attribute, and one rule for shared folder (with none for add, update and remove attributes).

Managing shared drives

GET /sharings/drives

The GET /sharings/drives route returns the list of shared drives.

Request

GET /sharings/drives HTTP/1.1
Host: acme.example.net
Accept: application/vnd.api+json

Response

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
  "data": [
    {
      "type": "io.cozy.sharings",
      "id": "aae62886e79611ef8381fb83ff72e425",
      "attributes": {
        "drive": true,
        "owner": true,
        "description": "Drive for the product team",
        "app_slug": "drive",
        "created_at": "2025-02-10T11:08:08Z",
        "updated_at": "2025-02-10T12:10:43Z",
        "members": [
          {
            "status": "owner",
            "public_name": "ACME",
            "email": "admin@acme.example.net",
            "instance": "acme.example.net"
          },
          {
            "status": "pending",
            "name": "Alice",
            "email": "alice@example.net"
          },
          {
            "status": "pending",
            "name": "Bob",
            "email": "bob@example.net"
          }
        ],
        "rules": [
          {
            "title": "Product team",
            "doctype": "io.cozy.files",
            "values": [
              "357665ec-e797-11ef-94fb-f3d08ccb3ff5"
            ],
            "add": "none",
            "update": "none",
            "remove": "none"
          }
        ]
      },
      "meta": {
        "rev": "1-272ba74b868f"
      },
      "links": {
        "self": "/sharings/aae62886e79611ef8381fb83ff72e425"
      }
    }
  ]
}

POST /sharings/drives

Creates a new shared drive from an existing folder. This is an alternative to the manual process of creating a sharing with drive: true - it automatically validates the folder and creates the sharing with appropriate rules.

The folder must:

  • Exist and be a directory (not a file)
  • Not be a system folder (root, trash, shared-with-me, shared-drives, no-longer-shared)
  • Not be inside the trash
  • Not already have a sharing (directly or via a parent folder)
  • Not contain any subfolder that already has a sharing

Request

POST /sharings/drives HTTP/1.1
Host: acme.example.net
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
{
  "data": {
    "type": "io.cozy.sharings",
    "attributes": {
      "description": "Project Documents",
      "folder_id": "357665ec-e797-11ef-94fb-f3d08ccb3ff5"
    },
    "relationships": {
      "recipients": {
        "data": [
          {"id": "contact-id-1", "type": "io.cozy.contacts"},
          {"id": "group-id-1", "type": "io.cozy.contacts.groups"}
        ]
      },
      "read_only_recipients": {
        "data": [
          {"id": "contact-id-2", "type": "io.cozy.contacts"}
        ]
      }
    }
  }
}

Attributes:

Attribute Required Description
folder_id Yes The ID of the existing folder to convert into a shared drive
description No A description for the shared drive. If not provided, defaults to the folder name

Relationships:

Relationship Description
recipients Contacts or groups to add as read-write members
read_only_recipients Contacts or groups to add as read-only members

Both relationships are optional - you can create a shared drive without any recipients and add them later.

Response (Success)

HTTP/1.1 201 Created
Content-Type: application/vnd.api+json
{
  "data": {
    "type": "io.cozy.sharings",
    "id": "aae62886e79611ef8381fb83ff72e425",
    "attributes": {
      "drive": true,
      "owner": true,
      "description": "Project Documents",
      "app_slug": "drive",
      "created_at": "2025-02-10T11:08:08Z",
      "updated_at": "2025-02-10T11:08:08Z",
      "members": [
        {
          "status": "owner",
          "public_name": "ACME",
          "email": "admin@acme.example.net",
          "instance": "acme.example.net"
        },
        {
          "status": "pending",
          "name": "Alice",
          "email": "alice@example.net"
        }
      ],
      "rules": [
        {
          "title": "Project Documents",
          "doctype": "io.cozy.files",
          "values": [
            "357665ec-e797-11ef-94fb-f3d08ccb3ff5"
          ],
          "add": "none",
          "update": "none",
          "remove": "none"
        }
      ]
    },
    "meta": {
      "rev": "1-272ba74b868f"
    },
    "links": {
      "self": "/sharings/aae62886e79611ef8381fb83ff72e425"
    }
  }
}

Error Responses

Status Error Description
400 Bad Request Invalid JSON body
403 Forbidden Insufficient permissions to create a sharing
404 Not Found The folder with the given folder_id does not exist
409 Conflict The folder already has a sharing, is inside a shared folder, or contains a shared subfolder
422 Unprocessable Entity Missing folder_id, folder is a file, or folder is a system folder

Example error (folder already shared):

HTTP/1.1 409 Conflict
Content-Type: application/vnd.api+json
{
  "errors": [
    {
      "status": "409",
      "title": "Conflict",
      "detail": "Folder already has an existing sharing"
    }
  ]
}

Files and directories

Unless stated otherwise, a permission on the whole io.cozy.files doctype is required to use the following routes.

GET /sharings/drives/:id/download/:file-id

Download a file via a drive share.

Identical call to GET /files/download/:file-id but over a shared drive. See there for request and response examples

GET /sharings/drives/:id/_changes

Get the change feed for a drive.

Identical call to GET /files/_changes but over a shared drive. See there for request and response examples, differences are the URL and:

  • Any item that changed for that owner but isn’t under that shared drive is presented as a deletion.
  • Paths are truncated to the shared drive, and formatted accordingly:

eg: //io.cozy.files.shared-drives-dir/1/ba3b516812f636fc022f3968f991357a/Meetings/Checklist.txt

Schema and it’s version, followed by the shared drive ID, and the path within

GET /sharings/drives/:id/:file-id

Get a directory or a file informations inside a shared drive. In the case of a directory, it contains the list of files and sub-directories inside it. For a note, its images are included.

Request

GET /sharings/drives/aae62886e79611ef8381fb83ff72e425/af1e1b66e92111ef8ddd5fbac4938703 HTTP/1.1
Accept: application/vnd.api+json

Response

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
  "data": {
    "type": "io.cozy.files",
    "id": "af1e1b66e92111ef8ddd5fbac4938703",
    "meta": {
      "rev": "1-e36ab092"
    },
    "attributes": {
      "type": "directory",
      "name": "Streams",
      "path": "/Product team/Streams",
      "created_at": "2016-09-19T12:35:00Z",
      "updated_at": "2016-09-19T12:35:00Z",
      "tags": [],
      "cozyMetadata": {
        "doctypeVersion": "1",
        "metadataVersion": 1,
        "createdAt": "2016-09-20T18:32:47Z",
        "createdByApp": "drive",
        "createdOn": "https://cozy.example.com/",
        "updatedAt": "2016-09-20T18:32:47Z"
      },
      "driveId": "aae62886e79611ef8381fb83ff72e425"
    },
    "relationships": {
      "contents": {
        "data": [
          {
            "type": "io.cozy.files",
            "id": "a2843318f52411ef8e7ab79eae2f09ab"
          },
          {
            "type": "io.cozy.files",
            "id": "b1db1642f52411efbe0b3bfc5fc0b437"
          }
        ]
      }
    },
    "links": {
      "self": "/files/af1e1b66e92111ef8ddd5fbac4938703"
    }
  },
  "included": [
    {
      "type": "io.cozy.files",
      "id": "a2843318f52411ef8e7ab79eae2f09ab",
      "meta": {
        "rev": "1-ff3beeb456eb"
      },
      "attributes": {
        "type": "directory",
        "name": "Authentication",
        "path": "/Product team/Streams/Authentication",
        "created_at": "2016-09-19T12:35:08Z",
        "updated_at": "2016-09-19T12:35:08Z",
        "cozyMetadata": {
          "doctypeVersion": "1",
          "metadataVersion": 1,
          "createdAt": "2016-09-20T18:32:47Z",
          "createdByApp": "drive",
          "createdOn": "https://cozy.example.com/",
          "updatedAt": "2016-09-20T18:32:47Z"
        }
      }
    },
    {
      "type": "io.cozy.files",
      "id": "b1db1642f52411efbe0b3bfc5fc0b437",
      "meta": {
        "rev": "1-0e6d5b72"
      },
      "attributes": {
        "type": "file",
        "name": "REAMDE.md",
        "trashed": false,
        "md5sum": "ODZmYjI2OWQxOTBkMmM4NQo=",
        "created_at": "2016-09-19T12:38:04Z",
        "updated_at": "2016-09-19T12:38:04Z",
        "tags": [],
        "size": 12,
        "executable": false,
        "class": "document",
        "mime": "text/plain",
        "cozyMetadata": {
          "doctypeVersion": "1",
          "metadataVersion": 1,
          "createdAt": "2016-09-20T18:32:49Z",
          "createdByApp": "drive",
          "createdOn": "https://cozy.example.com/",
          "updatedAt": "2016-09-20T18:32:49Z",
          "uploadedAt": "2016-09-20T18:32:49Z",
          "uploadedOn": "https://cozy.example.com/",
          "uploadedBy": {
            "slug": "drive"
          }
        }
      }
    }
  ]
}

GET /sharings/drives/:id/:file-id/size

This endpoint returns the size taken by the files in a directory inside a shared drive, including those in subdirectories.

Request

GET /sharings/drives/aae62886e79611ef8381fb83ff72e425/af1e1b66e92111ef8ddd5fbac4938703/size HTTP/1.1
Accept: application/vnd.api+json

Response

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
  "data": {
    "type": "io.cozy.files.sizes",
    "id": "af1e1b66e92111ef8ddd5fbac4938703",
    "attributes": {
      "size": "1234567890"
    },
    "meta": {}
  }
}

POST /sharings/drives/:id/:file-id/copy

Duplicates a file.

Identical call to POST /files/:file-id/copy but over a shared drive. See there for request and response examples, the only difference is the URL.

POST /sharings/drives/move

Move or copy a file or a directory between locations (personal drive and/or shared drives).

Supported flows:

  • From a shared drive to another shared drive (same stack or cross-stack)
  • From a shared drive to a personal drive
  • From a personal drive to a shared drive

At least one side (source or destination) must be a shared drive.

The copy parameter controls whether the operation is a move (default) or copy: - When copy: false (default): The source file/directory is moved to the destination and deleted from the source - When copy: true: The source file/directory is copied to the destination but remains in the source location

Request

Body (preferred):

{
  "source": {
    "instance": "https://alice.localhost:8080", // omit for personal drive
    "sharing_id": "share_src_id",               // required when instance is set
    "file_id": "file123",                       // exactly one of file_id or dir_id
    "dir_id": ""
  },
  "dest": {
    "instance": "https://bob.localhost:8080",   // omit for personal drive
    "sharing_id": "share_dst_id",               // required when instance is set
    "dir_id": "destDir456"                      // destination directory id
  },
  "copy": false                                 // optional, defaults to false
}

Validation rules:

  • Exactly one of source.file_id or source.dir_id must be provided.
  • dest.dir_id is required.
  • If source.instance is provided, source.sharing_id is required.
  • If dest.instance is provided, dest.sharing_id is required.
  • At least one of source.sharing_id or dest.sharing_id must be provided.
  • When one side is a personal drive (no instance), whole-type permission on io.cozy.files is required.
  • copy is optional and defaults to false (move operation).

Notes on behavior:

  • Name conflicts at destination are resolved automatically by appending a conflict suffix.
  • For directory operations, the subtree is recreated top-down and files are copied, then sources are deleted (only for move operations).
  • Cross-stack operations perform a remote download/upload and delete the remote source upon success (only for move operations).
  • When copy: true, source files and directories are preserved in their original location.
  • When copy: false (default), source files and directories are deleted after successful copy to destination.

Responses

  • 201 Created, with a JSON object describing the created file or directory on the destination.
  • 400 Bad Request, when required inputs are missing or invalid.
  • 403 Forbidden, on permission errors or missing credentials for a shared drive.
  • 404 Not Found, when referenced files or drives do not exist.

Example response (file, abbreviated):

{
  "data": {
    "type": "io.cozy.files",
    "id": "new-file-id",
    "attributes": {
      "name": "example.txt",
      "dir_id": "destDir456",
      "type": "file",
      "size": 123,
      "mime": "text/plain",
      "class": "document",
      "executable": false,
      "tags": [],
      "driveId": "aae62886e7..." // present when destination is a shared drive
    }
  }
}

When the move occurs locally (same stack), the response matches the standard local creation response. For cross-stack moves, the response is built from the remote upload result and includes equivalent attributes.

Copy Example

To copy a file instead of moving it, set the copy parameter to true:

{
  "source": {
    "instance": "https://alice.localhost:8080",
    "sharing_id": "share_src_id",
    "file_id": "file123"
  },
  "dest": {
    "instance": "https://bob.localhost:8080",
    "sharing_id": "share_dst_id",
    "dir_id": "destDir456"
  },
  "copy": true
}

This will create a copy of the file in the destination while preserving the original file in the source location.

PATCH /sharings/drives/:id/:file-id

This endpoint can be used to update the metadata of a file or directory, to rename it or to move it within the same shared drive.

Some specific attributes of the patch can be used:

  • dir_id attribute can be updated to move a file or directory (the new directory needs to be in the same shared drive as the old one)

HTTP headers

It’s possible to send the If-Match header, with the previous revision of the file/directory (optional).

Request

PATCH /sharings/drives/aae62886e79611ef8381fb83ff72e425/9152d568-7e7c-11e6-a377-37cbfb190b4b HTTP/1.1
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
  "data": {
    "type": "io.cozy.files",
    "id": "9152d568-7e7c-11e6-a377-37cbfb190b4b",
    "attributes": {
      "type": "file",
      "name": "hi.txt",
      "dir_id": "f2f36fec-8018-11e6-abd8-8b3814d9a465",
      "tags": ["poem"]
    }
  }
}

Status codes

  • 200 OK, when the file or directory metadata has been successfully updated
  • 400 Bad Request, when a the destination directory does not exist
  • 403 Forbidden, when the file or directory cannot be modified or the
  • destination directory is not accessible
  • 404 Not Found, when the file/directory does not exist
  • 412 Precondition Failed, when the If-Match header is set and doesn’t match the last revision of the file/directory
  • 422 Unprocessable Entity, when the sent data is invalid (for example, the parent doesn’t exist)

Response

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
Location: https://cozy.example.com/sharings/drives/aae62886e79611ef8381fb83ff72e425/9152d568-7e7c-11e6-a377-37cbfb190b4b
{
  "data": {
    "type": "io.cozy.files",
    "id": "9152d568-7e7c-11e6-a377-37cbfb190b4b",
    "meta": {
      "rev": "1-0e6d5b72"
    },
    "attributes": {
      "type": "file",
      "name": "hi.txt",
      "trashed": false,
      "md5sum": "ODZmYjI2OWQxOTBkMmM4NQo=",
      "created_at": "2016-09-19T12:38:04Z",
      "updated_at": "2016-09-19T12:38:04Z",
      "tags": ["poem"],
      "size": 12,
      "executable": false,
      "class": "document",
      "mime": "text/plain",
      "cozyMetadata": {
        "doctypeVersion": "1",
        "metadataVersion": 1,
        "createdAt": "2016-09-20T18:32:49Z",
        "createdByApp": "drive",
        "createdOn": "https://cozy.example.com/",
        "updatedAt": "2016-09-22T13:32:51Z",
        "uploadedAt": "2016-09-21T04:27:50Z",
        "uploadedOn": "https://cozy.example.com/",
        "uploadedBy": {
          "slug": "drive"
        }
      }
    },
    "relationships": {
      "parent": {
        "links": {
          "related": "/files/f2f36fec-8018-11e6-abd8-8b3814d9a465"
        },
        "data": {
          "type": "io.cozy.files",
          "id": "f2f36fec-8018-11e6-abd8-8b3814d9a465"
        }
      }
    },
    "links": {
      "self": "/files/9152d568-7e7c-11e6-a377-37cbfb190b4b"
    }
  }
}

Similar to /files

The following routes are similar to /files, but for a shared drive.

GET /sharings/drives/:id/metadata

POST /sharings/drives/upload/metadata

DELETE /sharings/drives/:file-id

POST /sharings/drives/trash/:file-id

DELETE /sharings/drives/trash/:file-id

The following routes manage share-by-link permissions scoped to files inside a shared drive:

  • GET /sharings/drives/:id/permissions?ids=...
  • POST /sharings/drives/:id/permissions
  • PATCH /sharings/drives/:id/permissions/:perm-id
  • DELETE /sharings/drives/:id/permissions/:perm-id

GET /sharings/drives/:id/permissions?ids=…

Lists the share-by-link permissions for the requested file or folder IDs inside the shared drive.

Authorization rules:

  • The shared-drive owner can list all matching links.
  • A write-capable recipient can list writable and read-only links.
  • A read-only recipient only sees read-only links.

Validation:

  • ids is required and must be a comma-separated list of file or folder IDs.
  • Every requested ID must belong to the shared drive.

Status codes:

  • 200 OK listed
  • 403 Forbidden caller cannot access shared-drive permissions
  • 422 Unprocessable Entity missing or invalid ids

POST /sharings/drives/:id/permissions

Creates a share-by-link permission for one file or folder in the shared drive. The request body uses the same JSON:API shape as POST /permissions.

Authorization rules:

  • The shared-drive owner can create a link.
  • A write-capable recipient can create a link.
  • A read-only recipient cannot create a link.

Validation:

  • The permission set must target exactly one file or folder.
  • The target type must be io.cozy.files.
  • Selectors are not supported.
  • The target must belong to the shared drive and must be readable by the caller.
  • Only one share-by-link permission can exist per target. A second creation attempt on the same target returns a conflict, regardless of which member created the existing link.

Status codes:

  • 200 OK created
  • 400 Bad Request invalid permission set or invalid target
  • 403 Forbidden caller lacks access to the target or is read-only on the shared drive
  • 409 Conflict a share-by-link permission already exists for this target

PATCH /sharings/drives/:id/permissions/:perm-id

Updates an existing share-by-link permission.

Authorization rules:

  • The shared-drive owner can patch any share-by-link permission.
  • The creator of a share-by-link permission can patch the permission they created.
  • Creator resolution works for same-stack and cross-stack recipients.
  • Public share tokens (share, share-preview) cannot patch permissions.

Allowed updates:

  • password
  • expires_at
  • permissions (same target only)

Validation:

  • password must be a string (empty string clears the password).
  • expires_at must be a string (empty string clears expiration, otherwise RFC3339 date-time).
  • permissions, when provided, must still target the same file or folder inside the shared drive.
  • A write-capable creator or the owner can promote a read-only link to a writable link if their current token grants those verbs.
  • A read-only shared-drive recipient cannot patch a permission set to add writable verbs.

Status codes:

  • 200 OK updated
  • 400 Bad Request invalid payload (for example trying to update permissions or codes), invalid password / expires_at attribute format
  • 403 Forbidden caller is not owner/creator, or caller identity cannot be verified for a shared-drive token
  • 404 Not Found permission ID does not exist

DELETE /sharings/drives/:id/permissions/:perm-id

Revokes an existing share-by-link permission.

Authorization rules:

  • The shared-drive owner can revoke any share-by-link permission.
  • The creator of a share-by-link permission can revoke the permission they created.
  • A read-only shared-drive recipient cannot revoke a permission.
  • Public share tokens (share, share-preview) cannot revoke permissions.

Status codes:

  • 204 No Content revoked
  • 400 Bad Request permission is not a share-by-link permission
  • 403 Forbidden caller is not owner/creator, or permission does not belong to this shared drive
  • 404 Not Found permission ID does not exist

Delegated email sharing

Members of a shared drive add new recipients through the sharing API, not through a drive-specific route:

  • POST /sharings/:sharing-id/recipients

When that request is sent from a recipient Cozy, the stack delegates the operation to the owner Cozy internally.

Authorization rules:

  • The shared-drive owner can add recipients as for any other sharing.
  • A write-capable recipient can invite read-write or read-only recipients.
  • A read-only recipient can invite only read-only recipients.
  • A read-only recipient receives 403 Forbidden for a read-write invite.

Versions

The identifier of the io.cozy.files.versions is composed of the file-id and another string called the version-id, separated by a /. So, when a route makes reference to /something/:file-id/:version-id, you can use the identifier of the version document (without having to prepend the file identifier).

GET /sharings/drives/:id/download/:file-id/:version-id

Downloads an old version of the file content.

Identical call to GET /files/download/:file-id/:version-id but over a shared drive. See there for request and response examples, the only difference is the URL.

Notes

POST /sharings/drives/:id/notes

Create a note inside a shared drive. Identical to POST /notes.

GET /sharings/drives/:id/notes/:file-id/open

Return the parameters to build the URL where the note can be opened. Identical to GET /notes/:file-id/open.

Office

GET /sharings/drives/:id/office/:file-id/open

Returns the parameters to open an office document. Identical to GET /office/:file-id/open.

Realtime

GET /sharings/drives/:id/realtime

Get the changes inside a shared drive in real-time from a websocket. Identical to GET /realtime, except subscribing to the shared drive is automatically done.

client > {"method": "AUTH",
          "payload": "xxAppOrAuthTokenxx="}
server > {"event": "UPDATED",
          "payload": {"id": "idB", "rev": "6-457...", "type": "io.cozy.files", "doc": {embeded doc ...}}}