Dazr Files API: developer docs

Quick start

  1. Register your app and enable the Dazr Files API. Register your app, or use one you already have, in the Dazr Identity console: the same place as for Sign in with Dazr Identity and the Dazr Sign API, with the same client ID and secret. On your app’s page, choose Enable Dazr Files API. For unattended backups the app must be a web server app (with a client secret).
  2. Get approved. Give the organisation your client ID. An owner or admin opens Dazr Identity → Organisations → the organisation → Connected apps for Files, enters the client ID and chooses what your app may read: all shared files, or chosen folders with everything in them. They can also limit your app to listed networks (IP ranges).
  3. Get a token. Your server asks for a token with grant_type=client_credentials, the organisation id as org, and your client ID and secret (HTTP Basic or the form body). Only apps with a client secret can do this.
  4. Call the API. Send the access token as Authorization: Bearer. It lasts 10 minutes and there is no refresh token: ask for a new one when it runs out.
curl -u "$DAZR_CLIENT_ID:$DAZR_CLIENT_SECRET" https://identity.dazr.eu/oauth/token \
  -d grant_type=client_credentials -d scope=files.read -d org=org_3k9x2m0q8w1v5t7a

{ "access_token": "eyJhbGciOiJFUzI1NiIs...", "token_type": "Bearer", "expires_in": 600, "scope": "files.read" }

curl -H "Authorization: Bearer $TOKEN" https://suite.dazr.eu/api/files/v1/spaces

Organisation access for backups

There are two ways in. An organisation’s owner or admin approves your app for the organisation’s shared files, and your server then gets tokens without a person (best for unattended backups). Or a person allows your app with the files.read scope, and your app reads that person’s own files.

An organisation that did not approve your app, one that revoked it and one that does not exist all answer invalid_grant. The admins see when your app was last used and every file it downloaded, and can change its folders or revoke it at any time. A revoked approval stops on the next request: tokens already issued stop working at once, and so do download links already handed out. Members’ own files are never part of an organisation’s approval.

ParameterMeaning
grant_typeclient_credentials
orgThe organisation id, shown to its admins under Connected apps for Files.
scopefiles.read (the default and the only scope).
Client authenticationYour client ID and current client secret, with HTTP Basic (client_secret_basic) or in the form body (client_secret_post). A previous secret kept after a transfer is not accepted.

A person’s own files

Ask for openid files.read (and offline_access for a refresh token) in the usual authorization code flow with PKCE. The person sees on the consent screen that your app may see and download their files. The token reaches their own files, and an organisation’s shared files only when that organisation also approved your app and the person is a member who may open them, with the same folders. When the person removes your app under Connected apps, its tokens stop.

Reference

Base URL https://suite.dazr.eu/api/files/v1. Every endpoint is GET (and HEAD); any other method answers 405. Errors are JSON: { "error": { "code", "message" } }. Lists take limit (1 to 1000, default 200) and return next_cursor while there is more.

RequestWhat it returns
GET /spacesThe spaces your token reaches: me for a person’s own files, and organisations by their id.
GET /spaces/{space}One space.
GET /spaces/{space}/folders/root/childrenThe folders, then the files, at the top of what your token reaches. Use a folder id instead of root for a folder.
GET /spaces/{space}/folders/{id}One folder.
GET /spaces/{space}/files/{id}One file: name, path, parent_id, size, mime, version, etag, created_at, modified_at and trashed_at.
GET /spaces/{space}/files/{id}/versionsThe kept versions of a file, newest first.
GET /spaces/{space}/files/{id}/contentA redirect (302) to a download link at files.dazr.eu that works for 5 minutes and supports byte ranges. Add version= for an earlier version, or redirect=false to get the link as JSON.
GET /spaces/{space}/trashFiles in the trash, as metadata. They cannot be downloaded until someone restores them.
GET /spaces/{space}/changesWhat changed since a cursor, for incremental backups (below).

Every request needs the files.read scope. Query parameters:

ParameterMeaning
limitItems per page for children, trash and changes: 1 to 1000 (default 200, for changes 500).
cursorThe next_cursor of the previous page, or for changes the cursor it returned.
versionFor content: a version number from versions. Without it, the current version.
redirectFor content: false returns { "url", "expires_in" } instead of a 302.
GET /spaces
{ "spaces": [ { "id": "org_3k9x2m0q8w1v5t7a", "type": "organisation", "name": "Bakkerij Jansen", "folders": "chosen" } ] }

GET /spaces/org_3k9x2m0q8w1v5t7a/folders/root/children?limit=2
{ "items": [
    { "id": "Hk2mQ9xP4aL1", "type": "folder", "name": "Finance", "parent_id": null, "path": "/Finance",
      "created_at": "2026-09-01T08:12:40.000Z" },
    { "id": "p3RtY7uW0zQe", "type": "file", "name": "Budget 2027.xlsx", "parent_id": "Hk2mQ9xP4aL1",
      "path": "/Finance/Budget 2027.xlsx", "size": 48213,
      "mime": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "version": 3, "version_count": 3,
      "etag": "\"p3RtY7uW0zQe.3.48213\"", "sha256": null, "created_at": "2026-09-02T10:00:00.000Z",
      "modified_at": "2026-10-14T16:31:09.000Z", "trashed_at": null } ],
  "next_cursor": "o.2" }

GET /spaces/org_3k9x2m0q8w1v5t7a/files/p3RtY7uW0zQe/versions
{ "versions": [ { "version": 3, "size": 48213, "mime": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                  "created_at": "2026-10-14T16:31:09.000Z", "current": true, "restored_from": null } ] }

GET /spaces/org_3k9x2m0q8w1v5t7a/files/p3RtY7uW0zQe/content
302 Found
Location: https://files.dazr.eu/v1/file?t=g1.…

