Files
TieMeasureFlow/docs/API.md
T
Adriano dd2ebf863a feat: FASE 7 - Polish & Testing (security, i18n, test suite, docs)
Security hardening: CORS lockdown, rate limiting middleware con sliding
window e eviction IP stale, security headers (CSP, HSTS, X-Frame-Options),
session cookie hardening, filename sanitization upload.

i18n completion: internazionalizzati barcode.js e csv-export.js con bridge
window.BARCODE_I18N/CSV_I18N, aggiornati .po IT/EN con 27 nuove stringhe.

Tablet UX: touch target 44px per dispositivi coarse pointer.

Test suite: 101 test totali (76 server + 25 client), copertura completa
di tutti i router API, autenticazione, ruoli, CRUD, SPC, file upload,
security integration. Infrastruttura SQLite async in-memory con fixtures.

Fix critici: MissingGreenlet in recipe_service (selectinload eager),
route ordering tasks.py, auth_service bcrypt diretto, Measurement.id
Integer per SQLite.

Documentazione: API.md (riferimento completo 40+ endpoint),
DEPLOYMENT.md (guida produzione con Docker/Nginx/SSL),
USER_GUIDE.md (manuale utente per ruolo).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 17:10:24 +01:00

26 KiB

TieMeasureFlow API Reference

Overview

TieMeasureFlow provides a REST API built with FastAPI for managing measurement tasks, recipes, and statistical analysis. The API uses API Key authentication and supports both single and batch measurement creation.

Base URL: http://localhost:8000/api

API Version: 0.1.0

Table of Contents

  1. Authentication
  2. Authorization & Roles
  3. Rate Limiting
  4. Response Format
  5. Error Codes
  6. Endpoints
  7. Pagination
  8. Filtering

Authentication

Login Flow

  1. POST /auth/login - Obtain API Key

    {
      "username": "operator1",
      "password": "password123"
    }
    

    Response:

    {
      "user": {
        "id": 1,
        "username": "operator1",
        "display_name": "Operator One",
        "email": "op1@example.com",
        "roles": ["MeasurementTec"],
        "is_admin": false,
        "active": true,
        "language_pref": "it",
        "theme_pref": "light"
      },
      "api_key": "tmf_abc123def456ghi789jkl"
    }
    
  2. Include API Key in Subsequent Requests

    X-API-Key: tmf_abc123def456ghi789jkl
    
  3. POST /auth/logout - Invalidate API Key

    • Requires X-API-Key header
    • Returns HTTP 204 No Content on success

Example: Complete Auth Flow

# Login
curl -X POST http://localhost:8000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "operator1", "password": "password123"}'

# Response contains api_key: "tmf_..."

# Use API key in subsequent requests
curl -X GET http://localhost:8000/api/auth/me \
  -H "X-API-Key: tmf_..."

# Logout
curl -X POST http://localhost:8000/api/auth/logout \
  -H "X-API-Key: tmf_..."

Authorization & Roles

All API endpoints require authentication via X-API-Key header. Most endpoints require specific roles:

Role Permissions
Maker Create/edit recipes, tasks, subtasks, upload files
MeasurementTec Execute measurements, barcode lookup, export CSV
Metrologist View SPC statistics, capability indices, control charts
Admin Manage users, system settings, regenerate API keys

Roles are combinable - a user can have multiple roles (stored as JSON array in database).

Rate Limiting

Rate limits are enforced per IP address using a sliding window algorithm:

Endpoint Limit Window
/auth/login 5 requests 60 seconds
All other endpoints 100 requests 60 seconds

Error Response on Rate Limit Exceeded (HTTP 429):

{
  "detail": "Too many login attempts. Please try again later."
}

The response includes a Retry-After header indicating the number of seconds to wait:

Retry-After: 42

Response Format

Success Response

All successful responses return JSON with appropriate HTTP status codes:

{
  "id": 1,
  "username": "operator1",
  "display_name": "Operator One",
  ...
}

