basePath: /api definitions: api.HealthResponse: properties: error: example: database unavailable type: string status: example: ok type: string type: object auth.LoginRequest: properties: account: example: alice type: string password: example: secret123 type: string required: - account - password type: object auth.LoginResponse: properties: expires_at: example: "2026-09-21T10:00:00+08:00" type: string token: example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... type: string user: $ref: '#/definitions/model.User' type: object auth.RegisterRequest: properties: email: example: alice@example.com maxLength: 255 type: string password: example: secret123 maxLength: 72 minLength: 6 type: string username: example: alice maxLength: 50 minLength: 3 type: string required: - email - password - username type: object auth.UpdateProfileRequest: properties: birthday: example: "1995-06-15" type: string gender: example: male type: string nickname: example: Alice maxLength: 50 type: string type: object httpx.ErrorResponse: properties: error: example: record not found type: string type: object model.Note: properties: content: type: string created_at: type: string id: type: integer title: type: string updated_at: type: string type: object model.SiteSetting: properties: footer: example: Copyright © Rill type: string logo: example: https://example.com/logo.png type: string site_name: example: Rill type: string updated_at: type: string type: object model.User: properties: avatar: type: string birthday: example: "1995-06-15" type: string created_at: type: string email: type: string gender: example: male type: string groups: items: $ref: '#/definitions/model.UserGroup' type: array id: type: integer nickname: type: string status: type: integer updated_at: type: string username: type: string type: object model.UserGroup: properties: created_at: type: string description: type: string id: type: integer is_system: type: boolean name: type: string updated_at: type: string type: object note.ListResponse: properties: items: items: $ref: '#/definitions/model.Note' type: array page: example: 1 type: integer page_size: example: 20 type: integer total: example: 42 type: integer type: object note.Request: properties: content: example: Milk, eggs type: string title: example: Shopping list maxLength: 200 type: string required: - title type: object site.UpdateRequest: properties: footer: example: Copyright © Rill maxLength: 1000 type: string logo: example: https://example.com/logo.png maxLength: 500 type: string site_name: example: Rill maxLength: 100 type: string required: - site_name type: object user.CreateRequest: properties: avatar: example: https://example.com/avatar.png maxLength: 255 type: string birthday: example: "1995-06-15" type: string email: example: alice@example.com maxLength: 255 type: string gender: enum: - male - female - other example: male type: string group_ids: example: - 1 items: type: integer type: array nickname: example: Alice maxLength: 50 type: string password: example: secret123 maxLength: 72 minLength: 6 type: string status: enum: - 0 - 1 example: 1 type: integer username: example: alice maxLength: 50 type: string required: - email - password - username type: object user.ListResponse: properties: items: items: $ref: '#/definitions/model.User' type: array page: example: 1 type: integer page_size: example: 20 type: integer total: example: 42 type: integer type: object user.UpdateRequest: properties: avatar: example: https://example.com/avatar.png maxLength: 255 type: string birthday: example: "1995-06-15" type: string gender: enum: - male - female - other example: male type: string group_ids: example: - 1 items: type: integer type: array nickname: example: Alice maxLength: 50 type: string password: example: secret123 maxLength: 72 minLength: 6 type: string status: enum: - 0 - 1 example: 1 type: integer type: object usergroup.ListResponse: properties: items: items: $ref: '#/definitions/model.UserGroup' type: array page: example: 1 type: integer page_size: example: 20 type: integer total: example: 42 type: integer type: object usergroup.Request: properties: description: example: Handles daily operations maxLength: 255 type: string name: example: Operations maxLength: 50 type: string required: - name type: object info: contact: {} description: |- Rill server HTTP API documentation. All endpoints are prefixed with api.prefix (default /api); requests and responses are JSON. Swagger UI: {prefix}/swagger/index.html; OpenAPI JSON: {prefix}/swagger/doc.json. Except for health, swagger, and auth, all endpoints require a Bearer JWT: call /auth/login to get a token, then send Authorization: Bearer {token}. title: Rill API version: "1.0" paths: /auth/login: post: consumes: - application/json description: Login with username or email; returns a JWT (TTL from auth.token_ttl) and the user. parameters: - description: Login credentials in: body name: credentials required: true schema: $ref: '#/definitions/auth.LoginRequest' produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/auth.LoginResponse' "400": description: invalid request schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: incorrect account or password schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' summary: Login tags: - auth /auth/register: post: consumes: - application/json description: Public registration. Creates a regular user in the default user group (id 1). username is 3-50 chars and unique; email is unique; password is 6-72 chars. parameters: - description: Registration payload in: body name: user required: true schema: $ref: '#/definitions/auth.RegisterRequest' produces: - application/json responses: "201": description: Created schema: $ref: '#/definitions/model.User' "400": description: invalid request schema: $ref: '#/definitions/httpx.ErrorResponse' "409": description: username or email already exists schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' summary: Register tags: - auth /health: get: description: Check service and database connectivity; returns 503 when the database is unavailable. produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/api.HealthResponse' "503": description: Service Unavailable schema: $ref: '#/definitions/api.HealthResponse' summary: Health check tags: - system /me: get: description: Return the authenticated user's profile, including groups. produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.User' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Get current user profile tags: - profile put: consumes: - application/json description: Update the authenticated user's nickname, gender, and birthday. birthday accepts YYYY-MM-DD, an empty string clears it, and omitting it keeps the current value. parameters: - description: Fields to update in: body name: profile required: true schema: $ref: '#/definitions/auth.UpdateProfileRequest' produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.User' "400": description: invalid request schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Update current user profile tags: - profile /notes: get: description: List notes ordered by id DESC. page starts at 1; page_size is 1-100, default 20. parameters: - description: Page number, default 1 example: 1 in: query name: page type: integer - description: Page size, default 20, max 100 example: 20 in: query name: page_size type: integer produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/note.ListResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: List notes tags: - notes post: consumes: - application/json description: Create a note. title is required (max 200 chars); content is optional. parameters: - description: Note payload in: body name: note required: true schema: $ref: '#/definitions/note.Request' produces: - application/json responses: "201": description: Created schema: $ref: '#/definitions/model.Note' "400": description: invalid request schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Create a note tags: - notes /notes/{id}: delete: description: Delete a note by id; returns 204 with no body on success. parameters: - description: Note ID example: 1 in: path name: id required: true type: integer produces: - application/json responses: "204": description: Deleted "400": description: invalid id schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "404": description: record not found schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Delete a note tags: - notes get: description: Get a note by id. parameters: - description: Note ID example: 1 in: path name: id required: true type: integer produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.Note' "400": description: invalid id schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "404": description: record not found schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Get a note tags: - notes put: consumes: - application/json description: Update title and content; validation is the same as create. parameters: - description: Note ID example: 1 in: path name: id required: true type: integer - description: Note payload in: body name: note required: true schema: $ref: '#/definitions/note.Request' produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.Note' "400": description: invalid request or id schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "404": description: record not found schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Update a note tags: - notes /site: get: description: 'Public site settings: site name, logo URL, and footer text. Returns built-in defaults when the settings row is missing.' produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.SiteSetting' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' summary: Get site settings tags: - site put: consumes: - application/json description: Admin only. Update the site name, logo URL, and footer text; returns the updated settings. parameters: - description: Site settings in: body name: site required: true schema: $ref: '#/definitions/site.UpdateRequest' produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.SiteSetting' "400": description: invalid request schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Update site settings tags: - site /user-groups: get: description: List user groups ordered by id ASC. page starts at 1; page_size is 1-100, default 20. parameters: - description: Page number, default 1 example: 1 in: query name: page type: integer - description: Page size, default 20, max 100 example: 20 in: query name: page_size type: integer produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/usergroup.ListResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: List user groups tags: - user-groups post: consumes: - application/json description: Create a user group. name is required and unique (max 50 chars); description is optional (max 255 chars); id is assigned by the server. parameters: - description: User group payload in: body name: group required: true schema: $ref: '#/definitions/usergroup.Request' produces: - application/json responses: "201": description: Created schema: $ref: '#/definitions/model.UserGroup' "400": description: invalid request schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "409": description: user group name already exists schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Create a user group tags: - user-groups /user-groups/{id}: delete: description: Delete a user group by id; returns 204 with no body on success. Returns 409 for system groups or when the group still has members. parameters: - description: User group ID example: 2 in: path name: id required: true type: integer produces: - application/json responses: "204": description: Deleted "400": description: invalid id schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "404": description: record not found schema: $ref: '#/definitions/httpx.ErrorResponse' "409": description: system group cannot be deleted or group still has members schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Delete a user group tags: - user-groups get: description: Get a user group by id; id 0 is the built-in admin group. parameters: - description: User group ID example: 1 in: path name: id required: true type: integer produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.UserGroup' "400": description: invalid id schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "404": description: record not found schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Get a user group tags: - user-groups put: consumes: - application/json description: Update a user group's name and description; name is required and unique. parameters: - description: User group ID example: 1 in: path name: id required: true type: integer - description: User group payload in: body name: group required: true schema: $ref: '#/definitions/usergroup.Request' produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.UserGroup' "400": description: invalid request or id schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "404": description: record not found schema: $ref: '#/definitions/httpx.ErrorResponse' "409": description: user group name already exists schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Update a user group tags: - user-groups /users: get: description: List users with their groups, ordered by id DESC. page starts at 1; page_size is 1-100, default 20. parameters: - description: Page number, default 1 example: 1 in: query name: page type: integer - description: Page size, default 20, max 100 example: 20 in: query name: page_size type: integer produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/user.ListResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: List users tags: - users post: consumes: - application/json description: Create a user and assign groups. username and email are unique; password is 6-72 chars; gender is male/female/other; birthday is YYYY-MM-DD and cannot be in the future; defaults to the regular user group (id 1) when group_ids is omitted. parameters: - description: User payload in: body name: user required: true schema: $ref: '#/definitions/user.CreateRequest' produces: - application/json responses: "201": description: Created schema: $ref: '#/definitions/model.User' "400": description: invalid request or user group not found schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "409": description: username or email already exists schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Create a user tags: - users /users/{id}: delete: description: Delete a user and their group memberships; returns 204 with no body on success. parameters: - description: User ID example: 1 in: path name: id required: true type: integer produces: - application/json responses: "204": description: Deleted "400": description: invalid id schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "404": description: record not found schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Delete a user tags: - users get: description: Get a user by id, including groups. parameters: - description: User ID example: 1 in: path name: id required: true type: integer produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.User' "400": description: invalid id schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "404": description: record not found schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Get a user tags: - users put: consumes: - application/json description: Update user fields. group_ids replaces all groups; a non-empty password resets the password; birthday accepts YYYY-MM-DD, an empty string clears it, and omitting it keeps the current value. parameters: - description: User ID example: 1 in: path name: id required: true type: integer - description: Fields to update in: body name: user required: true schema: $ref: '#/definitions/user.UpdateRequest' produces: - application/json responses: "200": description: OK schema: $ref: '#/definitions/model.User' "400": description: invalid request, id, or user group not found schema: $ref: '#/definitions/httpx.ErrorResponse' "401": description: unauthorized or session expired schema: $ref: '#/definitions/httpx.ErrorResponse' "403": description: admin permission required or account disabled schema: $ref: '#/definitions/httpx.ErrorResponse' "404": description: record not found schema: $ref: '#/definitions/httpx.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/httpx.ErrorResponse' security: - BearerAuth: [] summary: Update a user tags: - users securityDefinitions: BearerAuth: description: 'Bearer JWT, format: Bearer {token}, obtained from /auth/login' in: header name: Authorization type: apiKey swagger: "2.0"