success boolean allows you to branch on success or failure without inspecting the HTTP status code.
Response formats
Success response
Successful responses include adata object and an optional message string.
message is included for write operations (create, update, delete, login, logout, change-password). It is omitted on GET responses.
data is omitted from responses that return only a message (e.g., delete operations, logout).
Error response
Failed responses always setsuccess to false and include a message describing what went wrong. An additional error field may be present for more specific error detail.
HTTP status codes
200 OK
200 OK
The request succeeded. Returned for successful
GET, PUT, PATCH, and DELETE operations, as well as successful POST requests to non-creation endpoints (login, logout, refresh token, change-password, mark-complete).201 Created
201 Created
A new resource was created. Returned by:
POST /api/auth/register— new user accountPOST /api/tasks— new taskPOST /api/tags— new tag
400 Bad Request
400 Bad Request
The request was malformed or failed validation. Common causes include missing required fields, values that fail format validation, or an empty request body.
401 Unauthorized
401 Unauthorized
403 Forbidden
403 Forbidden
The authenticated user does not have permission to access the requested resource. This occurs when a standard user attempts to access another user’s tasks, or when a non-admin accesses an admin-only endpoint.
404 Not Found
404 Not Found
The requested resource does not exist.
409 Conflict
409 Conflict
The request conflicts with the current state of the server — typically a uniqueness constraint violation.
429 Too Many Requests
429 Too Many Requests
The client has exceeded the rate limit for the endpoint. See Rate Limiting for limits and retry guidance.
500 Internal Server Error
500 Internal Server Error
An unexpected error occurred on the server. The error is logged internally. If you encounter a
500 repeatedly, it indicates a bug — please report it.Common error messages
The following table lists specific error messages returned by the API, the HTTP status code, and the conditions that trigger them.| Message | Status | Trigger |
|---|---|---|
Missing required fields: {fields} | 400 | Required fields are absent or empty in the request body |
No data provided | 400 | Request body is missing or not valid JSON |
Invalid email format | 400 | Email does not match expected format |
Invalid date format. Use ISO format (e.g., 2024-01-15T10:30:00) | 400 | due_date is not a valid ISO 8601 datetime |
Color must be a valid hex code (e.g., #FF0000) | 400 | Tag color does not match ^#[0-9A-Fa-f]{6}$ |
Invalid email or password | 401 | Login credentials are wrong |
Current password is incorrect | 401 | old_password does not match stored hash on change-password |
Invalid refresh token | 401 | Refresh token does not exist in the database |
Refresh token expired or revoked | 401 | Refresh token exists but is_revoked=true or expires_at is in the past |
User account not found or inactive | 404 | JWT identity maps to a user that no longer exists or is_active=false |
Account is inactive | 403 | Login attempted for a user with is_active=false |
You do not have permission to view this task | 403 | Standard user requesting another user’s task |
You do not have permission to update this task | 403 | Standard user trying to update another user’s task |
You do not have permission to delete this task | 403 | Standard user trying to delete another user’s task |
Task not found | 404 | task_id does not match any row in tasks |
Tag not found | 404 | tag_id does not match any row in tags |
User not found | 404 | user_id does not match any row in users |
Username already exists | 409 | Registration attempted with a username already in users |
Email already exists | 409 | Registration attempted with an email already in users |
Tag already exists | 409 | Tag creation attempted with a name already in tags |
System error: User role not found | 500 | The user role is missing from the roles table |
Validation errors (400)
Validation failures return400 Bad Request. The message field describes which validation rule was violated.
Required fields
If any required field is absent or empty, the response identifies every missing field:Password rules
Passwords are validated when registering and when changing a password. All rules must be satisfied simultaneously:Password requirements
Password requirements
| Rule | Error message |
|---|---|
| At least 8 characters | Password must be at least 8 characters |
| No more than 128 characters | Password is too long |
| At least one uppercase letter (A–Z) | Password must contain at least one uppercase letter |
| At least one lowercase letter (a–z) | Password must contain at least one lowercase letter |
| At least one digit (0–9) | Password must contain at least one number |
Username rules
| Rule | Error message |
|---|---|
| At least 3 characters | Username must be at least 3 characters |
| No more than 80 characters | Username is too long |
Only letters, digits, _, and - | Username can only contain letters, numbers, underscores, and hyphens |
Enum fields
status and priority on tasks must be one of the exact values defined in their enums. Any other value returns:
Date format
due_date must be provided in ISO 8601 format:
2024-12-31T23:59:59, 2024-12-31T23:59:59Z, and 2024-12-31T23:59:59+00:00.
Authentication errors (401)
All protected endpoints require a valid JWT access token in theAuthorization header:
Token missing
Token missing
No
Authorization header was provided.This error is returned by Flask-JWT-Extended and uses
msg instead of message.Token invalid or malformed
Token invalid or malformed
The token could not be decoded — it may be corrupted or signed with a different secret.
Token expired
Token expired
The access token is past its expiry time (Refresh tokens expire after 30 days (
JWT_ACCESS_TOKEN_EXPIRES, default 1 hour). Use the refresh token to obtain a new access token.JWT_REFRESH_TOKEN_EXPIRES=2592000). Once expired, the user must log in again.Refresh token invalid or revoked
Refresh token invalid or revoked
The refresh token provided to
POST /api/auth/refresh does not exist in the database, or has been revoked (e.g., the user logged out or changed their password).