Skip to content

Latest commit

 

History

History
1037 lines (830 loc) · 19.7 KB

File metadata and controls

1037 lines (830 loc) · 19.7 KB

Paintjob API Documentation

Version: 1.0
Last Updated: October 5, 2025

Overview

This document provides a comprehensive reference for the Paintjob API. The API allows clients to manage users, projects, rooms, walls, and generate motion paths for painting automation.

Base URL

http://localhost:8000/api

Authentication

Most endpoints require JWT Bearer token authentication. Include the token in the Authorization header:

Authorization: Bearer <your_jwt_token>

Tokens are obtained through the /users/login endpoint and expire after a configured time period (check the expires_in field in login responses).


Data Models

User

Represents a registered user in the system.

{
  "id": 1,
  "email": "user@example.com",
  "full_name": "John Doe",
  "is_admin": false,
  "created_at": "2025-10-05T12:00:00"
}

Fields:

  • id (integer): Unique user identifier
  • email (string): User's email address
  • full_name (string, optional): User's full name
  • is_admin (boolean): Whether the user has admin privileges
  • created_at (datetime): Account creation timestamp

Project

Represents a project that contains multiple rooms.

{
  "id": 1,
  "name": "Home Renovation",
  "description": "Main house painting project",
  "user_id": 1,
  "created_at": "2025-10-05T12:00:00",
  "updated_at": "2025-10-05T14:30:00"
}

Fields:

  • id (integer): Unique project identifier
  • name (string, required): Project name
  • description (string, optional): Project description
  • user_id (integer): ID of the user who owns this project
  • created_at (datetime): Project creation timestamp
  • updated_at (datetime, optional): Last update timestamp

Room

Represents a room within a project.

{
  "id": 1,
  "name": "Living Room",
  "project_id": 1,
  "data": {
    "width": 500,
    "height": 300,
    "customField": "value"
  },
  "walls": ["wall-1", "wall-2"],
  "created_at": "2025-10-05T12:00:00",
  "updated_at": "2025-10-05T14:30:00"
}

Fields:

  • id (integer): Unique room identifier
  • name (string, required): Room name
  • project_id (integer, required): ID of the parent project
  • data (object, required): JSON object containing room-specific data
  • walls (array of strings, optional): Array of wall IDs/references
  • created_at (datetime): Room creation timestamp
  • updated_at (datetime, optional): Last update timestamp

Wall

Represents a wall within a room with geometry and motion path data.

{
  "id": 1,
  "wall_id": "wall-front",
  "room_id": 1,
  "data": {
    "version": 1,
    "unit": "px",
    "origin": "bottom-left",
    "wall": {
      "width": 620,
      "height": 460,
      "thickness": 15
    },
    "obstacles": []
  },
  "motion_data": {
    "strokes": [],
    "geojson": {},
    "params": {}
  },
  "created_at": "2025-10-05T12:00:00",
  "updated_at": "2025-10-05T14:30:00"
}

Fields:

  • id (integer): Unique database identifier
  • wall_id (string, required): Public wall identifier (e.g., "wall-front")
  • room_id (integer, required): ID of the parent room
  • data (object, required): JSON object containing wall geometry data
  • motion_data (object, optional): Generated motion path payload with strokes and parameters
  • created_at (datetime): Wall creation timestamp
  • updated_at (datetime, optional): Last update timestamp

API Endpoints

Users

Create User

Creates a new user account.

Endpoint: POST /users/
Authentication: None
Request Body:

{
  "email": "user@example.com",
  "password": "password123",
  "full_name": "John Doe"
}

Response: 201 Created

{
  "id": 1,
  "email": "user@example.com",
  "full_name": "John Doe",
  "is_admin": false,
  "created_at": "2025-10-05T12:00:00"
}

Error Responses:

  • 409 Conflict: Email already registered

Login

Authenticates a user and returns a JWT token.

Endpoint: POST /users/login
Authentication: None
Rate Limit: 5 attempts per 5 minutes
Request Body:

{
  "email": "user@example.com",
  "password": "password123"
}

Response: 200 OK

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 43200,
  "user": {
    "id": 1,
    "email": "user@example.com",
    "full_name": "John Doe",
    "is_admin": false,
    "created_at": "2025-10-05T12:00:00"
  }
}

Error Responses:

  • 401 Unauthorized: Invalid credentials
  • 429 Too Many Requests: Rate limit exceeded

Get Current User

Returns the authenticated user's profile.

Endpoint: GET /users/me
Authentication: Required
Response: 200 OK

{
  "id": 1,
  "email": "user@example.com",
  "full_name": "John Doe",
  "is_admin": false,
  "created_at": "2025-10-05T12:00:00"
}

Error Responses:

  • 401 Unauthorized: Invalid or missing token

Get Current User with Projects

Returns the authenticated user's profile along with all projects and their rooms.

Endpoint: GET /users/me/with-projects
Authentication: Required
Response: 200 OK

{
  "id": 1,
  "email": "user@example.com",
  "full_name": "John Doe",
  "is_admin": false,
  "created_at": "2025-10-05T12:00:00",
  "projects": [
    {
      "id": 1,
      "name": "Home Renovation",
      "description": "Main house painting project",
      "user_id": 1,
      "created_at": "2025-10-05T12:00:00",
      "updated_at": "2025-10-05T14:30:00",
      "rooms": [
        {
          "id": 1,
          "name": "Living Room",
          "project_id": 1,
          "data": {},
          "walls": [],
          "created_at": "2025-10-05T12:00:00",
          "updated_at": null
        }
      ]
    }
  ]
}

Refresh Token

Generates a new JWT token for the authenticated user.

Endpoint: POST /users/refresh
Authentication: Required
Response: 200 OK

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 43200,
  "user": {
    "id": 1,
    "email": "user@example.com",
    "full_name": "John Doe",
    "is_admin": false,
    "created_at": "2025-10-05T12:00:00"
  }
}

Validate Token

Checks if the current JWT token is valid.

Endpoint: GET /users/validate-token
Authentication: Required
Response: 200 OK

{
  "valid": true,
  "user": {
    "id": 1,
    "email": "user@example.com",
    "full_name": "John Doe",
    "is_admin": false
  }
}

Logout

Client-side token invalidation endpoint.

Endpoint: POST /users/logout
Authentication: None
Response: 200 OK

{
  "message": "Successfully logged out. Please remove the token from your client.",
  "logout_time": 1696512000
}

Projects

Create Project

Creates a new project for the authenticated user.

Endpoint: POST /projects/
Authentication: Required
Request Body:

{
  "name": "Home Renovation",
  "description": "Main house painting project"
}

Response: 201 Created

{
  "id": 1,
  "name": "Home Renovation",
  "description": "Main house painting project",
  "user_id": 1,
  "created_at": "2025-10-05T12:00:00",
  "updated_at": null
}

Error Responses:

  • 401 Unauthorized: Not authenticated
  • 409 Conflict: Project name already exists for this user

Get My Projects

Returns all projects belonging to the authenticated user.

Endpoint: GET /projects/
Authentication: Required
Response: 200 OK

[
  {
    "id": 1,
    "name": "Home Renovation",
    "description": "Main house painting project",
    "user_id": 1,
    "created_at": "2025-10-05T12:00:00",
    "updated_at": null
  }
]

Get Project by ID

Returns a specific project by ID (must belong to authenticated user).

Endpoint: GET /projects/{project_id}
Authentication: Required
Response: 200 OK

{
  "id": 1,
  "name": "Home Renovation",
  "description": "Main house painting project",
  "user_id": 1,
  "created_at": "2025-10-05T12:00:00",
  "updated_at": null
}

Error Responses:

  • 403 Forbidden: Project belongs to another user
  • 404 Not Found: Project not found

Get Project with Rooms

Returns a project with all its associated rooms.

