Skip to content
CelPlume
select · Enteropen · Escclose Powered by Pagefind

API Reference

Authentication, all endpoints, error codes, and response formats.

  • Base URL: the deployed origin of the Workers application, for example https://{domain}. All paths in this reference are relative to that origin.
  • Authentication: each request uses one of three methods, depending on the client.
  • CSRF tokens: cookie-authenticated write requests must include the X-CSRF-Token header. Obtain a token from GET /api/auth/csrf-token. API-key and WebDAV requests skip CSRF checks.
  • Content type: send JSON request bodies with Content-Type: application/json. File uploads use multipart/form-data or a raw body.
  • Rate limits: auth endpoints allow 5 requests per minute per IP. Other endpoints apply a configurable per-user and per-IP limit.
MethodHeaderUse for
HttpOnly cookie JWTCookie: auth_token=...Web frontend and browser clients
API key (Bearer)Authorization: Bearer {key_id}.{secret}PicGo, PicList, scripts, and custom clients
WebDAV BasicAuthorization: Basic base64({key_id}:{secret})WebDAV clients

The API key is an opaque token in the format pk_{24 chars}.sk_{48 chars}. The server stores only a SHA-256 hash of the full token, so you cannot retrieve it again after creation.

Every successful response uses the same envelope.

{
"success": true,
"data": { },
"message": "optional message",
"timestamp": 1710000000000
}
FieldTypeDescription
successbooleanAlways true for successful responses.
dataobjectThe resource payload. The shape depends on the endpoint.
messagestringAn optional human-readable message.
timestampintegerThe server time in milliseconds since the Unix epoch.

Failed requests return an error envelope with an HTTP status code and a machine-readable error code.

{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "文件不存在",
"details": {}
},
"timestamp": 1710000000000
}
FieldTypeDescription
successbooleanAlways false for error responses.
error.codestringA stable machine-readable code such as NOT_FOUND.
error.messagestringA human-readable message.
error.detailsobjectAdditional details. Present only in development environments.
timestampintegerThe server time in milliseconds since the Unix epoch.
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400A request field or query parameter has an invalid value.Correct the field and retry.
INVALID_CREDENTIALS401The username or password is wrong.Re-enter the credentials.
UNAUTHORIZED401The request has no valid session or API key.Log in or provide a valid key.
INVALID_TOKEN401The session token, API key, or download token is invalid or expired.Refresh the credential and retry.
INVALID_PASSWORD401A file, folder, or share password is incorrect.Re-enter the password.
USER_DISABLED401The account is not active.Contact the administrator.
ROLE_CHANGED401The account role changed since the session started.Log in again.
SESSION_REVOKED401The session ended after a password change or logout.Log in again.
EMAIL_NOT_VERIFIED403Login requires email verification.Verify the email first.
FORBIDDEN403The caller lacks permission for the operation.Check the permission rules and the API key scope.
PASSWORD_REQUIRED403The file is password-protected and the password is not verified.Call the password-verify endpoint first.
NOT_FOUND404The resource, file, mount, or path does not exist.Confirm the identifier or path and retry.
ALREADY_EXISTS409A file or folder with the same name already exists.Use a different name.
OPERATION_FAILED409 / 422The operation cannot proceed because of the current state.Check the error message and retry.
SHARE_EXPIRED410The share link has expired.Ask the creator for a new link.
SHARE_REVOKED410The share link no longer works.Ask the creator for a new link.
SHARE_LIMIT_REACHED410The share hit its view or download limit.Ask the creator to raise the limit.
UPLOAD_SESSION_EXPIRED410The upload session exceeded its one-hour lifetime.Start a new upload session.
QUOTA_EXCEEDED413The storage or file-count quota has run out.Free up space or raise the quota.
PAYLOAD_TOO_LARGE413The upload exceeds the 1 GB free-mode limit.Split the file or use a smaller file.
RATE_LIMIT_EXCEEDED429The caller exceeded a rate limit.Wait and retry, or raise the limit.
INTERNAL_ERROR500An unexpected server error occurred.Retry later or report the issue.

Authentication endpoints manage registration, login, sessions, email verification, and password reset. Registration, login, logout, and password endpoints are public; GET /api/auth/me and GET /api/auth/csrf-token require a logged-in session.

Creates a user account and, when the site requires email verification, sends a verification link.

POST /api/auth/register

FieldTypeRequiredDescription
usernamestringYes3 to 20 characters. Letters, digits, and underscores only.
passwordstringYesAt least 8 characters, at most 128.
emailstringYesA valid email address.
inviteCodestringNoThe invite code, required when invite codes are active.
turnstileTokenstringNoThe Turnstile token, required when Turnstile is active.

Returns the new user and a confirmation message with status 201.

