API reference
Authentication, error format, and shared conventions of the ImgYoung HTTP API; per-group endpoint details are generated from the OpenAPI document.
Where the API document comes from
api/openapi.yaml in the repository (OpenAPI 3.1) is the single source of truth for ImgYoung's HTTP API. The group
pages in this section are generated from it; the end-to-end tests check the status code and body of every /api/,
/healthz, and /i/ response against it; and the admin console's frontend types are generated from it too.
The summaries and descriptions in that document are written in Chinese, so the generated group pages are in Chinese as well.
The document is compiled into the binary, and a running instance serves a copy matching its own version at
GET /api/openapi.yaml. You can import it directly into Swagger UI, Scalar, or a code generator:
curl -o openapi.yaml https://img.example.com/api/openapi.yamlThe S3 protocol surface (/s3/…) follows the AWS S3 specification, returns XML error bodies, and is not part of this
reference; see Upload clients for how to use it.
Authentication
There are exactly two authentication surfaces, and their credentials are not interchangeable:
| Data plane | Admin console | |
|---|---|---|
| Paths | POST /api/upload, POST /api/upload/url | /api/admin/… |
| Credential | An upload token in the Authorization: Bearer <token> header | The session cookie imgyoung_session; state-changing requests also send an X-CSRF-Token header |
| How to obtain | Issue one on the admin console's "Upload tokens" page; the plaintext is shown only once | POST /api/admin/auth/login |
| Not authenticated | 401: the token is invalid, expired, or revoked | 401: not logged in, or the session expired |
| Not permitted | 403: the token lacks upload permission | 403: CSRF token missing or wrong; still on the factory default password; administrator required |
Upload tokens look like imgy_…. They only authorize the data plane; no admin console endpoint accepts them. The image
access endpoints (/i/…, /o/…), /healthz, and /api/openapi.yaml need no authentication.
Sessions and the CSRF token
Log in: POST /api/admin/auth/login with the body {"name": "<login name>", "password": "<password>"}. On success
the response sets the session cookie, and the body is {"user": {…}, "csrf_token": "…"}.
Call endpoints: send the cookie with every request. GET, HEAD, and OPTIONS need only the cookie; every other
method must also echo the csrf_token from login in the X-CSRF-Token header, or it gets a 403.
Restore the session state: if you lose the csrf_token (after a page reload, say), GET /api/admin/me returns the
current user and the same csrf_token.
# Log in, saving the cookie to jar.txt; note csrf_token in the response body
curl -c jar.txt -H 'Content-Type: application/json' \
-d '{"name":"admin","password":"<password>"}' \
https://img.example.com/api/admin/auth/login
# Read-only endpoints need only the cookie
curl -b jar.txt https://img.example.com/api/admin/me
# State-changing endpoints also need the CSRF token
curl -b jar.txt -X POST -H 'X-CSRF-Token: <csrf_token>' \
https://img.example.com/api/admin/auth/logoutSession details:
- The cookie is
HttpOnlyandSameSite=Lax; it is alsoSecurewhen the request arrives over HTTPS (TLS terminated by the server itself, or reported viaX-Forwarded-Protoby a trusted reverse proxy). - A session lasts 30 days from login and is not extended by use; after that you must log in again.
- Changing a user's password invalidates all of that user's sessions.
- Too many login attempts return 429.
- While an account still has the factory default password, its session can use only three endpoints: log out
(
POST /api/admin/auth/logout), view itself (GET /api/admin/me, wheremust_change_passwordis true), and change the password (PUT /api/admin/me/password). Every other admin endpoint, including admin console uploads and issuing tokens, returns 403须先修改出厂默认密码(change the factory default password first) until the password is changed and you log in again.
Channels, users, hotlink protection, audit log, alerts, backups, export, and moderation endpoints, as well as reading the full runtime settings and changing them, are for administrators only; a regular user gets 403.
Error format
Apart from the exception below, every error response is a JSON envelope:
{ "error": "存储渠道暂不可用,请稍后重试" }error is a user-facing description (in Chinese) with no internal details or credentials. Programs should branch on
the HTTP status code, not parse this text.
The exception is the image access endpoints (/i/{id}, /o/{user_id}/{object_key}, and the admin console
preview /api/admin/images/{id}/thumb): apart from 400 for invalid processing parameters, which is still a JSON
envelope, their error responses are plain text:
| Status | Meaning |
|---|---|
| 403 | Rejected by hotlink protection (/i/ and /o/ only) |
| 404 | The image does not exist, has expired, or was deleted |
| 500 | Looking up the image metadata failed (database error); retry later |
| 503 | No healthy channel can currently read the image |
The admin console preview enters this pipeline only after session authentication, so authentication errors such as
not being logged in are still JSON envelopes. In redirect access mode, the 302 redirect from an image access
endpoint carries Cache-Control: no-store: the redirect target changes with channel health and configuration, so it
stays out of CDN and browser caches.
Common status codes:
| Status | Meaning |
|---|---|
| 400 | Invalid request body or parameters |
| 401 | Not authenticated: invalid token, or not logged in / session expired |
| 403 | Not permitted: CSRF check failed, still on the factory default password, administrator required, or the token lacks upload permission; for uploads it also means the storage quota is full |
| 404 | The target does not exist or is not visible to the current user |
| 409 | Conflicts with existing state, such as a duplicate name, or a channel that holds the only copy of some images |
| 413 | Larger than the per-image size limit (runtime setting max_upload_mb) |
| 415 | Not a supported image format (judged by file content) |
| 422 | Resolution exceeds the pixel budget (runtime setting max_pixels_mp) |
| 429 | Upload concurrency is full, the user is uploading too often, or there are too many login attempts; wait as indicated by Retry-After when present |
| 500 | Internal server error |
| 503 | On upload, no healthy channel can be written or too few synchronous replicas succeeded; on image access, no channel can currently read the image |
The group pages list the status codes each endpoint can actually return.
Shared conventions
- Timestamps: unless stated otherwise, UTC text in the form
YYYY-MM-DD HH:MM:SS(for example2026-10-15 08:30:00), with no time zone suffix. The exceptions arebuilt_atin build info andtaken_aton backup snapshots, which are RFC 3339 (taken_atwith millisecond precision). - Lists: list endpoints return
{"items": […], "total": N}, wheretotalcounts all items after filtering. - Pagination: the image, upload token, duplicate, cleanup, audit log, and alert lists accept
page(from 1, default 1) andpage_size(default 20, maximum 100; larger values are treated as 100). - Time ranges: the image list and the audit log accept
since(inclusive) anduntil(exclusive), in the same format. - IDs: images use a 10-character short ID string; user IDs are strings (the built-in administrator is
default); tokens, channels, duplicate candidates, and similar records use integers; backup snapshots are identified by file name. - Idempotent uploads: uploads may send an
Idempotency-Keyheader. Within the retention period (runtime settingidempotency_key_ttl_hours, default 24 hours), retries with the same key by the same user return the same image. Keys are scoped per user. - Request bodies: JSON endpoints use
application/json; file uploads usemultipart/form-datawith the file in thefilefield, and the optionalexif_policyandttlare read only from form fields (never from URL query parameters), at most 1 KiB each. - Base URL: the API shares the site's origin, with the path prefixes shown above and no version segment.
Groups
Data plane
Uploads and URL imports authenticated with an upload token
Image access
Public image endpoints, subject to hotlink protection
Admin · Session
Log in, log out, current user, and password change
Admin · Overview & settings
Dashboard and runtime settings
Admin · Upload
Session-authenticated uploads from the admin console, through the same pipeline as the data plane
Admin · Images
Image list, details, edits, and deletion
Admin · Upload tokens
Issuing, rotating, and revoking named tokens, and their S3 credentials
Admin · Storage channels
Channel templates, creation, editing, health checks, deletion, and cloud drive authorization
Admin · Users
Accounts, quotas, and password resets (administrators only)
Admin · Hotlink protection
The Referer allowlist (administrators only)
Admin · Moderation
Decisions on near-duplicates and pending-cleanup cold images (administrators only)
Admin · Operations
Audit log, alerts, backup snapshots, and metadata export (administrators only)
System
Health check and the API document