Error Response

{
  "detail": "Invalid username or password"
}

Error Codes

Code Meaning Common Causes
400 Bad Request Invalid input, validation error, file size exceeded
401 Unauthorized Missing/invalid API key, invalid credentials
403 Forbidden User lacks required role for operation
404 Not Found Resource does not exist
409 Conflict Resource conflict (duplicate username, editing non-current recipe version)
422 Unprocessable Entity Validation error (invalid role, marker number already exists)
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Unexpected server error

Endpoints

Health Check

GET /health

Health check endpoint - no authentication required.

Response:

{
  "status": "ok",
  "service": "TieMeasureFlow API",
  "version": "0.1.0"
}

Auth

POST /auth/login

Login with username and password to receive an API key.

Request:

{
  "username": "string",
  "password": "string"
}

Response: HTTP 200 with LoginResponse

{
  "user": { ... },
  "api_key": "tmf_..."
}

Rate Limit: 5 requests/min

Errors:

  • 401: Invalid username or password
  • 429: Rate limit exceeded

GET /auth/me

Get current user profile.

Headers:

X-API-Key: tmf_...

Response: HTTP 200 with UserResponse

Errors:

  • 401: Invalid/missing API key

PUT /auth/me

Update own profile (display_name, language_pref, theme_pref).

Headers:

X-API-Key: tmf_...

Request:

{
  "display_name": "New Display Name",
  "language_pref": "en",
  "theme_pref": "dark"
}

Response: HTTP 200 with updated UserResponse

Errors:

  • 401: Invalid/missing API key

POST /auth/logout

Invalidate the current API key.

Headers:

X-API-Key: tmf_...

Response: HTTP 204 No Content

Errors:

  • 401: Invalid/missing API key

Users

All user management endpoints require Admin role.

GET /users

List all users.

Headers:

X-API-Key: tmf_... (Admin required)

Response: HTTP 200 with list[UserResponse]


POST /users

Create a new user.

Headers:

X-API-Key: tmf_... (Admin required)

Request:

{
  "username": "newuser",
  "password": "securepass",
  "display_name": "New User",
  "email": "user@example.com",
  "roles": ["MeasurementTec"],
  "is_admin": false,
  "language_pref": "it",
  "theme_pref": "light"
}

Response: HTTP 201 with UserResponse

Errors:

  • 403: Not admin
  • 409: Username already exists
  • 422: Invalid role (must be one of: Maker, MeasurementTec, Metrologist)

PUT /users/{user_id}

Update a user.

Headers:

X-API-Key: tmf_... (Admin required)

Request: (all fields optional)

{
  "display_name": "Updated Name",
  "email": "newemail@example.com",
  "roles": ["Maker", "MeasurementTec"],
  "is_admin": true,
  "language_pref": "en"
}

Response: HTTP 200 with updated UserResponse

Errors:

  • 403: Not admin
  • 404: User not found
  • 422: Invalid role

DELETE /users/{user_id}

Deactivate a user (soft delete). Sets active=False and clears api_key.

Headers:

X-API-Key: tmf_... (Admin required)

Response: HTTP 204 No Content

Errors:

  • 400: Cannot deactivate yourself
  • 403: Not admin
  • 404: User not found

POST /users/{user_id}/regenerate-key

Regenerate API key for a user.

Headers:

X-API-Key: tmf_... (Admin required)

Response: HTTP 200

{
  "api_key": "tmf_newsecretkey...",
  "message": "API key regenerated for user username"
}

Errors:

  • 403: Not admin
  • 404: User not found

Recipes

POST /recipes

Create a new recipe with its initial version (v1).

Headers:

X-API-Key: tmf_... (Maker required)

Request:

{
  "code": "RECIPE_001",
  "name": "Measurement Recipe 1",
  "description": "Optional description",
  "change_notes": "Initial version"
}

Response: HTTP 201 with RecipeResponse (includes current_version)

Errors:

  • 403: Maker role required
  • 409: Recipe code already exists