{
"success": true,
"data": {
"user": {
"id": "uuid",
"username": "alice",
"email": "alice@example.com",
"emailVerified": false,
"role": "user"
},
"message": "注册成功"
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400A field violates the format rules.Correct the field and retry.
FORBIDDEN403This site does not allow registration.Contact the administrator.
ALREADY_EXISTS409The username or email is already registered.Choose another username or email.
curl -X POST https://{domain}/api/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"alice","password":"secret-pass","email":"alice@example.com"}'

Authenticates a user and sets an HttpOnly auth_token cookie for 7 days.

POST /api/auth/login

FieldTypeRequiredDescription
usernamestringYesThe username.
passwordstringYesThe password.
turnstileTokenstringNoThe Turnstile token, required when Turnstile is active.

Returns the user and the current quota.

{
"success": true,
"data": {
"user": {
"id": "uuid",
"username": "alice",
"email": "alice@example.com",
"emailVerified": true,
"role": "user",
"displayName": "Alice",
"avatarUrl": null,
"defaultPath": "/",
"locale": "zh-CN",
"theme": "system"
},
"quota": {
"maxStorage": 10737418240,
"usedStorage": 0,
"maxFiles": 1000,
"usedFiles": 0
}
},
"timestamp": 1710000000000
}

The Set-Cookie header carries the auth_token JWT with HttpOnly, SameSite=Strict, and a 7-day lifetime.

Error CodeHTTP StatusCauseRecommended Action
INVALID_CREDENTIALS401The username or password is wrong.Re-enter the credentials.
USER_DISABLED401The account is not active.Contact the administrator.
EMAIL_NOT_VERIFIED403Login requires email verification.Verify the email first.
curl -X POST https://{domain}/api/auth/login \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"username":"alice","password":"secret-pass"}'

Revokes the current session and clears the auth_token cookie. Logout bumps the session version, which invalidates every JWT issued for the account.

POST /api/auth/logout

Returns data: null on success.

curl -X POST https://{domain}/api/auth/logout \
-b cookies.txt

Returns the logged-in user and their quota.

GET /api/auth/me

{
"success": true,
"data": {
"user": {
"id": "uuid",
"username": "alice",
"email": "alice@example.com",
"emailVerified": true,
"role": "user",
"displayName": "Alice",
"defaultPath": "/",
"locale": "zh-CN",
"theme": "system"
},
"quota": {
"maxStorage": 10737418240,
"usedStorage": 1048576,
"maxFiles": 1000,
"usedFiles": 3
}
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
UNAUTHORIZED401No valid session cookie.Log in first.
curl https://{domain}/api/auth/me -b cookies.txt

Returns a CSRF token for the logged-in session. Send this token in the X-CSRF-Token header of cookie-authenticated write requests. The server caches the token in KV for 2 hours.

GET /api/auth/csrf-token

{
"success": true,
"data": { "token": "32-char-random-string" },
"timestamp": 1710000000000
}
curl https://{domain}/api/auth/csrf-token -b cookies.txt

Completes email verification with the token from the verification email. Both /verify-email and /verify are aliases. The endpoint returns an HTML confirmation page, not JSON.

GET /api/auth/verify-email?token={token}

GET /api/auth/verify?token={token}

FieldTypeRequiredDescription
tokenstringYesThe verification token from the email. Valid for 24 hours.
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The token is missing, invalid, or expired.Request a new verification email.

Sends a password-reset link to the email address. The response does not reveal whether the email belongs to an account.

POST /api/auth/forgot-password

FieldTypeRequiredDescription
emailstringYesThe registered email address.
{
"success": true,
"data": { "message": "如果该邮箱已注册,重置链接已发送" },
"timestamp": 1710000000000
}
curl -X POST https://{domain}/api/auth/forgot-password \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com"}'

Sets a new password with the token from the reset link. A successful reset revokes all existing sessions.

POST /api/auth/reset-password

FieldTypeRequiredDescription
tokenstringYesThe reset token from the email. Valid for 15 minutes.
passwordstringYesThe new password, at least 8 characters.
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The token is invalid, expired, or the password is too short.Request a new reset link.

File endpoints list, create, update, move, and delete files and folders. All of them require a logged-in session, and each operation enforces path-level permission rules.

Lists the children of a directory with pagination, sorting, filtering, and search.

GET /api/files?path={path}&page={page}&limit={limit}&sort={sort}&order={order}&type={type}&search={search}

FieldTypeRequiredDescription
pathstringNoThe directory to list. Defaults to /.
pageintegerNoThe page number. Defaults to 1.
limitintegerNoItems per page. Defaults to 100, maximum 1000.
sortstringNoname, time, size, or manual. Defaults to the mount setting.
orderstringNoasc or desc. Defaults to asc.
typestringNofile or folder to filter by type.
searchstringNoA keyword to match against file names.
{
"success": true,
"data": {
"items": [
{
"id": "uuid",
"name": "photo.jpg",
"path": "/drive/photos",
"type": "file",
"size": 1048576,
"mimeType": "image/jpeg",
"hasPassword": false,
"ownerId": "uuid",
"createdAt": 1710000000000,
"updatedAt": 1710000000000
}
],
"pagination": { "total": 12, "page": 1, "limit": 100, "pages": 1 },
"mount": { "id": "mount-id", "name": "Drive", "sortBy": "name", "sortOrder": "asc" }
},
"timestamp": 1710000000000
}
FieldTypeDescription
itemsarrayThe file and folder entries in the directory.
items[].idstringThe file identifier.
items[].namestringThe file or folder name.
items[].pathstringThe parent directory path.
items[].typestringfile or folder.
items[].sizeintegerThe size in bytes.
items[].mimeTypestringThe MIME type, for files.
items[].hasPasswordbooleanWhether the file is password-protected.
paginationobjecttotal, page, limit, and pages.
mountobjectThe mount that contains the directory.
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400A query parameter is invalid.Correct the parameter and retry.
NOT_FOUND404The path is not mounted.Confirm the path.
FORBIDDEN403The caller lacks read permission.Check the permission rules.
curl "https://{domain}/api/files?path=/drive&limit=50" -b cookies.txt

Creates a folder under the target path. Requires write permission on the target directory.

POST /api/files/folder

FieldTypeRequiredDescription
pathstringYesThe parent directory path.
namestringYesThe folder name, at most 255 characters.

Returns the created folder with status 201.

{
"success": true,
"data": {
"file": {
"id": "uuid",
"name": "photos",
"path": "/drive",
"type": "folder",
"size": 0,
"hasPassword": false,
"createdAt": 1710000000000
}
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The name or path is invalid.Correct the input and retry.
FORBIDDEN403The caller lacks write permission.Check the permission rules.
NOT_FOUND404The parent path is not mounted.Confirm the path.
ALREADY_EXISTS409A file or folder with the same name already exists.Choose a different name.
curl -X POST https://{domain}/api/files/folder \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"path":"/drive","name":"photos"}'

Returns a single file or folder with its permissions, access mode, and password status.

GET /api/files/{id}

FieldTypeRequiredDescription
idstringYesThe file identifier.
{
"success": true,
"data": {
"file": {
"id": "uuid",
"name": "photo.jpg",
"path": "/drive/photos",
"type": "file",
"size": 1048576,
"mimeType": "image/jpeg",
"hasPassword": false,
"createdAt": 1710000000000
},
"mount": { "id": "mount-id", "name": "Drive", "sortBy": "name", "sortOrder": "asc" },
"permissions": ["read", "write", "update", "delete", "share", "download"],
"accessMode": "public_cdn",
"hasPassword": false
},
"timestamp": 1710000000000
}

The accessMode value is public_cdn for files served by a public CDN domain, signed_redirect for pre-signed URL providers, or private_gateway for the Worker download gateway.

Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The file or mount does not exist.Confirm the identifier.
FORBIDDEN403The caller lacks read permission.Check the permission rules.
curl https://{domain}/api/files/{id} -b cookies.txt

Renames a file or folder and updates its metadata, including the access password and display options.

PUT /api/files/{id}

FieldTypeRequiredDescription
idstringYesThe file identifier.
FieldTypeRequiredDescription
namestringNoThe new name. Renaming requires the update permission.
customTitlestringNoA custom display title, at most 200 characters.
customColorstringNoA custom accent color in #RRGGBB format.
coverUrlstringNoA cover image URL.
iconEmojistringNoAn icon emoji, at most 16 characters.
accessPasswordstringNoA new access password, or null to remove it. The server stores only a hash.
manualPositionintegerNoThe manual sort position.

Returns the updated file.

{
"success": true,
"data": {
"file": {
"id": "uuid",
"name": "renamed.jpg",
"path": "/drive/photos",
"type": "file",
"size": 1048576,
"hasPassword": true,
"updatedAt": 1710000000000
}
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400A field is invalid.Correct the field and retry.
FORBIDDEN403The caller lacks the update permission.Check the permission rules.
ALREADY_EXISTS409The new name collides with an existing entry.Choose a different name.
NOT_FOUND404The file does not exist.Confirm the identifier.
curl -X PUT https://{domain}/api/files/{id} \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"name":"renamed.jpg","accessPassword":"my-pass"}'

Verifies the access password of a protected file and returns a short-lived gateway download URL.

POST /api/files/{id}/verify-password

FieldTypeRequiredDescription
idstringYesThe file identifier.
FieldTypeRequiredDescription
passwordstringYesThe file access password.
{
"success": true,
"data": { "url": "https://{domain}/api/gateway/download/{token}", "expiresIn": 900 },
"timestamp": 1710000000000
}

The returned URL stays valid for 900 seconds and works once.

Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The file has no password, or the password is missing.Provide a password or skip verification.
INVALID_PASSWORD401The password is wrong.Re-enter the password.
curl -X POST https://{domain}/api/files/{id}/verify-password \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"password":"my-pass"}'

Returns a single-use gateway download URL for the file. The file must not be password-protected.

GET /api/files/{id}/download

FieldTypeRequiredDescription
idstringYesThe file identifier.
{
"success": true,
"data": { "url": "https://{domain}/api/gateway/download/{token}", "expiresAt": 1710000900000 },
"timestamp": 1710000000000
}

The token expires 15 minutes after issue, and the gateway consumes it on the first download.

Error CodeHTTP StatusCauseRecommended Action
PASSWORD_REQUIRED403The file is password-protected.Call the password-verify endpoint first.
FORBIDDEN403The caller lacks the download permission.Check the permission rules.
NOT_FOUND404The file does not exist.Confirm the identifier.
curl https://{domain}/api/files/{id}/download -b cookies.txt

Returns the file URL in four formats: direct, HTML, Markdown, and BBCode. By default the direct URL points to the public path of the file, such as {origin}/drive/photos/photo.jpg. Pass signed=true to request a pre-signed URL from the provider, with the gateway URL as the fallback.

GET /api/files/{id}/copy-links?signed={signed}&expiresIn={expiresIn}

FieldTypeRequiredDescription
idstringYesThe file identifier.
FieldTypeRequiredDescription
signedbooleanNoWhen true, request a signed URL. Defaults to false.
expiresInintegerNoThe signed URL lifetime in seconds, from 60 to 604800. Defaults to 3600. Used only with signed=true.
{
"success": true,
"data": {
"formats": {
"direct": "https://{domain}/drive/photos/photo.jpg",
"html": "<img src=\"https://{domain}/drive/photos/photo.jpg\" alt=\"photo.jpg\">",
"markdown": "![photo.jpg](https://{domain}/drive/photos/photo.jpg)",
"bbcode": "[img]https://{domain}/drive/photos/photo.jpg[/img]"
},
"accessMode": "public_path",
"needsPassword": false,
"expiresIn": null
},
"timestamp": 1710000000000
}

When you set signed=true, accessMode becomes signed and expiresIn returns the signed URL lifetime.

Error CodeHTTP StatusCauseRecommended Action
FORBIDDEN403The caller lacks the download permission.Check the permission rules.
NOT_FOUND404The file does not exist.Confirm the identifier.
curl "https://{domain}/api/files/{id}/copy-links?signed=true&expiresIn=3600" -b cookies.txt

Hard-deletes a file or folder. The metadata and quota update in a single transaction, and cleanup of the object runs asynchronously.

DELETE /api/files/{id}

FieldTypeRequiredDescription
idstringYesThe file or folder identifier.
{
"success": true,
"data": { "deleted": 1, "size": 1048576 },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
FORBIDDEN403The caller lacks the delete permission.Check the permission rules.
NOT_FOUND404The file does not exist.Confirm the identifier.
curl -X DELETE https://{domain}/api/files/{id} \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt

Moves or renames a file or folder asynchronously. The move uses a Saga: it copies the object, verifies the copy, switches the metadata atomically, then cleans up the source. Requires the delete permission on the source and the write permission on the target.

POST /api/files/{id}/move

FieldTypeRequiredDescription
idstringYesThe file or folder identifier.
FieldTypeRequiredDescription
targetPathstringYesThe target directory path.
newNamestringNoAn optional new name for the entry.

Returns the job identifier and its initial status.

{
"success": true,
"data": { "jobId": "job-uuid", "status": "pending" },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The target path is missing or invalid.Provide a target path.
FORBIDDEN403The caller lacks the required permissions.Check the permission rules.
NOT_FOUND404The file does not exist.Confirm the identifier.
OPERATION_FAILED409A conflict or a cycle would result.Choose another target.
curl -X POST https://{domain}/api/files/{id}/move \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"targetPath":"/drive/archive"}'

Returns the status of an asynchronous operation, such as a move. Only the job owner can read it.

GET /api/files/jobs/{jobId}

FieldTypeRequiredDescription
jobIdstringYesThe job identifier from a move request.
{
"success": true,
"data": {
"job": {
"id": "job-uuid",
"type": "move",
"status": "running",
"progress": 40,
"errorMessage": null,
"createdAt": 1710000000000,
"completedAt": null
}
},
"timestamp": 1710000000000
}

The status value is pending, running, completed, or failed. progress is a percentage from 0 to 100.

Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The job does not exist or belongs to another user.Confirm the job identifier.
curl https://{domain}/api/files/jobs/{jobId} -b cookies.txt

Runs a delete or move operation over up to 100 files and folders. Each item runs independently, and the response lists the successful and failed items.

POST /api/files/batch

FieldTypeRequiredDescription
actionstringYesdelete or move.
fileIdsarrayYesThe file or folder identifiers, 1 to 100 items.
targetPathstringNoThe target directory. Required for the move action.
{
"success": true,
"data": {
"succeeded": ["uuid-1", "uuid-2"],
"failed": [{ "id": "uuid-3", "error": "文件不存在" }]
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The batch parameters are invalid.Correct the parameters and retry.
curl -X POST https://{domain}/api/files/batch \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"action":"delete","fileIds":["uuid-1","uuid-2"]}'

Upload endpoints create an upload session, stream the object through the Worker or a pre-signed URL, and commit the file atomically. Sessions expire after one hour. Files larger than 100 MB use multipart upload automatically.

Creates an upload session and atomically reserves quota. Files larger than 100 MB, or sessions with a partCount greater than 1, switch to multipart upload.

POST /api/files/upload-session

FieldTypeRequiredDescription
pathstringYesThe target directory path.
fileNamestringYesThe file name, at most 255 characters.
fileSizeintegerYesThe file size in bytes, up to 20 GB.
mimeTypestringNoThe MIME type of the file.
partCountintegerNoThe number of parts. Values greater than 1 enable multipart upload.
idempotencyKeystringNoA client-supplied key to resume a completed session.
{
"success": true,
"data": {
"sessionId": "session-uuid",
"uploadUrl": "https://provider.example.com/presigned-upload",
"uploadId": null,
"uploadMode": "presigned",
"totalParts": null,
"parts": [],
"expiresAt": 1710003600000,
"expiresIn": 3600
},
"timestamp": 1710000000000
}

When the provider supports multipart pre-signed URLs, the response also returns parts, an array of { partNumber, url } entries for direct concurrent upload. Otherwise uploadMode is worker and you stream parts through the Worker.

Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400A request field is invalid.Correct the field and retry.
FORBIDDEN403The caller lacks the write permission.Check the permission rules.
QUOTA_EXCEEDED413The storage or file-count quota has run out.Free up space or raise the quota.
NOT_FOUND404The target mount does not exist.Confirm the path.
curl -X POST https://{domain}/api/files/upload-session \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"path":"/drive/photos","fileName":"photo.jpg","fileSize":1048576,"mimeType":"image/jpeg"}'

Uploads the file bytes directly through the Worker for a single-file session. Send the file bytes as the request body.

PUT /api/files/upload/raw/{sessionId}

FieldTypeRequiredDescription
sessionIdstringYesThe session identifier from the upload session.
FieldTypeRequiredDescription
Content-TypestringNoThe MIME type of the file. Defaults to the session MIME type.

Returns the object ETag and size after verification.

{
"success": true,
"data": { "etag": "\"abc123\"", "size": 1048576 },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The session does not exist or belongs to another user.Start a new session.
OPERATION_FAILED409The session is not in a pending state.Start a new session.
UPLOAD_SESSION_EXPIRED410The session exceeded its one-hour lifetime.Start a new session.
OPERATION_FAILED422The object size does not match the session.Re-upload the file.
curl -X PUT https://{domain}/api/files/upload/raw/{sessionId} \
-H "Content-Type: image/jpeg" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
--data-binary @photo.jpg

Uploads one part of a multipart session through the Worker. The part size is 8 MB. The server records the returned ETag for resume and completion checks.

PUT /api/files/upload/multipart/{sessionId}/part/{partNumber}

FieldTypeRequiredDescription
sessionIdstringYesThe session identifier.
partNumberintegerYesThe part number, starting at 1.
{
"success": true,
"data": { "partNumber": 1, "etag": "\"abc123\"" },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The part number is out of range.Use a valid part number.
NOT_FOUND404The session does not exist.Start a new session.
OPERATION_FAILED409The session is not a multipart session.Use a multipart session.
UPLOAD_SESSION_EXPIRED410The session expired.Start a new session.
curl -X PUT https://{domain}/api/files/upload/multipart/{sessionId}/part/1 \
-H "Content-Type: application/octet-stream" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
--data-binary @part1.bin

Returns the uploaded and missing parts of a multipart session. This endpoint is the source of truth for resuming an interrupted upload; for pre-signed providers it also returns new pre-signed URLs for the missing parts.

GET /api/files/upload/multipart/{sessionId}/parts

FieldTypeRequiredDescription
sessionIdstringYesThe session identifier.
{
"success": true,
"data": {
"sessionId": "session-uuid",
"totalParts": 4,
"completedCount": 2,
"parts": [{ "partNumber": 1, "etag": "\"abc123\"" }],
"missingParts": [2, 3, 4],
"presignedParts": [{ "partNumber": 2, "url": "https://provider.example.com/part-2" }],
"uploadMode": "presigned"
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The session does not exist.Start a new session.
OPERATION_FAILED409The session is not a multipart session.Use a multipart session.
curl https://{domain}/api/files/upload/multipart/{sessionId}/parts -b cookies.txt

Aborts a multipart upload, releases the reserved quota, and marks the session as aborted.

DELETE /api/files/upload/multipart/{sessionId}

FieldTypeRequiredDescription
sessionIdstringYesThe session identifier.

Returns data: null on success.

Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The session does not exist.Start a new session.
curl -X DELETE https://{domain}/api/files/upload/multipart/{sessionId} \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt

Commits the uploaded object. The server verifies the object with a HEAD request, checks the size and ETag, merges multipart parts, then commits the metadata and quota in a single transaction.

POST /api/files/upload-complete

FieldTypeRequiredDescription
sessionIdstringYesThe session identifier.
etagstringNoThe object ETag returned by the raw upload. Required for single-file sessions.
partsarrayNoThe part list { partNumber, etag } for pre-signed direct uploads. Ignored when the server recorded the parts.
{
"success": true,
"data": {
"file": {
"id": "file-uuid",
"name": "photo.jpg",
"path": "/drive/photos",
"size": 1048576,
"createdAt": 1710000000000
}
},
"timestamp": 1710000000000
}

Calling this endpoint again after success returns alreadyCompleted: true with the same result, which makes the completion idempotent.

Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The request body is invalid or an ETag is missing.Provide the session and ETag.
NOT_FOUND404The session does not exist.Start a new session.
OPERATION_FAILED409The session state prevents completion.Start a new session.
UPLOAD_SESSION_EXPIRED410The session expired.Start a new session.
OPERATION_FAILED422The object or parts fail verification.Re-upload the file.
curl -X POST https://{domain}/api/files/upload-complete \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"sessionId":"session-uuid","etag":"\"abc123\""}'

PicGo and PicList upload to the compatible endpoints with a Bearer API key. The key must include the write permission. Both endpoints accept multipart/form-data or a raw body.

POST /api/upload

POST /api/upload/upload

POST /api/compat/upload

FieldTypeRequiredDescription
AuthorizationstringYesBearer {key_id}.{secret}.
Content-TypestringDependsmultipart/form-data, or the file MIME type for a raw body.

For multipart/form-data, provide the file in the file field and an optional path field. For a raw body, set the file name with the X-File-Name header (or the legacy filename header) and an optional target path with the X-Path header.

{
"success": true,
"data": {
"url": "https://{domain}/api/files/{fileId}/download",
"fileId": "file-uuid",
"path": "/uploads/2026/photo.jpg",
"size": 1048576,
"filename": "photo.jpg"
},
"timestamp": 1710000000000
}

The path field honors the key’s upload path template, such as /uploads/{year}/{month}/. The final target must stay inside the key’s upload root.

Error CodeHTTP StatusCauseRecommended Action
UNAUTHORIZED401No valid API key.Provide a Bearer key.
INVALID_TOKEN401The API key is invalid, revoked, or expired.Create a new key.
FORBIDDEN403The key lacks the write permission, or the target is outside the upload root.Grant write permission or adjust the path.
QUOTA_EXCEEDED413The quota has run out.Free up space or raise the quota.
OPERATION_FAILED422The uploaded object fails verification.Re-upload the file.
curl -X POST https://{domain}/api/upload \
-H "Authorization: Bearer pk_xxx.sk_yyy" \
-F "file=@photo.jpg" \
-F "path=/uploads/"

Share endpoints create, list, verify, download, preview, and revoke share links. Creating, listing, and revoking shares require a logged-in session. Reading a share, verifying its password, downloading, and previewing are public.

Creates a share link for a file. Requires the share permission on the file.

POST /api/shares/

FieldTypeRequiredDescription
fileIdstringYesThe file identifier.
titlestringNoA display title. Defaults to the file name.
passwordstringNoA share password. The server stores only a hash.
expiresInintegerNoThe share lifetime in seconds, from 60 to 1 year.
maxViewsintegerNoThe maximum number of views.
maxDownloadsintegerNoThe maximum number of downloads.
allowPreviewbooleanNoAllow in-page preview. Defaults to true.
allowDownloadbooleanNoAllow downloads. Defaults to true.

Returns the share with its short id and public URL with status 201.

{
"success": true,
"data": {
"share": {
"id": "abc123",
"url": "https://{domain}/share/abc123",
"expiresAt": null,
"createdAt": 1710000000000,
"passwordProtected": true,
"allowPreview": true,
"allowDownload": true
}
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
UNAUTHORIZED401No logged-in session.Log in first.
VALIDATION_ERROR400A share parameter is invalid.Correct the parameter and retry.
FORBIDDEN403The caller lacks the share permission.Check the permission rules.
NOT_FOUND404The file does not exist.Confirm the file identifier.
curl -X POST https://{domain}/api/shares/ \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"fileId":"file-uuid","password":"share-pass","expiresIn":604800}'

Returns the shares created by the current user with pagination.

GET /api/shares/?page={page}&limit={limit}&status={status}

FieldTypeRequiredDescription
pageintegerNoThe page number. Defaults to 1.
limitintegerNoItems per page. Defaults to 20, maximum 100.
statusstringNoactive, expired, or revoked.
{
"success": true,
"data": {
"items": [
{
"id": "abc123",
"title": "photo.jpg",
"file": { "id": "file-uuid", "name": "photo.jpg", "path": "/drive/photos", "type": "file", "size": 1048576 },
"expiresAt": null,
"viewCount": 3,
"maxViews": null,
"downloadCount": 1,
"maxDownloads": null,
"allowPreview": true,
"allowDownload": true,
"status": "active",
"createdAt": 1710000000000
}
],
"pagination": { "total": 1, "page": 1, "limit": 20, "pages": 1 }
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
UNAUTHORIZED401No logged-in session.Log in first.
curl "https://{domain}/api/shares/?status=active" -b cookies.txt

Returns public share information without requiring a login. When the share is password-protected, the response includes requiresPassword: true and no file data until the visitor verifies the password.

GET /api/shares/{id}

FieldTypeRequiredDescription
idstringYesThe share id.

For a share without a password, or after password verification:

{
"success": true,
"data": {
"share": {
"id": "abc123",
"title": "photo.jpg",
"creatorName": "alice",
"file": { "id": "file-uuid", "name": "photo.jpg", "path": "/drive/photos", "type": "file", "size": 1048576, "mimeType": "image/jpeg" },
"allowPreview": true,
"allowDownload": true,
"expiresAt": null,
"requiresPassword": false,
"viewCount": 4,
"maxViews": null,
"downloadCount": 1,
"maxDownloads": null
}
},
"timestamp": 1710000000000
}

For a password-protected share that is not yet verified, file, allowPreview, and allowDownload are null or false, and requiresPassword is true.

Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The share does not exist.Confirm the share id.
SHARE_REVOKED410The share link no longer works.Ask the creator for a new link.
SHARE_EXPIRED410The share expired.Ask the creator for a new link.
SHARE_LIMIT_REACHED410The share reached its view limit.Ask the creator to raise the limit.
curl https://{domain}/api/shares/abc123

Verifies the share password. The password is sent in the request body, never in the URL. On success the endpoint sets a short-lived HttpOnly cookie, so subsequent requests to the share do not need the password again.

POST /api/shares/{id}/verify

FieldTypeRequiredDescription
idstringYesThe share id.
FieldTypeRequiredDescription
passwordstringYesThe share password.
{
"success": true,
"data": { "authorized": true },
"timestamp": 1710000000000
}

The endpoint also sets a share_auth_{id} cookie that is valid for 15 minutes.

Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The share does not exist.Confirm the share id.
SHARE_EXPIRED410The share expired.Ask the creator for a new link.
INVALID_PASSWORD401The password is wrong.Re-enter the password.
curl -X POST https://{domain}/api/shares/abc123/verify \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"password":"share-pass"}'

Returns a single-use gateway download URL for the share. The download count increments only when the gateway consumes the token.

GET /api/shares/{id}/download

FieldTypeRequiredDescription
idstringYesThe share id.
{
"success": true,
"data": { "url": "https://{domain}/api/gateway/download/{token}", "expiresIn": 900 },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The share does not exist.Confirm the share id.
SHARE_REVOKED410The share is not active.Ask the creator for a new link.
SHARE_EXPIRED410The share expired.Ask the creator for a new link.
FORBIDDEN403The share does not allow downloads.Ask the creator to enable downloads.
INVALID_PASSWORD401The password is not verified.Call the verify endpoint first.
SHARE_LIMIT_REACHED410The share reached its download limit.Ask the creator to raise the limit.
curl https://{domain}/api/shares/abc123/download -b cookies.txt

Streams the shared image directly with Content-Disposition: inline when the share allows preview. Returns binary image data, not JSON.

GET /api/shares/{id}/preview

FieldTypeRequiredDescription
idstringYesThe share id.
Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The share or the file object does not exist.Confirm the share id.
SHARE_EXPIRED410The share is not active.Ask the creator for a new link.
FORBIDDEN403The share does not allow preview.Ask the creator to enable preview.
INVALID_PASSWORD401The password is not verified.Call the verify endpoint first.
curl https://{domain}/api/shares/abc123/preview -o photo.jpg

Revokes a share. Only the share creator can revoke it.

DELETE /api/shares/{id}

FieldTypeRequiredDescription
idstringYesThe share id.

Returns data: null on success.

Error CodeHTTP StatusCauseRecommended Action
UNAUTHORIZED401No logged-in session.Log in first.
NOT_FOUND404The share does not exist or belongs to another user.Confirm the share id.
curl -X DELETE https://{domain}/api/shares/abc123 \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt

User endpoints read and update the current user’s settings, password, and email.

Returns the profile, appearance, and quota of the current user.

GET /api/users/me/settings

{
"success": true,
"data": {
"profile": {
"username": "alice",
"email": "alice@example.com",
"emailVerified": true,
"displayName": "Alice",
"avatarUrl": null,
"defaultPath": "/",
"locale": "zh-CN",
"role": "user",
"createdAt": 1710000000000
},
"appearance": { "theme": "system", "accentColor": "#3B82F6", "enableBlur": true },
"quota": { "maxStorage": 10737418240, "usedStorage": 1048576, "maxFiles": 1000, "usedFiles": 3 }
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The user does not exist.Log in with a valid account.
curl https://{domain}/api/users/me/settings -b cookies.txt

Updates the profile and appearance of the current user.

PUT /api/users/me/settings

FieldTypeRequiredDescription
displayNamestringNoThe display name, at most 100 characters. Use null to clear.
avatarUrlstringNoAn avatar URL, at most 1000 characters. Use null to clear.
localestringNozh-CN or en-US.
themestringNolight, dark, or system.
defaultPathstringNoThe default directory, starting with /.
{
"success": true,
"data": { "message": "已保存" },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400A setting is invalid, or defaultPath does not start with /.Correct the setting and retry.
curl -X PUT https://{domain}/api/users/me/settings \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"displayName":"Alice","theme":"dark","defaultPath":"/drive"}'

Changes the password of the current user. The change revokes all existing sessions, so the user must log in again.

PUT /api/users/me/password

FieldTypeRequiredDescription
oldPasswordstringYesThe current password.
newPasswordstringYesThe new password, at least 8 characters.
{
"success": true,
"data": { "message": "密码已修改,请重新登录" },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The new password is shorter than 8 characters.Choose a longer password.
INVALID_PASSWORD401The current password is wrong.Re-enter the old password.
curl -X PUT https://{domain}/api/users/me/password \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"oldPassword":"secret-pass","newPassword":"new-pass-123"}'

Sends a 6-digit verification code to the given email. The code is valid for 5 minutes. The site must enable email service.

POST /api/users/me/email/send-otp

FieldTypeRequiredDescription
emailstringYesThe email address to verify.
{
"success": true,
"data": { "success": true, "expiresIn": 300 },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The email is invalid or already used by another account.Use a different email.
MAIL_ERROR500The email service failed to send the code.Try again later or contact the administrator.
curl -X POST https://{domain}/api/users/me/email/send-otp \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"email":"new@example.com"}'

Verifies the 6-digit code and updates the user email. After 5 wrong attempts, the code stops working.

POST /api/users/me/email/verify-otp

FieldTypeRequiredDescription
emailstringYesThe email address to verify.
codestringYesThe 6-digit code from the email.
{
"success": true,
"data": { "success": true },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The code is invalid, expired, or has too many failed attempts.Send a new code and retry.
curl -X POST https://{domain}/api/users/me/email/verify-otp \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"email":"new@example.com","code":"123456"}'

API key endpoints create, list, and revoke keys. Keys authenticate PicGo, PicList, scripts, and WebDAV clients. A user can have up to 20 active keys.

Creates an API key. The full token is shown only once, so store it before you close the response. The server stores only a SHA-256 hash of the token.

POST /api/keys/

FieldTypeRequiredDescription
namestringYesA label for the key, at most 100 characters.
permissionsarrayYesread, write, or delete. At least one permission.
protocolsarrayYeswebdav or api. At least one protocol.
uploadPathstringNoThe upload root for writes. Defaults to /uploads. Normalized; the server rejects paths containing .. or ~.
allowedIpsarrayNoAn IP whitelist. The server rejects requests from other IPs.
expiresInintegerNoThe key lifetime in seconds, at least 60.

Returns the key and ready-to-use client configuration with status 201.

{
"success": true,
"data": {
"key": {
"id": "pk_abcdef123456",
"keyId": "pk_abcdef123456",
"secret": "sk_xyz789",
"fullToken": "pk_abcdef123456.sk_xyz789",
"name": "picgo",
"permissions": ["write"],
"protocols": ["api", "webdav"],
"uploadPath": "/uploads",
"createdAt": 1710000000000,
"expiresAt": null
},
"configs": {
"bearer": {
"url": "https://{domain}",
"header": "Authorization: Bearer pk_abcdef123456.sk_xyz789"
},
"webdav": {
"url": "https://{domain}/webdav",
"username": "pk_abcdef123456",
"password": "sk_xyz789"
}
}
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400A request field is invalid.Correct the field and retry.
FORBIDDEN403The user already has 20 active keys, or the upload path is invalid.Revoke an old key or fix the path.
curl -X POST https://{domain}/api/keys/ \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"name":"picgo","permissions":["write"],"protocols":["api"],"uploadPath":"/uploads"}'

Lists the keys of the current user without the secret.

GET /api/keys/

{
"success": true,
"data": {
"keys": [
{
"id": "key-uuid",
"name": "picgo",
"keyId": "pk_abcdef123456",
"permissions": ["write"],
"protocols": ["api"],
"uploadPath": "/uploads",
"lastUsedAt": null,
"expiresAt": null,
"createdAt": 1710000000000,
"status": "active"
}
]
},
"timestamp": 1710000000000
}
curl https://{domain}/api/keys/ -b cookies.txt

Returns the path rules that apply to the current user’s keys. Use this endpoint for display and debugging.

GET /api/keys/rules

{
"success": true,
"data": { "rules": [] },
"timestamp": 1710000000000
}
curl https://{domain}/api/keys/rules -b cookies.txt

Revokes a key so it can no longer authenticate.

DELETE /api/keys/{id}

FieldTypeRequiredDescription
idstringYesThe key identifier.

Returns data: null on success.

Error CodeHTTP StatusCauseRecommended Action
NOT_FOUND404The key does not exist or belongs to another user.Confirm the key identifier.
curl -X DELETE https://{domain}/api/keys/{id} \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt

Admin endpoints manage users, shares, files, logs, settings, announcements, storage providers, mounts, and permission rules. All of them require an administrator session.

Returns aggregate statistics, recent activity, and the request count for the last 24 hours.

GET /api/admin/dashboard

{
"success": true,
"data": {
"stats": {
"users": 12,
"files": 345,
"storage": [{ "providerId": "provider-uuid", "name": "R2", "usedSpace": 10485760, "fileCount": 300 }]
},
"requests24h": 1200,
"recentActivity": [{ "action": "upload", "path": "/drive/a.txt", "userId": "user-uuid", "createdAt": 1710000000000 }]
},
"timestamp": 1710000000000
}
curl https://{domain}/api/admin/dashboard -b cookies.txt

Returns user, file, and storage statistics.

GET /api/admin/stats

{
"success": true,
"data": {
"users": 12,
"files": 345,
"storage": [{ "providerId": "provider-uuid", "name": "R2", "usedSpace": 10485760, "fileCount": 300 }],
"recentActivity": []
},
"timestamp": 1710000000000
}
curl https://{domain}/api/admin/stats -b cookies.txt

Lists, updates, and deletes users.

GET /api/admin/users?page={page}&limit={limit}&role={role}&status={status}&search={search}

PUT /api/admin/users/{id}

DELETE /api/admin/users/{id}

FieldTypeRequiredDescription
pageintegerNoThe page number. Defaults to 1.
limitintegerNoItems per page. Defaults to 20, maximum 100.
rolestringNoFilter by role.
statusstringNoFilter by status.
searchstringNoA keyword to match users.
FieldTypeRequiredDescription
rolestringNoadmin, user, or guest.
statusstringNoactive, disabled, or banned. Disabling or banning revokes the user sessions.
defaultPathstringNoThe default directory, starting with /.
maxStorageintegerNoThe storage quota in bytes.
maxFilesintegerNoThe file-count quota.
{
"success": true,
"data": {
"users": [{ "id": "user-uuid", "username": "alice", "role": "user", "status": "active", "quota": {} }],
"pagination": { "total": 12, "page": 1, "limit": 20, "pages": 1 }
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400A user parameter is invalid, or the request tries to delete the own account.Correct the parameter.
NOT_FOUND404The user does not exist.Confirm the user id.
curl "https://{domain}/api/admin/users?role=user&limit=20" -b cookies.txt
curl -X PUT https://{domain}/api/admin/users/{id} \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"role":"admin","status":"active"}'

Lists all shares and revokes any share.

GET /api/admin/shares?page={page}&limit={limit}&status={status}

DELETE /api/admin/shares/{id}

FieldTypeRequiredDescription
pageintegerNoThe page number. Defaults to 1.
limitintegerNoItems per page. Defaults to 20, maximum 100.
statusstringNoFilter by share status.
{
"success": true,
"data": {
"items": [{ "id": "abc123", "title": "photo.jpg", "creatorId": "user-uuid", "status": "active" }],
"pagination": { "total": 5, "page": 1, "limit": 20, "pages": 1 }
},
"timestamp": 1710000000000
}
curl "https://{domain}/api/admin/shares?status=active" -b cookies.txt
curl -X DELETE https://{domain}/api/admin/shares/abc123 \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt

Searches every file across all mounts.

GET /api/admin/files?page={page}&limit={limit}&search={search}

FieldTypeRequiredDescription
pageintegerNoThe page number. Defaults to 1.
limitintegerNoItems per page. Defaults to 20, maximum 100.
searchstringNoA keyword to match file names.
{
"success": true,
"data": {
"items": [{ "id": "file-uuid", "name": "photo.jpg", "path": "/drive/photos", "type": "file", "size": 1048576 }],
"pagination": { "total": 345, "page": 1, "limit": 20, "pages": 18 }
},
"timestamp": 1710000000000
}
curl "https://{domain}/api/admin/files?search=photo" -b cookies.txt

Returns access logs with pagination and filtering.

GET /api/admin/logs?page={page}&limit={limit}&userId={userId}&action={action}&search={search}&from={from}&to={to}

FieldTypeRequiredDescription
pageintegerNoThe page number. Defaults to 1.
limitintegerNoItems per page. Defaults to 50, maximum 200.
userIdstringNoFilter by user.
actionstringNoFilter by action, such as upload or download.
searchstringNoA keyword to match log content.
fromintegerNoStart time in milliseconds.
tointegerNoEnd time in milliseconds.
{
"success": true,
"data": {
"logs": [{ "id": "log-uuid", "userId": "user-uuid", "action": "upload", "path": "/drive/a.txt", "bytesTransferred": 1024, "statusCode": 200, "createdAt": 1710000000000 }],
"pagination": { "total": 5000, "page": 1, "limit": 50, "pages": 100 }
},
"timestamp": 1710000000000
}
curl "https://{domain}/api/admin/logs?action=upload&limit=50" -b cookies.txt

Reads and updates the global site settings, including registration, email, rate limits, and Turnstile.

GET /api/admin/settings

PATCH /api/admin/settings

All fields are optional.

FieldTypeRequiredDescription
siteTitlestringNoThe site title.
siteLogostringNoThe site logo URL.
siteFaviconstringNoThe favicon URL.
allowRegistrationbooleanNoAllow new user registration.
allowGuestAccessbooleanNoAllow guest access.
requireEmailVerificationbooleanNoRequire email verification on registration.
enableTurnstilebooleanNoEnable Cloudflare Turnstile.
turnstileSiteKeystringNoThe Turnstile site key.
rateLimitEnabledbooleanNoEnable rate limiting.
rateLimitRequestsPerMinuteintegerNoRequests per minute, 1 to 10000.
smtpHoststringNoThe SMTP host.
smtpPortintegerNoThe SMTP port.
smtpSecurebooleanNoUse a secure SMTP connection.
smtpUserstringNoThe SMTP user.
smtpPasswordstringNoThe SMTP password. Pass "******" or an empty string to keep the current password.
smtpFromNamestringNoThe sender name.
smtpFromEmailstringNoThe sender email.
emailEnabledbooleanNoEnable email service.
curl -X PATCH https://{domain}/api/admin/settings \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"siteTitle":"My Picumet","rateLimitRequestsPerMinute":60}'

Sends a test email through the configured SMTP service.

POST /api/admin/settings/test-email

FieldTypeRequiredDescription
tostringYesThe recipient email address.
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The email is invalid or SMTP is not configured.Configure SMTP first.
MAIL_ERROR500The email service failed.Check the SMTP configuration.
curl -X POST https://{domain}/api/admin/settings/test-email \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"to":"admin@example.com"}'

Lists, creates, updates, and deletes announcements.

GET /api/admin/announcements

POST /api/admin/announcements

PUT /api/admin/announcements/{id}

DELETE /api/admin/announcements/{id}

FieldTypeRequiredDescription
titlestringYesThe announcement title, at most 200 characters.
contentstringYesThe announcement content, at most 5000 characters.
levelstringNoinfo, warning, or danger.
expiresInintegerNoThe announcement lifetime in seconds, at least 60.
curl -X POST https://{domain}/api/admin/announcements \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"title":"Maintenance","content":"Downtime on Saturday","level":"warning"}'

Lists, creates, updates, tests, and deletes storage providers. The server stores credentials encrypted.

GET /api/admin/storage/providers

POST /api/admin/storage/providers

PUT /api/admin/storage/providers/{id}

POST /api/admin/storage/providers/{id}/test

DELETE /api/admin/storage/providers/{id}

FieldTypeRequiredDescription
namestringYesA label for the provider.
typestringYesr2, s3, or oracle.
endpointstringNoThe S3 endpoint. Leave empty for a bound R2 provider. Public http(s) addresses only.
regionstringNoThe region. Defaults to auto for R2.
bucketstringYesThe bucket name.
accessKeyIdstringNoThe access key. Leave empty for a bound R2 provider.
secretAccessKeystringNoThe secret key. Leave empty for a bound R2 provider.
publicDomainstringNoA public CDN domain for direct URLs.
uploadDomainstringNoA custom upload domain.
pathPrefixstringNoA prefix applied to object keys.
{
"success": true,
"data": { "connected": true, "message": "ok" },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The endpoint fails SSRF checks or a field is invalid.Use a public endpoint or correct the field.
NOT_FOUND404The provider does not exist.Confirm the provider id.
OPERATION_FAILED409The provider still has mounts.Delete the mounts first.
curl -X POST https://{domain}/api/admin/storage/providers \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"name":"R2","type":"r2","bucket":"my-bucket"}'

Lists, creates, updates, and deletes mount points that bind a provider to a virtual path.

GET /api/admin/mounts

POST /api/admin/mounts

PUT /api/admin/mounts/{id}

DELETE /api/admin/mounts/{id}

FieldTypeRequiredDescription
providerIdstringYesThe storage provider id.
mountPathstringYesThe virtual path, such as /drive.
namestringYesA display name.
sortBystringNoThe default sort field.
sortOrderstringNoasc or desc.
priorityintegerNoThe mount priority. Higher values win for overlapping paths.
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The provider does not exist or a field is invalid.Correct the input.
OPERATION_FAILED409The mount still contains files.Delete the files first.
curl -X POST https://{domain}/api/admin/mounts \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"providerId":"provider-uuid","mountPath":"/drive","name":"Drive"}'

Lists, creates, updates, and deletes path-based permission rules. Each rule targets exactly one subject: a role, a user, or an API key.

GET /api/admin/rules?page={page}&limit={limit}

POST /api/admin/rules

PUT /api/admin/rules/{id}

DELETE /api/admin/rules/{id}

FieldTypeRequiredDescription
pathPatternstringYesThe path pattern, such as /public/**.
effectstringYesallow or deny.
mountIdstringNoThe mount this rule applies to. Empty means all mounts.
rolestringNoThe role subject. Mutually exclusive with userId and apiKeyId.
userIdstringNoThe user subject. Mutually exclusive with role and apiKeyId.
apiKeyIdstringNoThe API key subject. Mutually exclusive with role and userId.
permissionsarrayNoThe permissions the rule grants, such as ["read","write"].
requirePasswordbooleanNoRequire a password on the matched paths.
passwordstringNoThe plaintext password. The server stores only a hash.
allowedIpsarrayNoAn IP whitelist for the rule.
priorityintegerNoThe rule priority.

The list response masks passwordHash values.

Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The rule must specify exactly one of role, userId, or apiKeyId.Set exactly one subject.
curl -X POST https://{domain}/api/admin/rules \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-d '{"pathPattern":"/public/**","effect":"allow","role":"guest","permissions":["read"]}'

Free-mode endpoints let an anonymous visitor use their own object-storage credentials in a temporary session. The server encrypts credentials and stores them in KV for a short TTL. The session endpoints require the fm_token cookie, and write operations require a session-level CSRF token.

Validates the storage endpoint, tests the connection, and starts a temporary session. Sets the fm_token cookie.

POST /api/free-mode/init

FieldTypeRequiredDescription
typestringYesr2, s3, or oracle.
endpointstringYesThe S3 endpoint. Public http(s) addresses only; the server rejects private or local addresses.
regionstringNoThe region.
bucketstringYesThe bucket name.
accessKeyIdstringYesThe access key.
secretAccessKeystringYesThe secret key.
sessionHoursintegerNoThe session lifetime in hours, 1 to 8. Defaults to 1.

Returns the session user, expiry, and a session-level CSRF token with status 201.

{
"success": true,
"data": {
"user": { "id": "user-uuid", "username": "fm_ab12cd34", "role": "user", "defaultPath": "/" },
"expiresAt": 1710003600000,
"sessionHours": 1,
"provider": { "type": "s3", "bucket": "my-bucket" },
"csrfToken": "32-char-random-string"
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
VALIDATION_ERROR400The endpoint is private or invalid, or a field is malformed.Use a public endpoint and valid credentials.
OPERATION_FAILED400The storage connection test failed.Check the credentials and endpoint.
curl -X POST https://{domain}/api/free-mode/init \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"type":"s3","endpoint":"https://s3.example.com","bucket":"my-bucket","accessKeyId":"AK","secretAccessKey":"SK","sessionHours":1}'

Lists the objects in the free-mode session root.

GET /api/free-mode/files?path={path}

FieldTypeRequiredDescription
pathstringNoThe directory prefix to list. Must stay inside the session root.
{
"success": true,
"data": {
"items": [
{ "key": "photo.jpg", "name": "photo.jpg", "path": "/photo.jpg", "type": "file", "size": 1048576, "etag": "\"abc123\"" }
],
"mount": { "id": "free", "name": "自由模式", "mountPath": "/", "sortBy": "name", "sortOrder": "asc" }
},
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
UNAUTHORIZED401The session is missing or expired.Initialize a new session.
FORBIDDEN403The path is outside the session root.Use a path inside the root.
curl "https://{domain}/api/free-mode/files" -b cookies.txt

Uploads a file to the free-mode session. The file must be 1 GB or smaller.

POST /api/free-mode/upload?path={path}

FieldTypeRequiredDescription
pathstringNoThe target directory prefix.
FieldTypeRequiredDescription
X-CSRF-TokenstringYesThe session CSRF token from the init response.
X-File-NamestringOnly for raw bodiesThe file name, used when the body is not multipart/form-data.

Use multipart/form-data with a file field, or send the raw file bytes with the X-File-Name header.

Returns the object key and size with status 201.

{
"success": true,
"data": { "key": "photo.jpg", "size": 1048576 },
"timestamp": 1710000000000
}
Error CodeHTTP StatusCauseRecommended Action
UNAUTHORIZED401The session is missing or expired.Initialize a new session.
INVALID_CSRF403The session CSRF token is wrong.Use the token from the init response.
VALIDATION_ERROR400The file name or path is invalid.Correct the input.
PAYLOAD_TOO_LARGE413The file exceeds 1 GB.Use a smaller file.
FORBIDDEN403The target key is outside the session root.Use a path inside the root.
curl -X POST "https://{domain}/api/free-mode/upload" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt \
-F "file=@photo.jpg"

Deletes an object from the free-mode session.

DELETE /api/free-mode/object?key={key}

FieldTypeRequiredDescription
keystringYesThe object key. Must stay inside the session root and contain no .., ~, control characters, or backslashes.

Returns data: null on success.

Error CodeHTTP StatusCauseRecommended Action
UNAUTHORIZED401The session is missing or expired.Initialize a new session.
VALIDATION_ERROR400The key is missing, too long, or contains illegal characters.Correct the key.
FORBIDDEN403The key is outside the session root.Use a key inside the root.
curl -X DELETE "https://{domain}/api/free-mode/object?key=photo.jpg" \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt

Ends the free-mode session and clears the fm_token cookie.

POST /api/free-mode/logout

Returns data: null on success.

curl -X POST https://{domain}/api/free-mode/logout \
-H "X-CSRF-Token: {csrf_token}" \
-b cookies.txt

The WebDAV service exposes the file store to standard WebDAV clients. Every request authenticates with Basic auth using the API key pair: username {key_id}, password {secret}. The base URL is https://{domain}/webdav. All methods enforce path-level permissions, and write targets must stay inside the key’s upload root.

MethodDescriptionPermission
OPTIONSAnnounces DAV capabilities (DAV: 1,2).None
PROPFINDLists a directory as an XML multistatus response.read
MKCOLCreates a folder.write, inside the upload root
PUTUploads a file with a streaming body.write, inside the upload root
GET / HEADDownloads a file or reads its information.read
DELETEDeletes a file or folder.delete
MOVEMoves or renames an entry. Uses the move Saga.delete on source, write on target
curl -X PROPFIND https://{domain}/webdav/ \
-u "pk_xxx:sk_yyy" \
-H "Depth: 1"
curl -X PUT https://{domain}/webdav/uploads/photo.jpg \
-u "pk_xxx:sk_yyy" \
-H "Content-Type: image/jpeg" \
--data-binary @photo.jpg
curl -X MOVE https://{domain}/webdav/uploads/photo.jpg \
-u "pk_xxx:sk_yyy" \
-H "Destination: https://{domain}/webdav/uploads/renamed.jpg"

The gateway streams an object after consuming a single-use download token. Tokens stay valid for 15 minutes, and the gateway consumes each token atomically, so concurrent requests cannot reuse a token.

GET /api/gateway/download/{token}

FieldTypeRequiredDescription
tokenstringYesThe single-use download token from a download or share endpoint.

The gateway streams the object with the stored content type and an appropriate Content-Disposition. For password-protected files, the token must carry password verification. For shares, the gateway increments the download count once.

Error CodeHTTP StatusCauseRecommended Action
INVALID_TOKEN401The token is invalid or expired.Generate a new download link.
PASSWORD_REQUIRED403The file is password-protected and the token is not verified.Verify the file password first.
NOT_FOUND404The file or object no longer exists.Confirm the file still exists.
SHARE_LIMIT_REACHED410The share reached its download limit.Ask the creator to raise the limit.
curl -L "https://{domain}/api/gateway/download/{token}" -o photo.jpg

Public endpoints require no authentication.

Returns site-wide settings for the landing page and login screen.

GET /api/public/settings

{
"success": true,
"data": {
"siteTitle": "Picumet",
"siteLogo": null,
"siteFavicon": null,
"allowGuestAccess": false,
"allowRegistration": true,
"requireEmailVerification": false
},
"timestamp": 1710000000000
}
curl https://{domain}/api/public/settings

Returns the currently active announcements.

GET /api/public/announcements

{
"success": true,
"data": { "items": [{ "id": "announcement-uuid", "title": "Maintenance", "content": "Downtime on Saturday", "level": "warning" }] },
"timestamp": 1710000000000
}
curl https://{domain}/api/public/announcements

Returns service health. The liveness probe always reports ok. The readiness probe reports 503 with ready: false until the database seed completes.

GET /api/public/health

GET /api/public/health/live

GET /api/public/health/ready

{ "service": "picumet-api", "status": "ok", "ready": true, "detail": "seeded" }
curl https://{domain}/api/public/health/ready

The API serves files directly from their public virtual path, for example GET https://{domain}/drive/photos/photo.jpg. This route runs after all API and WebDAV routes, so it never shadows them.

Files on a public mount serve with no authentication. Files on a private mount require a logged-in session with download permission. Password-protected files return 403 PASSWORD_REQUIRED. Paths that are not mounted, or that contain .., return 404.

curl "https://{domain}/drive/photos/photo.jpg" -o photo.jpg