Endpoint: GET /projects/{project_id}/with-rooms
Authentication: Required
Response: 200 OK

{
  "id": 1,
  "name": "Home Renovation",
  "description": "Main house painting project",
  "user_id": 1,
  "created_at": "2025-10-05T12:00:00",
  "updated_at": null,
  "rooms": [
    {
      "id": 1,
      "name": "Living Room",
      "data": {},
      "walls": [],
      "created_at": "2025-10-05T12:00:00",
      "updated_at": null
    }
  ]
}

Error Responses:

  • 403 Forbidden: Project belongs to another user
  • 404 Not Found: Project not found

Update Project

Updates a project's name or description.

Endpoint: PUT /projects/{project_id}
Authentication: Required
Request Body:

{
  "name": "Updated Project Name",
  "description": "Updated description"
}

Response: 200 OK

{
  "id": 1,
  "name": "Updated Project Name",
  "description": "Updated description",
  "user_id": 1,
  "created_at": "2025-10-05T12:00:00",
  "updated_at": "2025-10-05T15:00:00"
}

Error Responses:

  • 403 Forbidden: Project belongs to another user
  • 404 Not Found: Project not found
  • 409 Conflict: Project name already exists

Delete Project

Deletes a project and all associated rooms and walls.

Endpoint: DELETE /projects/{project_id}
Authentication: Required
Response: 204 No Content

Error Responses:

  • 403 Forbidden: Project belongs to another user
  • 404 Not Found: Project not found

Rooms

Create Room

Creates a new room within a project.

Endpoint: POST /rooms/
Authentication: None (consider adding if needed)
Request Body:

{
  "name": "Living Room",
  "project_id": 1,
  "data": {
    "width": 500,
    "height": 300
  },
  "walls": ["wall-1", "wall-2"]
}

Response: 201 Created

{
  "id": 1,
  "name": "Living Room",
  "project_id": 1,
  "data": {
    "width": 500,
    "height": 300
  },
  "walls": ["wall-1", "wall-2"],
  "created_at": "2025-10-05T12:00:00",
  "updated_at": null
}

Error Responses:

  • 409 Conflict: Room name already exists in this project

Get All Rooms

Returns all rooms in the system.

Endpoint: GET /rooms/
Authentication: None
Response: 200 OK

[
  {
    "id": 1,
    "name": "Living Room",
    "project_id": 1,
    "data": {},
    "walls": [],
    "created_at": "2025-10-05T12:00:00",
    "updated_at": null
  }
]

Get Room by ID

Returns a specific room by its ID.

Endpoint: GET /rooms/{room_id}
Authentication: None
Response: 200 OK

{
  "id": 1,
  "name": "Living Room",
  "project_id": 1,
  "data": {},
  "walls": [],
  "created_at": "2025-10-05T12:00:00",
  "updated_at": null
}

Error Responses:

  • 404 Not Found: Room not found

Update Room

Updates a room's properties.

Endpoint: PUT /rooms/{room_id}
Authentication: None
Request Body:

{
  "name": "Updated Room Name",
  "data": {
    "width": 600,
    "height": 400
  }
}

Response: 200 OK

{
  "id": 1,
  "name": "Updated Room Name",
  "project_id": 1,
  "data": {
    "width": 600,
    "height": 400
  },
  "walls": [],
  "created_at": "2025-10-05T12:00:00",
  "updated_at": "2025-10-05T15:00:00"
}

Error Responses:

  • 404 Not Found: Room not found
  • 409 Conflict: Room name already exists

Delete Room

Deletes a room and all associated walls.

Endpoint: DELETE /rooms/{room_id}
Authentication: None
Response: 204 No Content

Error Responses:

  • 404 Not Found: Room not found

Walls

Create Wall

Creates a new wall within a room.

Endpoint: POST /walls/
Authentication: None
Request Body:

{
  "wall_id": "wall-front",
  "room_id": 1,
  "data": {
    "version": 1,
    "unit": "px",
    "origin": "bottom-left",
    "wall": {
      "width": 620,
      "height": 460,
      "thickness": 15
    },
    "obstacles": []
  }
}