GET /recipes

List active recipes with pagination and optional search.

Headers:

X-API-Key: tmf_...

Query Parameters:

  • page (int, default=1, >=1)
  • per_page (int, default=20, 1-100)
  • search (string, optional, max_length=200)

Response: HTTP 200 with RecipeListResponse

{
  "items": [...],
  "total": 45,
  "page": 1,
  "per_page": 20,
  "pages": 3
}

GET /recipes/{recipe_id}

Get recipe detail with current version and all tasks/subtasks.

Headers:

X-API-Key: tmf_...

Response: HTTP 200 with RecipeResponse

Errors:

  • 404: Recipe not found

GET /recipes/code/{code}

Look up a recipe by barcode/code. Requires MeasurementTec role.

Headers:

X-API-Key: tmf_... (MeasurementTec required)

Response: HTTP 200 with RecipeResponse

Errors:

  • 403: MeasurementTec role required
  • 404: Recipe not found

PUT /recipes/{recipe_id}

Update a recipe. Creates a new version via copy-on-write. Existing measurements remain linked to the original version.

Headers:

X-API-Key: tmf_... (Maker required)

Request:

{
  "name": "Updated Name",
  "description": "Updated description",
  "change_notes": "Updated tolerances"
}

Response: HTTP 200 with RecipeResponse (with new version)

Errors:

  • 403: Maker role required
  • 404: Recipe not found
  • 409: Recipe is inactive

DELETE /recipes/{recipe_id}

Deactivate a recipe (soft delete).

Headers:

X-API-Key: tmf_... (Maker required)

Response: HTTP 200 with RecipeResponse

Errors:

  • 403: Maker role required
  • 404: Recipe not found

GET /recipes/{recipe_id}/versions

List all versions of a recipe.

Headers:

X-API-Key: tmf_...

Response: HTTP 200 with list[RecipeVersionResponse]


GET /recipes/{recipe_id}/versions/{version_number}

Get a specific version with its tasks.

Headers:

X-API-Key: tmf_...

Response: HTTP 200 with RecipeVersionResponse

Errors:

  • 404: Version not found

GET /recipes/{recipe_id}/versions/{version_number}/measurement-count

Count measurements on a specific version. Useful to warn before creating a new version.

Headers:

X-API-Key: tmf_... (Maker required)

Response: HTTP 200

{
  "recipe_id": 1,
  "version_number": 1,
  "measurement_count": 42
}

Errors:

  • 403: Maker role required
  • 404: Recipe or version not found

Tasks & Subtasks

GET /recipes/{recipe_id}/tasks

List all tasks for the current version of a recipe.

Headers:

X-API-Key: tmf_...

Response: HTTP 200 with list[TaskResponse] (sorted by order_index)


POST /recipes/{recipe_id}/tasks

Add a task to the current version. Creates a new version via copy-on-write.

Headers:

X-API-Key: tmf_... (Maker required)

Request:

{
  "title": "Task Title",
  "directive": "Measure diameter at point A",
  "description": "Detailed instructions",
  "file_type": "image",
  "annotations_json": null,
  "subtasks": [
    {
      "marker_number": 1,
      "description": "Point A diameter",
      "measurement_type": "length",
      "nominal": 10.0,
      "utl": 10.5,
      "uwl": 10.2,
      "lwl": 9.8,
      "ltl": 9.5,
      "unit": "mm"
    }
  ]
}

Response: HTTP 201 with TaskResponse

Errors:

  • 403: Maker role required
  • 404: Recipe not found
  • 409: Recipe is inactive

PUT /tasks/{task_id}

Update a task. Must belong to current version.

Headers:

X-API-Key: tmf_... (Maker required)

Request: (all fields optional)

{
  "title": "Updated Title",
  "directive": "Updated directive"
}

Response: HTTP 200 with TaskResponse

Errors:

  • 403: Maker role required
  • 404: Task not found
  • 409: Task belongs to non-current version