curl -L -o "Budget 2027.xlsx" -H "Authorization: Bearer $TOKEN" \
  https://suite.dazr.eu/api/files/v1/spaces/org_3k9x2m0q8w1v5t7a/files/p3RtY7uW0zQe/content

Follow the redirect (curl -L): the link at files.dazr.eu needs no Authorization header. A file that is not in the Files gateway yet is streamed directly instead of redirected.

Checksums: Dazr Files does not store a hash of each file’s content, so sha256 is always null. The etag changes whenever the content changes (a new version). Compute your own hash while you download if you need one. Files are stored encrypted (AES-256-GCM, with a key for each space that Dazr holds) and Dazr decrypts them when you download them over TLS. This is not end-to-end encryption.

Incremental backups

Call changes without a cursor once: every folder and file comes back as created, with a cursor. Keep the cursor and send it next time as cursor=. You get what changed since: created, updated (new content), moved (renamed or moved to another folder), trashed, restored and deleted (gone for good, or no longer in the folders your app may read). Each change has the id, item_type and the current item (null when deleted). Folders come before files, parents before their children. While has_more is true, call again with the new cursor. When nothing changed you get the same cursor back. A cursor works for 90 days, for your app and that space only; an expired one answers 410, and you start again without a cursor. Only your app’s 5 newest cursors for a space stay valid.

GET /spaces/org_3k9x2m0q8w1v5t7a/changes?cursor=dc1.ds_…
{ "changes": [
    { "type": "updated", "id": "p3RtY7uW0zQe", "item_type": "file",
      "item": { "id": "p3RtY7uW0zQe", "type": "file", "name": "Budget 2027.xlsx", "version": 4, … } },
    { "type": "deleted", "id": "Zx81LmQa2bNc", "item_type": "file", "item": null } ],
  "cursor": "dc1.ds_…",
  "has_more": false }

Errors and limits

Errors have an HTTP status and a body like { "error": { "code": "not_found", "message": "..." } }. Anything your token does not reach answers 404, the same as something that does not exist.

StatusCodes
400invalid_limit, invalid_cursor (a cursor of another app or space)
401unauthenticated, invalid_token (expired or revoked, the approval or the person’s consent was removed, or the Dazr Files API was turned off for your app)
403insufficient_scope, files_api_disabled, network_not_allowed (the organisation listed networks and this request comes from elsewhere)
404space_not_found, folder_not_found, not_found, version_not_found, unknown_endpoint
405method_not_allowed: any method other than GET, HEAD and OPTIONS
410cursor_expired: start again without a cursor
429rate_limited, with a Retry-After header
503not_available, storage_unavailable: try again later

The token endpoint answers OAuth errors: invalid_client (wrong client ID or secret, or an app without a client secret), unauthorized_client (the Dazr Files API is not enabled for your app), invalid_scope and invalid_grant (no approval by that organisation, or not from its listed networks).

WhatLimit
API requests600 a minute per approval (or per person and app), 6,000 a minute per app, 1,200 a minute per IP address
Downloads300 a minute per approval over the API, 600 a minute over WebDAV
changes120 an hour per space without a page cursor; further pages are not limited separately
WebDAV requests1,200 a minute per approval
/oauth/token600 requests a minute per IP address, and 300 a minute per app

Every download is logged for the organisation’s admins with your app’s name, the file, the time and part of the IP address: the latest 5,000, for up to 180 days. The same file downloaded again within 10 minutes counts once. The organisation’s Files activity shows one line a day for your app. Downloads from a person’s own files show in their Connected apps.

WebDAV

For backup tools that speak WebDAV, an organisation’s admin creates a WebDAV sign-in for your app under Connected apps for Files. The password is shown once, is stored only as a hash and is never anyone’s Dazr password. The sign-in follows the approval: the same folders, networks and limits, and it stops when the approval is revoked.

SettingValue
Serverhttps://files.dazr.eu/dav/
Sign-inHTTP Basic with the username (dzw_…) and password from the admin.
MethodsOPTIONS, PROPFIND with Depth: 0 or 1, GET and HEAD with byte ranges. Depth: infinity is refused. Every write answers 403 or 405.

Folder and file names are shown as they are in Dazr Files. When two items in a folder have the same name (ignoring case), the older one keeps it and the others get their id in brackets before the extension, so every item has its own path. A folder with more than 10,000 items cannot be listed over WebDAV; use the API for it. After 10 failed sign-ins from one IP address within 15 minutes, that address waits 15 minutes.

rclone

  1. Run rclone config, choose n for a new remote, name it dazr and choose the storage type webdav.
  2. URL: https://files.dazr.eu/dav/. Vendor: other. User: the username (dzw_…). Password: choose to type it yourself and paste the password.
  3. Leave the bearer token empty and save. Check it with rclone lsd dazr:, then back up.
# ~/.config/rclone/rclone.conf (rclone obscure <password> gives the pass value)
[dazr]
type = webdav
url = https://files.dazr.eu/dav/
vendor = other
user = dzw_0123456789abcdef0123
pass = <obscured password>

rclone lsd dazr:
rclone sync dazr: /backup/dazr --transfers 4

rclone compares size and modification time. Use copy instead of sync to keep files in your backup that were deleted in Dazr Files.

Synology Cloud Sync

  1. Open Cloud Sync on the NAS, add a cloud provider and choose WebDAV.
  2. Server address: https://files.dazr.eu/dav/. Username and password: the WebDAV sign-in from the admin.
  3. Choose a local folder, the remote folder / (or one folder), and the sync direction Download remote changes only.

Cloud Sync with WebDAV, sync direction “Download remote changes only”. Hyper Backup writes backups to WebDAV but cannot read from it.