API

Packages.

A package is a set of files you send to people who have no account. They open a link. These calls are for the account that sends the package, not for the recipient.

See the API reference for the key and for errors.

Service

GET /api/v1/packages

Packages you sent; an administrator sees every package.

Who. Any user with a key that has the read scope. A key with the admin scope includes the others. The key needs a license that includes the REST API.

Parameters.

NameInTypeMeaning
statusquerystringoptionalactive (still collectable) or history (withdrawn or expired).
scopequerystringoptionalmine, or all for an administrator. An administrator who leaves it out sees everything; anyone else always sees their own.
statequerystringoptionaldraft, uploading, ready, expired or deleted.
limitqueryintegeroptional1 to 500.

Success. 200.

The body is an array.

FieldTypeMeaning
idstringrequiredThe id.
senderIdstringrequiredThe id.
senderEmailstringrequiredAn email address.
subjectstringrequired
statestringrequiredWhere this record is in its life.
statusstringrequiredWhat a person reads: active, expired or revoked. Worked out from the state and the expiry, so an expired package says so without anything having to run at the moment it lapsed.
totalBytesintegerrequired
fileCountintegerrequired
createdAtstringrequiredA time, as RFC 3339.
expiresAtstringoptional, left out when emptyA time, as RFC 3339.
hasPasswordbooleanrequiredWhether a recipient has to type a password, not which one. The hash itself is never read out of the database by this module.
recipientCountintegerrequired
downloadCountintegerrequiredEvery file collected by every recipient, counted once each time.
collectedByintegerrequiredRecipients who have collected anything at all.
rootIdstringrequiredThe id.
rootNamestringrequiredThe storage location's name.
revokedAtstringoptional, left out when emptyA time, as RFC 3339.
revokedBystringoptional, left out when empty
[
  {
    "id": "k7Qm2sLp9vX4aB1c",
    "senderId": "k7Qm2sLp9vX4aB1c",
    "senderEmail": "[email protected]",
    "subject": "Files for Monday",
    "state": "active",
    "status": "example",
    "totalBytes": 1,
    "fileCount": 1,
    "createdAt": "2026-10-05T18:00:00Z",
    "hasPassword": true,
    "recipientCount": 1,
    "downloadCount": 1,
    "collectedBy": 1,
    "rootId": "k7Qm2sLp9vX4aB1c",
    "rootName": "example"
  }
]

Errors. The body always has the shape in Errors. Match on code.

StatuscodeWhen
400bad_requestscope must be mine or all
401unauthenticatedno key, or a key that is unknown, expired, revoked, or owned by a disabled account
403forbiddenthe key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator
402licence_requiredthe license does not include the REST API
{
  "error": {
    "code": "bad_request",
    "message": "scope must be mine or all"
  }
}

POST /api/v1/packages

Send one. Body: {"root", "files": [paths of files or folders], "everything", "recipients", "subject", "message", "expiresAt", "passcode", "notify"}. A folder brings everything inside it.

Who. Any user with a key that has the write scope. A key with the admin scope includes the others. The key needs a license that includes the REST API.

Request body. JSON.

A field the server does not know is refused with 400.

FieldTypeMeaning
rootstringoptionalThe location, for a caller that still names storage directly.
spacestringoptionalThe space the files were chosen from. Paths are then relative to it.
subjectstringoptional
messagestringoptional
filesarray of stringoptional
everythingbooleanoptional
recipientsarray of stringrequired
expiresAtstringoptionalA time, as RFC 3339.
passcodestringoptionalTyped by a person and told to the recipient some other way. Never put in the email that carries the link.
notifybooleanoptionalWhether to email the recipients now. A sender who wants to hand the links over themselves can say no.
{
  "recipients": [
    "example"
  ]
}

Success. 200.

FieldTypeMeaning
packageobjectrequired

It is an object:

FieldTypeMeaning
idstringrequiredThe id.
senderIdstringrequiredThe id.
senderEmailstringrequiredAn email address.
subjectstringrequired
statestringrequiredWhere this record is in its life.
statusstringrequiredWhat a person reads: active, expired or revoked. Worked out from the state and the expiry, so an expired package says so without anything having to run at the moment it lapsed.
totalBytesintegerrequired
fileCountintegerrequired
createdAtstringrequiredA time, as RFC 3339.
expiresAtstringoptionalA time, as RFC 3339.
hasPasswordbooleanrequiredWhether a recipient has to type a password, not which one. The hash itself is never read out of the database by this module.
recipientCountintegerrequired
downloadCountintegerrequiredEvery file collected by every recipient, counted once each time.
collectedByintegerrequiredRecipients who have collected anything at all.
rootIdstringrequiredThe id.
rootNamestringrequiredThe storage location's name.
revokedAtstringoptionalA time, as RFC 3339.
revokedBystringoptional
notifiedarray of stringrequiredAddresses that were emailed their link.
notNotifiedarray of objectsrequiredAddresses that were not, and why. The package exists either way: a relay that is down must not throw away the sender's work.

Each item is an object:

FieldTypeMeaning
emailstringrequiredAn email address.
reasonstringrequired
notifyProblemstringoptional, left out when emptyWhy nobody was emailed, when that applies to all of them at once — email not set up, or no server address to build links from.
linksarray of objectsrequiredEach person's link, for a sender who chose not to email them or whose email did not go. Shown once: only hashes are kept.

Each item is an object:

FieldTypeMeaning
emailstringrequiredAn email address.
linkstringrequired
{
  "package": {
    "id": "k7Qm2sLp9vX4aB1c",
    "senderId": "k7Qm2sLp9vX4aB1c",
    "senderEmail": "[email protected]",
    "subject": "Files for Monday",
    "state": "active",
    "status": "example",
    "totalBytes": 1,
    "fileCount": 1,
    "createdAt": "2026-10-05T18:00:00Z",
    "hasPassword": true,
    "recipientCount": 1,
    "downloadCount": 1,
    "collectedBy": 1,
    "rootId": "k7Qm2sLp9vX4aB1c",
    "rootName": "example"
  },
  "notified": [
    "example"
  ],
  "notNotified": [
    {
      "email": "[email protected]",
      "reason": "The scanner was wrong about this file."
    }
  ],
  "notifyProblem": "example",
  "links": [
    {
      "email": "[email protected]",
      "link": "example"
    }
  ]
}

Errors. The body always has the shape in Errors. Match on code.

StatuscodeWhen
400bad_requestno recipients, nothing chosen, an address that is not one, a passcode under 6 characters, or an expiry in the past
404not_foundno such storage location, or this caller may not read what was chosen
401unauthenticatedno key, or a key that is unknown, expired, revoked, or owned by a disabled account
403forbiddenthe key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator
402licence_requiredthe license does not include the REST API
{
  "error": {
    "code": "bad_request",
    "message": "no recipients, nothing chosen, an address that is not one, a passcode under 6 characters, or an expiry in the past"
  }
}

GET /api/v1/packages/{id}

One package: its files, each recipient, and every download they made.

Who. Any user with a key that has the read scope. A key with the admin scope includes the others. The key needs a license that includes the REST API.

Parameters.

NameInTypeMeaning
idpathstringrequiredThe resource's id.

Success. 200.

FieldTypeMeaning
packageobjectrequired

It is an object:

FieldTypeMeaning
idstringrequiredThe id.
senderIdstringrequiredThe id.
senderEmailstringrequiredAn email address.
subjectstringrequired
statestringrequiredWhere this record is in its life.
statusstringrequiredWhat a person reads: active, expired or revoked. Worked out from the state and the expiry, so an expired package says so without anything having to run at the moment it lapsed.
totalBytesintegerrequired
fileCountintegerrequired
createdAtstringrequiredA time, as RFC 3339.
expiresAtstringoptionalA time, as RFC 3339.
hasPasswordbooleanrequiredWhether a recipient has to type a password, not which one. The hash itself is never read out of the database by this module.
recipientCountintegerrequired
downloadCountintegerrequiredEvery file collected by every recipient, counted once each time.
collectedByintegerrequiredRecipients who have collected anything at all.
rootIdstringrequiredThe id.
rootNamestringrequiredThe storage location's name.
revokedAtstringoptionalA time, as RFC 3339.
revokedBystringoptional
messagestringrequired
filesarray of objectsrequired

Each item is an object:

FieldTypeMeaning
idstringrequiredThe id.
pathstringrequiredA path relative to the space or the storage location, with / between folders and no leading slash.
sizeintegerrequiredSize in bytes.
hashstringoptional
recipientsarray of objectsrequired

Each item is an object:

FieldTypeMeaning
idstringrequiredThe id.
emailstringrequiredAn email address.
createdAtstringrequiredA time, as RFC 3339.
expiresAtstringoptionalA time, as RFC 3339.
notifiedAtstringoptionalA time, as RFC 3339.
firstSeenAtstringoptionalA time, as RFC 3339.
lastSeenAtstringoptionalA time, as RFC 3339.
downloadCountintegerrequired
revokedAtstringoptionalA time, as RFC 3339.
revokedBystringoptional
downloadsarray of objectsrequiredNewest first.

Each item is an object:

FieldTypeMeaning
pathstringrequiredA path relative to the space or the storage location, with / between folders and no leading slash.
methodstringrequiredbrowser, zip or ticket.
atstringrequired
ipstringoptional
{
  "package": {
    "id": "k7Qm2sLp9vX4aB1c",
    "senderId": "k7Qm2sLp9vX4aB1c",
    "senderEmail": "[email protected]",
    "subject": "Files for Monday",
    "state": "active",
    "status": "example",
    "totalBytes": 1,
    "fileCount": 1,
    "createdAt": "2026-10-05T18:00:00Z",
    "hasPassword": true,
    "recipientCount": 1,
    "downloadCount": 1,
    "collectedBy": 1,
    "rootId": "k7Qm2sLp9vX4aB1c",
    "rootName": "example"
  },
  "message": "The cut is in the folder.",
  "files": [
    {
      "id": "k7Qm2sLp9vX4aB1c",
      "path": "projects/rush",
      "size": 1048576,
      "hash": "example"
    }
  ],
  "recipients": [
    {
      "id": "k7Qm2sLp9vX4aB1c",
      "email": "[email protected]",
      "createdAt": "2026-10-05T18:00:00Z",
      "downloadCount": 1,
      "downloads": [
        {
          "path": "projects/rush",
          "method": "example",
          "at": "2026-10-05T18:00:00Z",
          "ip": "example"
        }
      ]
    }
  ]
}

Errors. The body always has the shape in Errors. Match on code.

StatuscodeWhen
401unauthenticatedno key, or a key that is unknown, expired, revoked, or owned by a disabled account
403forbiddenthe key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator
402licence_requiredthe license does not include the REST API
{
  "error": {
    "code": "unauthenticated",
    "message": "sign in to continue"
  }
}

DELETE /api/v1/packages/{id}

Withdraw it. Every link stops working at once.

Who. Any user with a key that has the delete scope. A key with the admin scope includes the others. The key needs a license that includes the REST API.

Parameters.

NameInTypeMeaning
idpathstringrequiredThe resource's id.

Success. 200.

FieldTypeMeaning
idstringrequiredThe id.
senderIdstringrequiredThe id.
senderEmailstringrequiredAn email address.
subjectstringrequired
statestringrequiredWhere this record is in its life.
statusstringrequiredWhat a person reads: active, expired or revoked. Worked out from the state and the expiry, so an expired package says so without anything having to run at the moment it lapsed.
totalBytesintegerrequired
fileCountintegerrequired
createdAtstringrequiredA time, as RFC 3339.
expiresAtstringoptional, left out when emptyA time, as RFC 3339.
hasPasswordbooleanrequiredWhether a recipient has to type a password, not which one. The hash itself is never read out of the database by this module.
recipientCountintegerrequired
downloadCountintegerrequiredEvery file collected by every recipient, counted once each time.
collectedByintegerrequiredRecipients who have collected anything at all.
rootIdstringrequiredThe id.
rootNamestringrequiredThe storage location's name.
revokedAtstringoptional, left out when emptyA time, as RFC 3339.
revokedBystringoptional, left out when empty
{
  "id": "k7Qm2sLp9vX4aB1c",
  "senderId": "k7Qm2sLp9vX4aB1c",
  "senderEmail": "[email protected]",
  "subject": "Files for Monday",
  "state": "active",
  "status": "example",
  "totalBytes": 1,
  "fileCount": 1,
  "createdAt": "2026-10-05T18:00:00Z",
  "hasPassword": true,
  "recipientCount": 1,
  "downloadCount": 1,
  "collectedBy": 1,
  "rootId": "k7Qm2sLp9vX4aB1c",
  "rootName": "example"
}

Errors. The body always has the shape in Errors. Match on code.