DELETE /tasks/{task_id}

Delete a task and its subtasks. Must belong to current version.

Headers:

X-API-Key: tmf_... (Maker required)

Response: HTTP 204 No Content

Errors:

  • 403: Maker role required
  • 404: Task not found
  • 409: Task belongs to non-current version

PUT /tasks/reorder

Reorder tasks by providing ordered task IDs. All tasks must belong to the same current version.

Headers:

X-API-Key: tmf_... (Maker required)

Request:

{
  "task_ids": [3, 1, 2]
}

Response: HTTP 200 with list[TaskResponse] (in new order)

Errors:

  • 403: Maker role required
  • 400: task_ids is empty or tasks from different versions
  • 404: One or more task IDs not found
  • 409: Tasks belong to non-current version

POST /tasks/{task_id}/subtasks

Add a subtask to a task. Task must belong to current version.

Headers:

X-API-Key: tmf_... (Maker required)

Request:

{
  "marker_number": 2,
  "description": "Additional measurement point",
  "measurement_type": "length",
  "nominal": 15.0,
  "utl": 15.3,
  "uwl": 15.1,
  "lwl": 14.9,
  "ltl": 14.7,
  "unit": "mm"
}

Response: HTTP 201 with SubtaskResponse

Errors:

  • 403: Maker role required
  • 404: Task not found
  • 409: Task belongs to non-current version OR marker_number already exists

PUT /subtasks/{subtask_id}

Update a subtask. Its parent task must belong to current version.

Headers:

X-API-Key: tmf_... (Maker required)

Request: (all fields optional)

{
  "description": "Updated description",
  "nominal": 15.5
}

Response: HTTP 200 with SubtaskResponse

Errors:

  • 403: Maker role required
  • 404: Subtask not found
  • 409: Parent task belongs to non-current version

DELETE /subtasks/{subtask_id}

Delete a subtask. Its parent task must belong to current version.

Headers:

X-API-Key: tmf_... (Maker required)

Response: HTTP 204 No Content

Errors:

  • 403: Maker role required
  • 404: Subtask not found
  • 409: Parent task belongs to non-current version

Measurements

POST /measurements/

Create a single measurement with auto-calculated pass/fail.

Headers:

X-API-Key: tmf_... (MeasurementTec required)

Request:

{
  "subtask_id": 1,
  "version_id": 1,
  "value": 10.2,
  "lot_number": "LOT123",
  "serial_number": "SN456",
  "input_method": "usb_caliper"
}

Response: HTTP 200 with MeasurementResponse

{
  "id": 1,
  "subtask_id": 1,
  "version_id": 1,
  "measured_by": 2,
  "value": 10.2,
  "pass_fail": "pass",
  "deviation": 0.2,
  "lot_number": "LOT123",
  "serial_number": "SN456",
  "input_method": "usb_caliper",
  "measured_at": "2025-02-07T12:34:56",
  "synced_to_csv": false
}

Pass/Fail Calculation:

  • pass: value is within LWL and UWL
  • warning: value is within LTL-LWL or UWL-UTL
  • fail: value is outside LTL or UTL

Errors:

  • 400: Invalid subtask/version or value out of logical bounds
  • 403: MeasurementTec role required

POST /measurements/batch

Create multiple measurements in a batch.

Headers:

X-API-Key: tmf_... (MeasurementTec required)

Request:

{
  "measurements": [
    {
      "subtask_id": 1,
      "version_id": 1,
      "value": 10.2,
      "lot_number": "LOT123",
      "serial_number": "SN456",
      "input_method": "usb_caliper"
    },
    {
      "subtask_id": 2,
      "version_id": 1,
      "value": 15.1,
      "lot_number": "LOT123",
      "serial_number": "SN456",
      "input_method": "manual"
    }
  ]
}

Response: HTTP 200 with list[MeasurementResponse]

Errors:

  • 400: Any measurement validation fails (entire batch fails)
  • 403: MeasurementTec role required

GET /measurements/

