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.
| Name | In | Type | Meaning | |
|---|---|---|---|---|
status | query | string | optional | active (still collectable) or history (withdrawn or expired). |
scope | query | string | optional | mine, or all for an administrator. An administrator who leaves it out sees everything; anyone else always sees their own. |
state | query | string | optional | draft, uploading, ready, expired or deleted. |
limit | query | integer | optional | 1 to 500. |
Success. 200.
The body is an array.
| Field | Type | Meaning | |
|---|---|---|---|
id | string | required | The id. |
senderId | string | required | The id. |
senderEmail | string | required | An email address. |
subject | string | required | |
state | string | required | Where this record is in its life. |
status | string | required | What 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. |
totalBytes | integer | required | |
fileCount | integer | required | |
createdAt | string | required | A time, as RFC 3339. |
expiresAt | string | optional, left out when empty | A time, as RFC 3339. |
hasPassword | boolean | required | Whether a recipient has to type a password, not which one. The hash itself is never read out of the database by this module. |
recipientCount | integer | required | |
downloadCount | integer | required | Every file collected by every recipient, counted once each time. |
collectedBy | integer | required | Recipients who have collected anything at all. |
rootId | string | required | The id. |
rootName | string | required | The storage location's name. |
revokedAt | string | optional, left out when empty | A time, as RFC 3339. |
revokedBy | string | optional, 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.
| Status | code | When |
|---|---|---|
| 400 | bad_request | scope must be mine or all |
| 401 | unauthenticated | no key, or a key that is unknown, expired, revoked, or owned by a disabled account |
| 403 | forbidden | the key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator |
| 402 | licence_required | the 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.
| Field | Type | Meaning | |
|---|---|---|---|
root | string | optional | The location, for a caller that still names storage directly. |
space | string | optional | The space the files were chosen from. Paths are then relative to it. |
subject | string | optional | |
message | string | optional | |
files | array of string | optional | |
everything | boolean | optional | |
recipients | array of string | required | |
expiresAt | string | optional | A time, as RFC 3339. |
passcode | string | optional | Typed by a person and told to the recipient some other way. Never put in the email that carries the link. |
notify | boolean | optional | Whether to email the recipients now. A sender who wants to hand the links over themselves can say no. |
{
"recipients": [
"example"
]
}
Success. 200.
| Field | Type | Meaning | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
package | object | required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
It is an object:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
notified | array of string | required | Addresses that were emailed their link. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
notNotified | array of objects | required | Addresses 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:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
notifyProblem | string | optional, left out when empty | Why nobody was emailed, when that applies to all of them at once — email not set up, or no server address to build links from. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
links | array of objects | required | Each 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:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{
"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.
| Status | code | When |
|---|---|---|
| 400 | bad_request | no recipients, nothing chosen, an address that is not one, a passcode under 6 characters, or an expiry in the past |
| 404 | not_found | no such storage location, or this caller may not read what was chosen |
| 401 | unauthenticated | no key, or a key that is unknown, expired, revoked, or owned by a disabled account |
| 403 | forbidden | the key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator |
| 402 | licence_required | the 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.
| Name | In | Type | Meaning | |
|---|---|---|---|---|
id | path | string | required | The resource's id. |
Success. 200.
| Field | Type | Meaning | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
package | object | required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
It is an object:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
message | string | required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
files | array of objects | required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Each item is an object:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
recipients | array of objects | required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Each item is an object:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{
"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.
| Status | code | When |
|---|---|---|
| 401 | unauthenticated | no key, or a key that is unknown, expired, revoked, or owned by a disabled account |
| 403 | forbidden | the key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator |
| 402 | licence_required | the 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.
| Name | In | Type | Meaning | |
|---|---|---|---|---|
id | path | string | required | The resource's id. |
Success. 200.
| Field | Type | Meaning | |
|---|---|---|---|
id | string | required | The id. |
senderId | string | required | The id. |
senderEmail | string | required | An email address. |
subject | string | required | |
state | string | required | Where this record is in its life. |
status | string | required | What 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. |
totalBytes | integer | required | |
fileCount | integer | required | |
createdAt | string | required | A time, as RFC 3339. |
expiresAt | string | optional, left out when empty | A time, as RFC 3339. |
hasPassword | boolean | required | Whether a recipient has to type a password, not which one. The hash itself is never read out of the database by this module. |
recipientCount | integer | required | |
downloadCount | integer | required | Every file collected by every recipient, counted once each time. |
collectedBy | integer | required | Recipients who have collected anything at all. |
rootId | string | required | The id. |
rootName | string | required | The storage location's name. |
revokedAt | string | optional, left out when empty | A time, as RFC 3339. |
revokedBy | string | optional, 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.
| Status | code | When |
|---|---|---|
| 401 | unauthenticated | no key, or a key that is unknown, expired, revoked, or owned by a disabled account |
| 403 | forbidden | the key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator |
| 402 | licence_required | the 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.
| Name | In | Type | Meaning | |
|---|---|---|---|---|
id | path | string | required | The package. |
recipient_id | path | string | required | The recipient. |
Success. 200.
| Field | Type | Meaning | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
package | object | required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
It is an object:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
message | string | required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
files | array of objects | required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Each item is an object:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
recipients | array of objects | required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Each item is an object:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{
"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.
| Status | code | When |
|---|---|---|
| 404 | not_found | not there, or not visible to this caller |
| 401 | unauthenticated | no key, or a key that is unknown, expired, revoked, or owned by a disabled account |
| 403 | forbidden | the key is valid but does not have the scope, or an administrator route was called by someone who is not an administrator |
| 402 | licence_required | the license does not include the REST API |
{
"error": {
"code": "not_found",
"message": "not found"
}
}