Skip to content

Endpoints

Complete reference for all API endpoints — schemas, curl examples, and request flow diagrams.

Base path

All endpoints are mounted under /api/v1. Examples below use $BASE_URL as a placeholder — replace with the appropriate environment URL.


Interactive API Explorer


GET /health — Health Check

GET /api/v1/health · No authentication required

Returns the service health status, version, and a summary of internal service states. This endpoint is intentionally public so load balancers and uptime monitors can reach it without credentials.

Response

{
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "healthy",
  "service_name": "modal-template-fastapi",
  "version": "1.0.0",
  "services_summary": {
    "total": 1,
    "healthy": 1,
    "unhealthy": 0
  }
}
Field Type Description
session_id string (UUID) Unique per-request trace ID
status "healthy" | "unhealthy" Service state enum
service_name string Identifies the service
version string SemVer string
services_summary object Counts of healthy/unhealthy subsystems

curl

curl -s "$BASE_URL/health" | jq .

Request Flow

sequenceDiagram
    autonumber
    participant C as Client
    participant R as Router
    participant H as health_check()

    C->>R: GET /api/v1/health
    Note over R: No auth dependency
    R->>H: invoke route handler
    H-->>R: HealthCheckResponse(status=healthy)
    R-->>C: 200 OK — HealthCheckResponse JSON

GET /items — List Items

GET /api/v1/items · JWT required

Returns the full array of stored items. Returns an empty array [] when no items exist.

Response

[
  {
    "id": "a1b2c3d4-...",
    "project_id": "my-project",
    "name": "Widget Alpha",
    "value": 42
  }
]

curl

curl -s "$BASE_URL/items" \
  -H "Authorization: Bearer $TOKEN" | jq .
curl -s "$BASE_URL/items" | jq .
# {
#   "detail": {
#     "detail": "Authorization header missing or malformed",
#     "session_id": "...",
#     "error_code": "MISSING_TOKEN"
#   }
# }

Request Flow

sequenceDiagram
    autonumber
    participant C  as Client
    participant D  as get_current_user()
    participant R  as list_items route
    participant H  as handler.list_items()

    C->>D: GET /api/v1/items + Bearer token
    D->>D: jwt.decode(token, JWT_SECRET)
    alt Invalid / missing token
        D-->>C: 401 Unauthorized + ErrorDetail
    end
    D-->>R: decoded payload
    R->>H: handler.list_items()
    H-->>R: list of item dicts
    R-->>C: 200 OK — array of items

POST /items — Create Item

POST /api/v1/items · JWT required · Returns 201 Created

Creates a new item from an arbitrary data payload. The item receives a server-generated UUID as its id.

Request Body

{
  "data": {
    "name": "Widget Alpha",
    "value": 42,
    "tags": ["beta", "featured"]
  },
  "project_id": "my-project-123"
}
Field Type Required Description
data object Yes Free-form payload stored verbatim
project_id string No Minimum length 1 if provided

Response

{
  "session_id": "550e8400-...",
  "status": "created",
  "message": "Item created successfully",
  "data": {
    "id": "a1b2c3d4-e5f6-...",
    "project_id": "my-project-123",
    "name": "Widget Alpha",
    "value": 42,
    "tags": ["beta", "featured"]
  }
}

curl

curl -s -X POST "$BASE_URL/items" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"data": {"name": "Widget Alpha", "value": 42}, "project_id": "proj-1"}' \
  | jq .
curl -s -X POST "$BASE_URL/items" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}' | jq .
# {
#   "detail": "...",
#   "session_id": "...",
#   "error_code": "VALIDATION_ERROR"
# }

Request Flow

sequenceDiagram
    autonumber
    participant C  as Client
    participant D  as get_current_user()
    participant V  as Pydantic Validation
    participant R  as create_item route
    participant H  as handler.create_item()
    participant S  as _store

    C->>D: POST /api/v1/items + Bearer token + body
    D->>D: jwt.decode(token)
    D-->>R: user payload

    R->>V: validate GenericRequest(body)
    alt Validation fails
        V-->>C: 422 Unprocessable Entity + VALIDATION_ERROR
    end

    R->>H: handler.create_item(request)
    H->>H: item_id = uuid4()
    H->>S: _store[item_id] = {...}
    S-->>H: stored dict
    H-->>R: item dict
    R-->>C: 201 Created — ItemResponse