Query measurements with filters and pagination.

Headers:

X-API-Key: tmf_... (Metrologist required)

Query Parameters:

  • recipe_id (int, optional)
  • version_id (int, optional)
  • subtask_id (int, optional)
  • measured_by (int, optional)
  • lot_number (string, optional)
  • serial_number (string, optional)
  • date_from (datetime ISO8601, optional)
  • date_to (datetime ISO8601, optional)
  • pass_fail (string, optional, enum: pass|warning|fail)
  • page (int, default=1, >=1)
  • per_page (int, default=50, 1-500)

Response: HTTP 200 with MeasurementListResponse

{
  "items": [...],
  "total": 256,
  "page": 1,
  "per_page": 50,
  "pages": 6
}

Errors:

  • 403: Metrologist role required

GET /measurements/export/csv

Export measurements to CSV with configurable delimiter and decimal separator.

Headers:

X-API-Key: tmf_... (MeasurementTec or Metrologist required)

Query Parameters: (same as GET /measurements/)

Response: HTTP 200 with CSV file (streaming)

CSV Format:

id,subtask_id,version_id,measured_by,value,pass_fail,deviation,lot_number,serial_number,input_method,measured_at,synced_to_csv

Delimiter and decimal separator read from system settings (csv_delimiter, csv_decimal_separator).

Errors:

  • 403: Insufficient role

Files

POST /files/upload

Upload an image or PDF file. Requires Maker role.

Headers:

X-API-Key: tmf_... (Maker required)
Content-Type: multipart/form-data

Form Parameters:

  • file (UploadFile, required): Image (JPEG, PNG, GIF, WebP) or PDF
  • recipe_id (int, optional): Target recipe
  • version_id (int, optional): Target version

Constraints:

  • Max file size: 50 MB (configurable via MAX_UPLOAD_SIZE_MB)
  • Allowed types: JPEG, PNG, GIF, WebP, PDF
  • Max filename length: 255 characters

Response: HTTP 200

{
  "file_path": "1/1/image.jpg",
  "thumbnail_path": "1/1/thumbnails/image.jpg",
  "file_type": "image/jpeg",
  "file_size": 204800
}

Notes:

  • Files stored in uploads/{recipe_id}/{version_id}/ or uploads/general/
  • Filenames sanitized (alphanumeric, dots, hyphens, underscores only)
  • Thumbnails auto-generated for images (200x200px)
  • Duplicate filenames auto-numbered

Errors:

  • 400: Invalid file type, file size exceeded, invalid filename
  • 403: Maker role required

GET /files/{file_path}

Serve a file from the uploads directory.

Headers:

X-API-Key: tmf_...

Path Parameter:

  • file_path (string): Relative path from uploads/ directory

Example:

GET /files/1/1/image.jpg
GET /files/1/1/thumbnails/image.jpg

Response: HTTP 200 with file content (Content-Type inferred)

Errors:

  • 403: Path traversal attempt
  • 404: File not found

DELETE /files/{file_path}

Delete a file from the uploads directory. Requires Maker role.

Headers:

X-API-Key: tmf_... (Maker required)

Notes:

  • Associated thumbnail also deleted if it exists

Response: HTTP 204 No Content

Errors:

  • 403: Maker role required or path traversal attempt
  • 404: File not found

Settings

GET /settings/

Get all system settings as a dictionary.

Headers:

X-API-Key: tmf_...

Response: HTTP 200

{
  "company_logo_path": "logos/company_logo.png",
  "csv_delimiter": ",",
  "csv_decimal_separator": "."
}

PUT /settings/

Update multiple system settings. Requires Admin role.

Headers:

X-API-Key: tmf_... (Admin required)

Request:

{
  "csv_delimiter": ";",
  "csv_decimal_separator": ",",
  "custom_setting": "value"
}

Response: HTTP 200

{
  "message": "Updated 3 settings",
  "updated_keys": ["csv_delimiter", "csv_decimal_separator", "custom_setting"]
}

