| name | file-sharing |
| display_name | File Sharing API |
| description | Upload files, create expiring share links with optional password protection, and track downloads on a self-hosted file drop service. |
| base_url | http://localhost:3000 |
| auth | none |
| version | 1.0 |
File Sharing Skill
When to use
Use this skill to:
- Upload files to the local server storage
- Create share links with expiry dates, download limits, and passwords
- Retrieve download counts and logs for specific files or links
- Manage link lifecycle (create, update, revoke, reactivate)
Base URL
http://localhost:3000
No authentication required. This service is intended for local or private network deployment.
Quick Reference
| Action | Method | Path |
|---|
| Upload file | POST | /api/files |
| List files | GET | /api/files |
| Get file metadata | GET | /api/files/:slug |
| Delete file | DELETE | /api/files/:slug |
| Create share link | POST | /api/files/:slug/links |
| List links for file | GET | /api/files/:slug/links |
| Get link metadata | GET | /api/links/:token |
| Update link | PUT | /api/links/:token |
| Revoke link | DELETE | /api/links/:token |
| Download file | GET | /dl/:token |
| Get download log | GET | /api/downloads |
| Get stats | GET | /api/stats |
Upload a file
curl -X POST http://localhost:3000/api/files \
-F "file=@/path/to/Q1-report.pdf"
Response 201:
{
"id": 1,
"slug": "ab3kx9mz1qwe",
"original_name": "Q1-report.pdf",
"mime_type": "application/pdf",
"size_bytes": 2516582,
"created_at": "2026-03-20T10:00:00Z"
}
Files larger than MAX_FILE_SIZE_MB (default 100) return 413 Payload Too Large.
List files
curl http://localhost:3000/api/files?limit=20&offset=0
Response:
{
"files": [
{
"slug": "ab3kx9mz1qwe",
"original_name": "Q1-report.pdf",
"mime_type": "application/pdf",
"size_bytes": 2516582,
"link_count": 3,
"download_count": 142,
"created_at": "2026-03-20T10:00:00Z"
}
],
"total": 37
}
Create a share link
All fields are optional.
curl -X POST http://localhost:3000/api/files/ab3kx9mz1qwe/links \
-H "Content-Type: application/json" \
-d '{
"label": "Client share",
"expires_at": "2026-04-20T00:00:00Z",
"max_downloads": 10,
"password": "hunter2"
}'
Response 201:
{
"token": "pq7rn4xyz90a",
"download_url": "http://localhost:3000/dl/pq7rn4xyz90a",
"label": "Client share",
"expires_at": "2026-04-20T00:00:00Z",
"max_downloads": 10,
"download_count": 0,
"has_password": true,
"is_active": true,
"created_at": "2026-03-20T10:05:00Z"
}
Create a permanent, public link (no restrictions)
curl -X POST http://localhost:3000/api/files/ab3kx9mz1qwe/links \
-H "Content-Type: application/json" \
-d '{}'
Download a file
Use the token from the share link:
curl -L -O http://localhost:3000/dl/pq7rn4xyz90a
If password-protected:
curl -L -O http://localhost:3000/dl/pq7rn4xyz90a \
-H "X-Link-Password: hunter2"
Error responses:
| Status | Meaning |
|---|
| 401 | {"requires_password": true} - password required or wrong |
| 410 | Link has expired or exhausted its download limit |
| 404 | Token does not exist |
Update a share link
Only label, expires_at, and max_downloads can be changed. To clear a field, pass null.
curl -X PUT http://localhost:3000/api/links/pq7rn4xyz90a \
-H "Content-Type: application/json" \
-d '{"expires_at": "2026-05-01T00:00:00Z", "max_downloads": 20}'
Revoke a share link
curl -X DELETE http://localhost:3000/api/links/pq7rn4xyz90a
Response 204 No Content. Link is deactivated (is_active = 0). Downloads previously recorded are retained.
List downloads for a file
curl http://localhost:3000/api/files/ab3kx9mz1qwe/downloads?limit=50
List downloads for a link
curl http://localhost:3000/api/links/pq7rn4xyz90a/downloads
Get aggregate stats
curl http://localhost:3000/api/stats
Response:
{
"total_files": 37,
"total_links": 35,
"active_links": 24,
"total_downloads": 1847,
"storage_bytes": 4509715456,
"data_served_bytes": 44040192000
}
File object
interface File {
id: number;
slug: string;
original_name: string;
mime_type: string;
size_bytes: number;
created_at: string;
}
ShareLink object
interface ShareLink {
token: string;
file_id: number;
label: string | null;
expires_at: string | null;
max_downloads: number | null;
download_count: number;
has_password: boolean;
is_active: boolean;
download_url: string;
created_at: string;
}
Error responses
{ "error": "File not found", "code": "NOT_FOUND" }
| Status | code | Meaning |
|---|
| 400 | VALIDATION_ERROR | Missing or invalid fields |
| 404 | NOT_FOUND | File or link does not exist |
| 410 | GONE | Link expired or download limit reached |
| 413 | FILE_TOO_LARGE | Upload exceeds MAX_FILE_SIZE_MB |
| 500 | INTERNAL_ERROR | Server error |
Environment variables
| Variable | Default | Description |
|---|
| PORT | 3000 | HTTP server port |
| DATA_DIR | ./data | Storage path for DB and files |
| MAX_FILE_SIZE_MB | 100 | Maximum upload size |
| ALLOW_ANONYMOUS_UPLOAD | 1 | 1 = uploads are open, 0 = disabled |