Response: 201 Created

{
  "id": 1,
  "wall_id": "wall-front",
  "room_id": 1,
  "data": {
    "version": 1,
    "unit": "px",
    "origin": "bottom-left",
    "wall": {
      "width": 620,
      "height": 460,
      "thickness": 15
    },
    "obstacles": []
  },
  "motion_data": null,
  "created_at": "2025-10-05T12:00:00",
  "updated_at": null
}

Error Responses:

  • 404 Not Found: Room not found
  • 409 Conflict: Wall ID already exists

Get Wall by Wall ID

Returns a wall by its public wall_id string.

Endpoint: GET /walls/by-wall-id/{wall_id}
Authentication: None
Response: 200 OK

{
  "id": 1,
  "wall_id": "wall-front",
  "room_id": 1,
  "data": {},
  "motion_data": null,
  "created_at": "2025-10-05T12:00:00",
  "updated_at": null
}

Error Responses:

  • 404 Not Found: Wall not found

Update Wall

Updates a wall's properties by its database ID.

Endpoint: PUT /walls/{wall_id}
Authentication: None
Request Body:

{
  "data": {
    "version": 1,
    "wall": {
      "width": 700,
      "height": 500
    }
  },
  "motion_data": {
    "strokes": [],
    "params": {}
  }
}

Response: 200 OK

{
  "id": 1,
  "wall_id": "wall-front",
  "room_id": 1,
  "data": {
    "version": 1,
    "wall": {
      "width": 700,
      "height": 500
    }
  },
  "motion_data": {
    "strokes": [],
    "params": {}
  },
  "created_at": "2025-10-05T12:00:00",
  "updated_at": "2025-10-05T15:00:00"
}

Error Responses:

  • 404 Not Found: Wall not found
  • 409 Conflict: Wall ID already exists

Delete Wall

Deletes a wall by its database ID.

Endpoint: DELETE /walls/{wall_id}
Authentication: None
Response: 204 No Content

Error Responses:

  • 404 Not Found: Wall not found

Paths (Motion Path Generation)

Generate Motion Path

Generates a motion path payload from wall data. Can accept either a wall_id to retrieve data from the database or direct wall_data. When using wall_id, the generated motion path is automatically saved to the wall's motion_data field.

Endpoint: POST /paths/generate-motion-path
Authentication: None
Request Body (Option 1 - Using wall_id):

{
  "wall_id": "wall-front"
}

Request Body (Option 2 - Using wall_data):

{
  "wall_data": {
    "version": 1,
    "unit": "px",
    "origin": "bottom-left",
    "scale": {
      "unitsPerPixel": 0.1
    },
    "wall": {
      "id": "wall-front",
      "name": "Front Wall",
      "width": 620,
      "height": 460,
      "thickness": 15
    },
    "obstacles": [
      {
        "id": "window-1",
        "name": "Door",
        "corners": [[85, 246], [196, 246], [196, 0], [85, 0]]
      }
    ]
  }
}

Coordinate System:

  • origin: Must be "bottom-left" (preferred) or "top-left"
  • x coordinates: 0 to wall.width
  • y coordinates: 0 to wall.height
  • For "top-left" origin, y=0 is at the top; the service converts to bottom-left internally
  • Obstacles outside these ranges will be rejected with 400 error

Response: 200 OK

{
  "strokes": [
    {
      "mode": "paint",
      "points": [[10.0, 20.0], [30.0, 20.0]],
      "metadata": {}
    },
    {
      "mode": "move",
      "points": [[30.0, 20.0], [30.0, 40.0]],
      "metadata": {}
    }
  ],
  "geojson": {
    "type": "FeatureCollection",
    "features": []
  },
  "params": {
    "step_size": 20.0,
    "margin": 0.0,
    "round_decimals": 3
  }
}