Errors:

  • 403: Admin role required

Upload company logo. Requires Admin role.

Headers:

X-API-Key: tmf_... (Admin required)
Content-Type: multipart/form-data

Form Parameters:

  • file (UploadFile, required): Image file (JPEG, PNG, GIF, WebP, SVG)

Response: HTTP 200

{
  "message": "Company logo uploaded successfully",
  "logo_path": "logos/company_logo.png",
  "file_size": 102400
}

Notes:

  • Stored as uploads/logos/company_logo.{ext}
  • Updates company_logo_path setting in database

Errors:

  • 400: Invalid file type or size exceeded
  • 403: Admin role required

Statistics

All statistics endpoints require Metrologist role.

GET /statistics/summary

Get pass/fail/warning summary for filtered measurements.

Headers:

X-API-Key: tmf_... (Metrologist required)

Query Parameters:

  • recipe_id (int, required)
  • version_id (int, optional)
  • subtask_id (int, optional)
  • date_from (datetime ISO8601, optional)
  • date_to (datetime ISO8601, optional)
  • operator_id (int, optional)
  • lot_number (string, optional)
  • serial_number (string, optional)

Response: HTTP 200 with SummaryData

{
  "total": 100,
  "pass_count": 85,
  "warning_count": 10,
  "fail_count": 5,
  "pass_percentage": 85.0,
  "warning_percentage": 10.0,
  "fail_percentage": 5.0
}

GET /statistics/capability

Get capability indices (Cp/Cpk/Pp/Ppk) for a specific subtask.

Headers:

X-API-Key: tmf_... (Metrologist required)

Query Parameters:

  • recipe_id (int, required)
  • subtask_id (int, required)
  • version_id (int, optional)
  • date_from (datetime ISO8601, optional)
  • date_to (datetime ISO8601, optional)
  • operator_id (int, optional)
  • lot_number (string, optional)
  • serial_number (string, optional)

Response: HTTP 200 with CapabilityData

{
  "n": 50,
  "mean": 10.05,
  "stdev": 0.15,
  "cp": 1.67,
  "cpk": 1.50,
  "pp": 1.60,
  "ppk": 1.40
}

GET /statistics/control-chart

Get control chart data with UCL/LCL and out-of-control detection.

Headers:

X-API-Key: tmf_... (Metrologist required)

Query Parameters: (same as capability)

Response: HTTP 200 with ControlChartData

{
  "points": [
    {
      "x": 0,
      "y": 10.1,
      "timestamp": "2025-02-01T08:00:00"
    },
    ...
  ],
  "ucl": 10.5,
  "lcl": 9.5,
  "center_line": 10.0,
  "out_of_control_indices": [5, 12, 18]
}

GET /statistics/histogram

Get histogram data with normal curve overlay.

Headers:

X-API-Key: tmf_... (Metrologist required)

Query Parameters:

  • recipe_id (int, required)
  • subtask_id (int, required)
  • version_id (int, optional)
  • date_from (datetime ISO8601, optional)
  • date_to (datetime ISO8601, optional)
  • operator_id (int, optional)
  • lot_number (string, optional)
  • serial_number (string, optional)
  • n_bins (int, default=20, 5-100)

Response: HTTP 200 with HistogramData

{
  "bins": [9.0, 9.2, 9.4, 9.6, ...],
  "frequencies": [0, 2, 5, 12, ...],
  "normal_curve_y": [0.001, 0.003, 0.008, ...]
}

GET /statistics/subtasks

Get subtasks for a recipe (for filter dropdown).

Headers:

X-API-Key: tmf_... (Metrologist required)

Query Parameters:

  • recipe_id (int, required)
  • version_id (int, optional): Use current version if not provided

Response: HTTP 200 with list of subtask objects

[
  {
    "id": 1,
    "marker_number": 1,
    "description": "Diameter at point A",
    "task_title": "Task 1: Measure",
    "nominal": 10.0,
    "utl": 10.5,
    "ltl": 9.5
  },
  ...
]