StatuscodeWhen
401unauthenticatedno key, or a key that is unknown, expired, revoked, or owned by a disabled account
403forbiddenthe key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator
402licence_requiredthe license does not include the REST API
{
  "error": {
    "code": "unauthenticated",
    "message": "sign in to continue"
  }
}

DELETE /api/v1/packages/{id}/recipients/{recipient_id}

Withdraw one person's link. The others keep working.

Who. Any user with a key that has the delete scope. A key with the admin scope includes the others. The key needs a license that includes the REST API.

Parameters.

NameInTypeMeaning
idpathstringrequiredThe package.
recipient_idpathstringrequiredThe recipient.

Success. 200.

FieldTypeMeaning
packageobjectrequired

It is an object:

FieldTypeMeaning
idstringrequiredThe id.
senderIdstringrequiredThe id.
senderEmailstringrequiredAn email address.
subjectstringrequired
statestringrequiredWhere this record is in its life.
statusstringrequiredWhat a person reads: active, expired or revoked. Worked out from the state and the expiry, so an expired package says so without anything having to run at the moment it lapsed.
totalBytesintegerrequired
fileCountintegerrequired
createdAtstringrequiredA time, as RFC 3339.
expiresAtstringoptionalA time, as RFC 3339.
hasPasswordbooleanrequiredWhether a recipient has to type a password, not which one. The hash itself is never read out of the database by this module.
recipientCountintegerrequired
downloadCountintegerrequiredEvery file collected by every recipient, counted once each time.
collectedByintegerrequiredRecipients who have collected anything at all.
rootIdstringrequiredThe id.
rootNamestringrequiredThe storage location's name.
revokedAtstringoptionalA time, as RFC 3339.
revokedBystringoptional
messagestringrequired
filesarray of objectsrequired

Each item is an object:

FieldTypeMeaning
idstringrequiredThe id.
pathstringrequiredA path relative to the space or the storage location, with / between folders and no leading slash.
sizeintegerrequiredSize in bytes.
hashstringoptional
recipientsarray of objectsrequired

Each item is an object:

FieldTypeMeaning
idstringrequiredThe id.
emailstringrequiredAn email address.
createdAtstringrequiredA time, as RFC 3339.
expiresAtstringoptionalA time, as RFC 3339.
notifiedAtstringoptionalA time, as RFC 3339.
firstSeenAtstringoptionalA time, as RFC 3339.
lastSeenAtstringoptionalA time, as RFC 3339.
downloadCountintegerrequired
revokedAtstringoptionalA time, as RFC 3339.
revokedBystringoptional
downloadsarray of objectsrequiredNewest first.

Each item is an object:

FieldTypeMeaning
pathstringrequiredA path relative to the space or the storage location, with / between folders and no leading slash.
methodstringrequiredbrowser, zip or ticket.
atstringrequired
ipstringoptional
{
  "package": {
    "id": "k7Qm2sLp9vX4aB1c",
    "senderId": "k7Qm2sLp9vX4aB1c",
    "senderEmail": "[email protected]",
    "subject": "Files for Monday",
    "state": "active",
    "status": "example",
    "totalBytes": 1,
    "fileCount": 1,
    "createdAt": "2026-10-05T18:00:00Z",
    "hasPassword": true,
    "recipientCount": 1,
    "downloadCount": 1,
    "collectedBy": 1,
    "rootId": "k7Qm2sLp9vX4aB1c",
    "rootName": "example"
  },
  "message": "The cut is in the folder.",
  "files": [
    {
      "id": "k7Qm2sLp9vX4aB1c",
      "path": "projects/rush",
      "size": 1048576,
      "hash": "example"
    }
  ],
  "recipients": [
    {
      "id": "k7Qm2sLp9vX4aB1c",
      "email": "[email protected]",
      "createdAt": "2026-10-05T18:00:00Z",
      "downloadCount": 1,
      "downloads": [
        {
          "path": "projects/rush",
          "method": "example",
          "at": "2026-10-05T18:00:00Z",
          "ip": "example"
        }
      ]
    }
  ]
}

Errors. The body always has the shape in Errors. Match on code.

StatuscodeWhen
404not_foundnot there, or not visible to this caller
401unauthenticatedno key, or a key that is unknown, expired, revoked, or owned by a disabled account
403forbiddenthe key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator
402licence_requiredthe license does not include the REST API
{
  "error": {
    "code": "not_found",
    "message": "not found"
  }
}