API Reference
Authentication
Endpoints marked as authenticated require the following header:
Authorization: Bearer <access_token>
Requests with a missing, malformed, or expired token will receive a 401 Unauthorized response.
Base URL
All API endpoints are relative to Hashdrop server's base URL, e.g. https://api.hashdrop.dev.
Endpoints
Health check
GET /api/healthz
Returns a plain text OK response indicating the API is running and ready to serve requests.
Authentication: None
Request body: None
Response 200 OK
OK
Content-Type: text/plain; charset=utf-8
Register a new user
POST /api/user/register
Creates a new user account. On success, a verification OTP is sent to the provided email address. The account must be verified before login is possible.
Authentication: None
Request body
{
"email": "user@example.com",
"password": "yourpassword"
}
Response 201 Created
{
"id": "uuid",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"email": "user@example.com"
}
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Malformed JSON or invalid password |
409 Conflict |
An account with this email already exists |
500 Internal Server Error |
Unexpected server error |
Verify account
PATCH /api/user/verify
Verifies a newly registered account using the OTP sent to the user's email. Once verified, the account can be used to log in.
Authentication: None
Request body
{
"email": "user@example.com",
"otp": "123456"
}
Response 204 No Content
No response body.
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Malformed JSON or OTP has expired |
401 Unauthorized |
Account not found or invalid OTP |
500 Internal Server Error |
Unexpected server error |
Login
POST /api/user/login
Authenticates a verified user and returns an access token and a refresh token. The account must be verified before login is possible.
Authentication: None
Request body
{
"email": "user@example.com",
"password": "yourpassword"
}
Response 200 OK
{
"access_token": "eyJ...",
"refresh_token": "a3f8..."
}
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Malformed JSON |
401 Unauthorized |
Invalid email or password, or account not verified |
500 Internal Server Error |
Unexpected server error |
Delete account
DELETE /api/user
Permanently deletes the authenticated user's account. This removes all uploaded files from storage, all associated database records, and invalidates all tokens. This action is irreversible.
Authentication: Required
Request body
{
"email": "user@example.com",
"password": "yourpassword"
}
Email and password are required as explicit confirmation before deletion proceeds.
Response 204 No Content
No response body.
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Malformed JSON |
401 Unauthorized |
Invalid email or password |
500 Internal Server Error |
Unexpected server error |
Refresh access token
POST /api/token/refresh
Exchanges a valid refresh token for a new access token. Use this when the current access token has expired.
Authentication: None
Request body
{
"refresh_token": "a3f8..."
}
Response 200 OK
{
"access_token": "eyJ..."
}
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Malformed JSON |
401 Unauthorized |
Refresh token is invalid, expired, or revoked |
500 Internal Server Error |
Unexpected server error |
Revoke refresh token
POST /api/token/revoke
Revokes a refresh token, immediately invalidating it. Any subsequent attempts to use it to obtain a new access token will fail. This is called automatically when the user logs out.
Authentication: None
Request body
{
"refresh_token": "a3f8..."
}
Response 204 No Content
No response body.
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Malformed JSON |
500 Internal Server Error |
Unexpected server error |
Request presigned upload URL
POST /api/files/presign
Validates storage quotas and returns a presigned S3 URL to upload an encrypted file directly to storage, along with a file ID to reference the upload in subsequent requests.
Authentication: Required
Request body
{
"file_name": "document.pdf",
"mime_type": "application/pdf"
}
Response 200 OK
{
"file_id": "uuid",
"upload_resource": {
"url": "https://s3.amazonaws.com/..."
}
}
The url in upload_resource is a presigned S3 PUT URL. The encrypted file should be uploaded directly to this URL with the content type set to application/octet-stream. The URL has a limited expiry window — upload should begin immediately after receiving it.
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Malformed JSON |
401 Unauthorized |
Missing or invalid access token |
403 Forbidden |
User storage quota exceeded |
503 Service Unavailable |
Global storage limit reached, uploads temporarily disabled |
500 Internal Server Error |
Unexpected server error |
Complete file upload
POST /api/files/complete
Called after the encrypted file has been successfully uploaded to S3 via the presigned URL. The server verifies the uploaded file size, finalises the file record, and marks it as uploaded. If the file exceeds the allowed size limit, it is deleted from storage and the upload is marked as failed.
Authentication: Required
Request body
{
"file_id": "uuid",
"plaintext_hash": "sha256...",
"plaintext_size_bytes": 102400,
"passphrase_salt": "hex-encoded-salt"
}
passphrase_salt should be provided only when the file was uploaded in no-vault mode. Leave it as an empty string or omit it for vault mode uploads.
Response 200 OK
{
"s3_object_key": "usrh-abc123/uuid",
"uploaded_file_size": 102400
}
uploaded_file_size reflects the server-verified encrypted file size in bytes as reported by S3, not the client-reported value.
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Malformed JSON |
401 Unauthorized |
Missing or invalid access token |
404 Not Found |
File ID not found or does not belong to this user |
413 Content Too Large |
Uploaded file exceeds the allowed size limit |
500 Internal Server Error |
Unexpected server error |
List all files
GET /api/files/all
Returns a list of all files belonging to the authenticated user.
Authentication: Required
Request body: None
Response 200 OK
[
{
"file_id": "uuid",
"file_name": "document.pdf",
"encrypted_size_bytes": 102400,
"status": "uploaded",
"key_management_mode": "vault",
"created_at": "2024-01-01T00:00:00Z"
}
]
key_management_mode is either vault or passphrase. status reflects the current state of the file and can be pending, uploaded, failed, or deleted.
Error responses
| Status | Description |
|---|---|
401 Unauthorized |
Missing or invalid access token |
404 Not Found |
No files found for this user |
500 Internal Server Error |
Unexpected server error |
Resolve file ID conflicts
GET /api/files/resolve?id={fileIDPrefix}
Returns all files whose IDs begin with the given prefix. Used when a shortened file ID matches more than one file, allowing the correct file to be identified by its full ID and name.
Authentication: Required
Query parameters
| Parameter | Required | Description |
|---|---|---|
id |
Yes | A file ID prefix to match against |
Response 200 OK
[
{
"file_name": "document.pdf",
"file_id": "uuid"
}
]
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Missing id query parameter |
401 Unauthorized |
Missing or invalid access token |
404 Not Found |
No files found matching the given prefix |
500 Internal Server Error |
Unexpected server error |
Get file details
GET /api/files?id={fileID}
Returns detailed metadata for files matching the given file ID. The id parameter is matched as a prefix, so a full or partial file ID can be provided. If multiple files match, all matches are returned.
Authentication: Required
Query parameters
| Parameter | Required | Description |
|---|---|---|
id |
Yes | Full or partial file ID to match against |
Response 200 OK
[
{
"file_id": "uuid",
"file_name": "document.pdf",
"status": "uploaded",
"plaintext_size_bytes": 98304,
"encrypted_size_bytes": 102400,
"s3_key": "usrh-abc123/uuid",
"key_management_mode": "vault",
"plaintext_hash": "sha256..."
}
]
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Missing id query parameter |
401 Unauthorized |
Missing or invalid access token |
404 Not Found |
No files found matching the given ID |
500 Internal Server Error |
Unexpected server error |
Get file hash
GET /api/files/hash?id={fileID}
Returns the plaintext hash of a file. Used to verify the integrity of a decrypted file against the hash computed at upload time.
Authentication: Required
Query parameters
| Parameter | Required | Description |
|---|---|---|
id |
Yes | The exact full file UUID |
Response 200 OK
{
"hash": "sha256..."
}
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Missing or invalid id query parameter |
401 Unauthorized |
Missing or invalid access token |
404 Not Found |
No file found matching the given ID |
500 Internal Server Error |
Unexpected server error |
Get passphrase salt
GET /api/files/salt?id={fileID}
Returns the passphrase salt for a file that was uploaded in no-vault mode. This salt is needed to re-derive the DEK from the original passphrase during decryption. Only available for files encrypted with a passphrase — vault mode files do not have a salt stored server-side.
Authentication: Required
Query parameters
| Parameter | Required | Description |
|---|---|---|
id |
Yes | The exact full file UUID |
Response 200 OK
{
"salt": "hex-encoded-salt"
}
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Missing or invalid id query parameter |
401 Unauthorized |
Missing or invalid access token |
404 Not Found |
No passphrase salt found for this file |
500 Internal Server Error |
Unexpected server error |
Delete file
DELETE /api/files?id={fileID}
Permanently deletes a file from both storage and the database. The id parameter is matched as a prefix. If the prefix matches more than one file, the request is rejected to prevent accidental deletion — use a longer prefix or the full file UUID to disambiguate.
Authentication: Required
Query parameters
| Parameter | Required | Description |
|---|---|---|
id |
Yes | Full or partial file ID to match against |
Response 204 No Content
No response body.
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Missing id query parameter |
401 Unauthorized |
Missing or invalid access token |
404 Not Found |
No file found matching the given ID |
409 Conflict |
The given ID prefix matches multiple files |
500 Internal Server Error |
Unexpected server error |
Download file
GET /files/download/{userIDHash}/{fileID}
Validates the daily download limit for the file and redirects to a short-lived signed CloudFront URL serving the encrypted file. This is a public endpoint — no authentication is required. The file URL is typically obtained from the files show command or the file details endpoint.
Authentication: None
Path parameters
| Parameter | Description |
|---|---|
userIDHash |
Hashed user ID of the file owner |
fileID |
Full file UUID |
Response 303 See Other
Redirects directly to a signed CloudFront URL. The response body contains the encrypted file content.
Error responses
| Status | Description |
|---|---|
400 Bad Request |
Invalid file ID format |
429 Too Many Requests |
Daily download limit exhausted for this file |
500 Internal Server Error |
Unexpected server error |
Reset (development only)
DELETE /admin/reset
Deletes all users and associated data from the database. This endpoint is only available when the server is running in a dev environment and will reject requests in any other environment.
Authentication: None
Request body: None
Response 204 No Content
No response body.
Error responses
| Status | Description |
|---|---|
403 Forbidden |
Server is not running in a dev environment |
500 Internal Server Error |
Unexpected server error |
Get compatible CLI version
GET /api/cli/version
Returns the minimum CLI version compatible with this server. The CLI calls this endpoint before executing any command to check whether the installed version is still supported. If the installed version does not match, the user is prompted to update.
Authentication: None
Request body: None
Response 200 OK
{
"compatible_version": "1.0.0"
}
Error responses
| Status | Description |
|---|---|
500 Internal Server Error |
Unexpected server error |