Reports

All report endpoints require Metrologist role.

GET /reports/spc

Generate and download SPC PDF report with charts and statistics.

Headers:

X-API-Key: tmf_... (Metrologist required)

Query Parameters:

  • recipe_id (int, required)
  • subtask_id (int, required)
  • version_id (int, optional)
  • date_from (datetime ISO8601, optional)
  • date_to (datetime ISO8601, optional)
  • operator_id (int, optional)
  • lot_number (string, optional)
  • serial_number (string, optional)

Response: HTTP 200 with PDF file (streaming)

Content-Type: application/pdf

Filename: spc_report_recipe{recipe_id}_st{subtask_id}.pdf

Errors:

  • 403: Metrologist role required
  • 500: Report generation failed

GET /reports/measurements

Generate and download measurement table PDF report.

Headers:

X-API-Key: tmf_... (Metrologist required)

Query Parameters:

  • recipe_id (int, required)
  • subtask_id (int, optional)
  • version_id (int, optional)
  • date_from (datetime ISO8601, optional)
  • date_to (datetime ISO8601, optional)
  • operator_id (int, optional)
  • lot_number (string, optional)
  • serial_number (string, optional)

Response: HTTP 200 with PDF file (streaming)

Content-Type: application/pdf

Filename: measurements_report_recipe{recipe_id}.pdf

Errors:

  • 403: Metrologist role required
  • 500: Report generation failed

Pagination

List endpoints return paginated responses with the following format:

{
  "items": [...],
  "total": 150,
  "page": 1,
  "per_page": 20,
  "pages": 8
}

Parameters:

  • page: Current page number (1-indexed, default=1)
  • per_page: Items per page (default varies by endpoint, max varies)

Example:

GET /api/recipes?page=2&per_page=50

Filtering

Most list endpoints support filtering via query parameters. Common filters:

Parameter Type Example
search string /recipes?search=recipe%20name
date_from ISO8601 datetime /measurements?date_from=2025-01-01T00:00:00
date_to ISO8601 datetime /measurements?date_to=2025-02-07T23:59:59
lot_number string /measurements?lot_number=LOT123
serial_number string /measurements?serial_number=SN456
pass_fail string (pass|warning|fail) /measurements?pass_fail=fail

Response Schemas

UserResponse

{
  "id": 1,
  "username": "operator1",
  "display_name": "Operator One",
  "email": "op1@example.com",
  "roles": ["MeasurementTec"],
  "is_admin": false,
  "active": true,
  "language_pref": "it",
  "theme_pref": "light"
}

RecipeResponse

{
  "id": 1,
  "code": "RECIPE_001",
  "name": "Recipe Name",
  "description": "Description",
  "active": true,
  "created_at": "2025-01-01T10:00:00",
  "updated_at": "2025-01-15T14:30:00",
  "created_by": 1,
  "current_version": { ... }
}

TaskResponse

{
  "id": 1,
  "version_id": 1,
  "order_index": 0,
  "title": "Task Title",
  "directive": "Measurement directive",
  "description": "Detailed description",
  "file_type": "image",
  "annotations_json": null,
  "subtasks": [...]
}

SubtaskResponse

{
  "id": 1,
  "task_id": 1,
  "marker_number": 1,
  "description": "Measurement point description",
  "measurement_type": "length",
  "nominal": 10.0,
  "utl": 10.5,
  "uwl": 10.2,
  "lwl": 9.8,
  "ltl": 9.5,
  "unit": "mm"
}

MeasurementResponse

{
  "id": 1,
  "subtask_id": 1,
  "version_id": 1,
  "measured_by": 2,
  "value": 10.15,
  "pass_fail": "pass",
  "deviation": 0.15,
  "lot_number": "LOT123",
  "serial_number": "SN456",
  "input_method": "usb_caliper",
  "measured_at": "2025-02-07T12:34:56",
  "synced_to_csv": false
}