GET /items/{item_id} — Get Item

GET /api/v1/items/{item_id} · JWT required

Retrieves a single item by its UUID. Returns 404 if the ID does not exist in the store.

Path Parameters

Parameter Type Description
item_id string (UUID) The item's unique identifier

Response

{
  "session_id": "550e8400-...",
  "status": "success",
  "message": "Item retrieved successfully",
  "data": {
    "id": "a1b2c3d4-...",
    "name": "Widget Alpha",
    "value": 42
  }
}

curl

ITEM_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"

curl -s "$BASE_URL/items/$ITEM_ID" \
  -H "Authorization: Bearer $TOKEN" | jq .
curl -s "$BASE_URL/items/nonexistent-id" \
  -H "Authorization: Bearer $TOKEN" | jq .
# {"detail": "Item 'nonexistent-id' not found"}

Request Flow

sequenceDiagram
    autonumber
    participant C  as Client
    participant D  as get_current_user()
    participant R  as get_item route
    participant H  as handler.get_item()
    participant S  as _store

    C->>D: GET /api/v1/items/{id} + Bearer token
    D-->>R: user payload
    R->>H: handler.get_item(item_id)
    H->>S: _store.get(item_id)
    alt Item not in store
        S-->>H: None
        H-->>R: None
        R-->>C: 404 Not Found
    end
    S-->>H: item dict
    H-->>R: item dict
    R-->>C: 200 OK — ItemResponse

PUT /items/{item_id} — Update Item

PUT /api/v1/items/{item_id} · JWT required

Performs a full replacement of the item's data. The id is preserved; all other fields come from the request body.

Request Body

Same schema as POST /items.

curl

curl -s -X PUT "$BASE_URL/items/$ITEM_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"data": {"name": "Updated Widget", "value": 99}}' \
  | jq .
curl -s -X PUT "$BASE_URL/items/ghost-id" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"data": {"x": 1}}' | jq .

Request Flow

sequenceDiagram
    autonumber
    participant C  as Client
    participant D  as get_current_user()
    participant R  as update_item route
    participant H  as handler.update_item()
    participant S  as _store

    C->>D: PUT /api/v1/items/{id} + token + body
    D-->>R: user payload
    R->>H: handler.update_item(item_id, request)
    H->>S: item_id in _store?
    alt Not found
        S-->>H: False
        H-->>R: None
        R-->>C: 404 Not Found
    end
    H->>S: _store[item_id] = {id, project_id, **request.data}
    S-->>H: updated dict
    H-->>R: updated dict
    R-->>C: 200 OK — ItemResponse(status="updated")

DELETE /items/{item_id} — Delete Item

DELETE /api/v1/items/{item_id} · JWT required · Returns 204 No Content

Permanently removes an item from the store. Returns 204 on success (no body), 404 if the item does not exist.

curl

curl -s -o /dev/null -w "%{http_code}" \
  -X DELETE "$BASE_URL/items/$ITEM_ID" \
  -H "Authorization: Bearer $TOKEN"
# 204
curl -s -X DELETE "$BASE_URL/items/ghost-id" \
  -H "Authorization: Bearer $TOKEN" | jq .
# {"detail": "Item 'ghost-id' not found"}

Request Flow

sequenceDiagram
    autonumber
    participant C  as Client
    participant D  as get_current_user()
    participant R  as delete_item route
    participant H  as handler.delete_item()
    participant S  as _store

    C->>D: DELETE /api/v1/items/{id} + token
    D-->>R: user payload
    R->>H: handler.delete_item(item_id)
    H->>S: item_id in _store?
    alt Not found
        H-->>R: False
        R-->>C: 404 Not Found
    end
    H->>S: del _store[item_id]
    S-->>H: True
    H-->>R: True
    R-->>C: 204 No Content

Error Reference

All auth and validation errors return a structured ErrorDetail body:

{
  "detail": {
    "detail": "Human-readable error message",
    "session_id": "550e8400-...",
    "error_code": "MACHINE_READABLE_CODE"
  }
}
HTTP Status error_code Cause
401 MISSING_TOKEN No Authorization header, or scheme is not Bearer
401 TOKEN_EXPIRED JWT exp claim is in the past
401 INVALID_TOKEN Signature mismatch, malformed JWT, wrong algorithm
404 (FastAPI default) Item ID not found in store
422 VALIDATION_ERROR Request body failed Pydantic validation