Error Responses:

  • 400 Bad Request: Invalid wall data or coordinates
  • 404 Not Found: Wall ID not found (when using wall_id)

Error Response Example:

{
  "detail": {
    "code": "INVALID_WALL_DATA",
    "message": "Invalid coordinate data",
    "hints": [
      "origin must be 'bottom-left' or 'top-left'",
      "Use wall-local coordinates: 0 <= x <= wall.width",
      "Use 0 <= y <= wall.height; do not send negative y",
      "If using top-left, y=0 is the top; backend flips internally"
    ]
  }
}

Preview Path

Generates a motion path preview with summary statistics and metadata. Accepts the same input as generate-motion-path but returns summary information instead of the full payload.

Endpoint: POST /paths/preview-path
Authentication: None
Request Body: Same as /paths/generate-motion-path

Response: 200 OK

{
  "total_strokes": 150,
  "paint_strokes": 120,
  "move_strokes": 30,
  "total_points": 3000,
  "bounds": {
    "x_min": 0.0,
    "x_max": 620.0,
    "y_min": 0.0,
    "y_max": 460.0
  },
  "parameters": {
    "step_size": 20.0,
    "margin": 0.0,
    "round_decimals": 3
  },
  "strokes_preview": [
    {
      "mode": "paint",
      "points": [[10.0, 20.0], [30.0, 20.0]],
      "metadata": {}
    }
  ],
  "truncated": true
}

Fields:

  • total_strokes: Total number of strokes in the motion path
  • paint_strokes: Number of painting strokes
  • move_strokes: Number of movement strokes (non-painting)
  • total_points: Total number of coordinate points
  • bounds: Min/max coordinates of the path
  • parameters: Generation parameters used
  • strokes_preview: First 5 strokes (for preview purposes)
  • truncated: Whether the preview is truncated (more than 5 strokes exist)

Error Responses:

  • 400 Bad Request: Invalid wall data or coordinates
  • 404 Not Found: Wall ID not found (when using wall_id)

Error Response Format

All error responses follow a standard format:

{
  "detail": "Error message describing what went wrong"
}

For validation errors with multiple fields:

{
  "detail": [
    {
      "loc": ["body", "field_name"],
      "msg": "Field is required",
      "type": "value_error.missing"
    }
  ]
}
}

Status Codes

  • 200 OK: Request succeeded
  • 201 Created: Resource created successfully
  • 204 No Content: Resource deleted successfully
  • 400 Bad Request: Invalid request data
  • 401 Unauthorized: Authentication required or token invalid
  • 403 Forbidden: Authenticated but not authorized for this resource
  • 404 Not Found: Resource not found
  • 409 Conflict: Resource conflict (e.g., duplicate name)
  • 429 Too Many Requests: Rate limit exceeded

Notes for Frontend Developers

  1. Authentication Flow:

    • Call POST /users/login to get a JWT token
    • Store the token securely (e.g., localStorage, secure cookie)
    • Include the token in all authenticated requests: Authorization: Bearer <token>
    • Use POST /users/refresh to get a new token before expiration
    • Use GET /users/validate-token to check if token is still valid
  2. Typical Workflow:

    • User logs in → receives JWT token
    • Create a project → POST /projects/
    • Create rooms in the project → POST /rooms/
    • Create walls in each room → POST /walls/
    • Generate motion paths → POST /paths/generate-motion-path
    • Retrieve project with all data → GET /projects/{id}/with-rooms
  3. Motion Path Generation:

    • Use wall_id when the wall already exists in the database
    • Use wall_data for testing or temporary calculations
    • Preview endpoint provides statistics without full data transfer
    • Generated paths are automatically saved when using wall_id
  4. Data Flexibility:

    • data fields in Room and Wall models accept arbitrary JSON
    • Structure your data as needed by your application
    • The API stores and retrieves it without modification
  5. Hierarchical Deletion:

    • Deleting a project deletes all its rooms and walls
    • Deleting a room deletes all its walls
    • Use with caution as these operations are irreversible