Skip to main content
All TaskForge API responses follow a consistent JSON structure. The top-level success boolean allows you to branch on success or failure without inspecting the HTTP status code.

Response formats

Success response

Successful responses include a data object and an optional message string.
{
  "success": true,
  "message": "Optional descriptive message",
  "data": {
    "id": 1,
    "title": "My task"
  }
}
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 set success to false and include a message describing what went wrong. An additional error field may be present for more specific error detail.
{
  "success": false,
  "message": "Error message here"
}
{
  "success": false,
  "message": "Validation failed",
  "error": "More specific error detail"
}
The 429 Too Many Requests response from Flask-Limiter does not follow this envelope. It returns { "message": "429 Too Many Requests: N per 1 hour" } directly. See Rate Limiting for details.

HTTP status codes

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).
{
  "success": true,
  "data": { "id": 1, "title": "My task" }
}
A new resource was created. Returned by:
  • POST /api/auth/register — new user account
  • POST /api/tasks — new task
  • POST /api/tags — new tag
{
  "success": true,
  "message": "Resource created successfully",
  "data": { "id": 5 }
}
The request was malformed or failed validation. Common causes include missing required fields, values that fail format validation, or an empty request body.
{
  "success": false,
  "message": "Missing required fields: title"
}
Authentication is required but the provided credentials or token are invalid, expired, or missing.
{
  "success": false,
  "message": "Invalid email or password"
}
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.
{
  "success": false,
  "message": "You do not have permission to view this task"
}
The requested resource does not exist.
{
  "success": false,
  "message": "Task not found"
}
The request conflicts with the current state of the server — typically a uniqueness constraint violation.
{
  "success": false,
  "message": "Username already exists"
}
The client has exceeded the rate limit for the endpoint. See Rate Limiting for limits and retry guidance.
{
  "message": "429 Too Many Requests: 5 per 1 hour"
}
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.
{
  "success": false,
  "message": "An unexpected error occurred"
}

Common error messages

The following table lists specific error messages returned by the API, the HTTP status code, and the conditions that trigger them.
MessageStatusTrigger
Missing required fields: {fields}400Required fields are absent or empty in the request body
No data provided400Request body is missing or not valid JSON
Invalid email format400Email does not match expected format
Invalid date format. Use ISO format (e.g., 2024-01-15T10:30:00)400due_date is not a valid ISO 8601 datetime
Color must be a valid hex code (e.g., #FF0000)400Tag color does not match ^#[0-9A-Fa-f]{6}$
Invalid email or password401Login credentials are wrong
Current password is incorrect401old_password does not match stored hash on change-password
Invalid refresh token401Refresh token does not exist in the database
Refresh token expired or revoked401Refresh token exists but is_revoked=true or expires_at is in the past
User account not found or inactive404JWT identity maps to a user that no longer exists or is_active=false
Account is inactive403Login attempted for a user with is_active=false
You do not have permission to view this task403Standard user requesting another user’s task
You do not have permission to update this task403Standard user trying to update another user’s task
You do not have permission to delete this task403Standard user trying to delete another user’s task
Task not found404task_id does not match any row in tasks
Tag not found404tag_id does not match any row in tags
User not found404user_id does not match any row in users
Username already exists409Registration attempted with a username already in users
Email already exists409Registration attempted with an email already in users
Tag already exists409Tag creation attempted with a name already in tags
System error: User role not found500The user role is missing from the roles table

Validation errors (400)

Validation failures return 400 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:
{
  "success": false,
  "message": "Missing required fields: username, password"
}

Password rules

Passwords are validated when registering and when changing a password. All rules must be satisfied simultaneously:
RuleError message
At least 8 charactersPassword must be at least 8 characters
No more than 128 charactersPassword 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

RuleError message
At least 3 charactersUsername must be at least 3 characters
No more than 80 charactersUsername 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:
{
  "success": false,
  "message": "Status must be one of: pending, in_progress, completed, cancelled"
}

Date format

due_date must be provided in ISO 8601 format:
{
  "success": false,
  "message": "Invalid date format. Use ISO format (e.g., 2024-01-15T10:30:00)"
}
Accepted formats include 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 the Authorization header:
Authorization: Bearer <access_token>
No Authorization header was provided.
{
  "msg": "Missing Authorization Header"
}
This error is returned by Flask-JWT-Extended and uses msg instead of message.
The token could not be decoded — it may be corrupted or signed with a different secret.
{
  "msg": "Signature verification failed"
}
The access token is past its expiry time (JWT_ACCESS_TOKEN_EXPIRES, default 1 hour). Use the refresh token to obtain a new access token.
{
  "msg": "Token has expired"
}
Refresh tokens expire after 30 days (JWT_REFRESH_TOKEN_EXPIRES=2592000). Once expired, the user must log in again.
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).
{
  "success": false,
  "message": "Refresh token expired or revoked"
}
Access tokens expire after 1 hour. Build your client to detect 401 responses with an expired token message and automatically call POST /api/auth/refresh before retrying the original request.

Build docs developers (and LLMs) love