{
  "security": [
    {
      "bearerAuth": []
    },
    {
      "cookieAuth": []
    }
  ],
  "openapi": "3.0.3",
  "info": {
    "title": "todoless API",
    "version": "1.0.0",
    "description": "todoless — self-hosted multi-user task and grocery manager.\n\nBase URL: https://[host]:7070/api\n\nAuthentication: PocketBase JWT token via Authorization: Bearer header or PB cookie.",
    "contact": {
      "name": "todoless"
    }
  },
  "servers": [
    {
      "url": "{scheme}://{host}:{port}/api",
      "description": "Your own todoless server",
      "variables": {
        "scheme": {
          "default": "http",
          "enum": [
            "http",
            "https"
          ]
        },
        "host": {
          "default": "your-server-ip"
        },
        "port": {
          "default": "7070"
        }
      }
    }
  ],
  "tags": [
    {
      "name": "System",
      "description": "Health check, setup status, OpenAPI spec"
    },
    {
      "name": "Auth",
      "description": "Authentication and registration"
    },
    {
      "name": "Invites",
      "description": "Invite code management"
    },
    {
      "name": "Entries",
      "description": "Unified entries (tasks + groceries)"
    },
    {
      "name": "Tasks",
      "description": "Task CRUD and actions"
    },
    {
      "name": "Items",
      "description": "Grocery items CRUD and actions"
    },
    {
      "name": "Calendar",
      "description": "Calendar events"
    },
    {
      "name": "Families",
      "description": "Family management (multi-user)"
    },
    {
      "name": "Goals",
      "description": "Goals/rewards points tracking"
    },
    {
      "name": "Labels",
      "description": "Label/tag management"
    },
    {
      "name": "Notes",
      "description": "Notes"
    },
    {
      "description": "Project management",
      "name": "Projects"
    },
    {
      "name": "Reminders",
      "description": "Reminders"
    },
    {
      "name": "Rewards",
      "description": "Rewards/awards"
    },
    {
      "name": "Settings",
      "description": "User settings"
    },
    {
      "name": "Shared",
      "description": "Shared cross-user views"
    },
    {
      "name": "Shops",
      "description": "Shop management (grocery)"
    },
    {
      "name": "Sprints",
      "description": "Sprint management"
    },
    {
      "name": "Users",
      "description": "User management"
    },
    {
      "name": "AI",
      "description": "AI assistant integration"
    },
    {
      "name": "External References",
      "description": "External system link management (Home Assistant, Gmail, custom)"
    },
    {
      "name": "Agents",
      "description": "Agent API tokens and scoped permissions for external AI agents"
    }
  ],
  "paths": {
    "/agent/approve": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "id"
                ],
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Approved"
          },
          "403": {
            "description": "Admin only"
          }
        },
        "tags": [
          "Agents"
        ],
        "summary": "Approve a pending agent token",
        "operationId": "agentApprove"
      }
    },
    "/agent/audit-log": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "List agent audit log entries",
        "operationId": "agentAuditLog",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Audit entries"
          }
        }
      }
    },
    "/agent/auth-test": {
      "get": {
        "summary": "Verify an agent API key",
        "operationId": "agentAuthTest",
        "security": [],
        "responses": {
          "200": {
            "description": "Key is valid"
          },
          "401": {
            "description": "Invalid API key"
          }
        },
        "tags": [
          "Agents"
        ]
      }
    },
    "/agent/counts": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approved": {
                      "type": "integer"
                    },
                    "pending": {
                      "type": "integer"
                    }
                  }
                }
              }
            },
            "description": "Counts"
          }
        },
        "tags": [
          "Agents"
        ],
        "summary": "Pending/approved agent token counts",
        "operationId": "agentCounts"
      }
    },
    "/agent/dispatch": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Dispatch an agent-scoped action",
        "operationId": "agentDispatchPost",
        "description": "Agent-key-authenticated CRUD dispatcher, scoped by key permissions.",
        "security": [],
        "responses": {
          "200": {
            "description": "Result"
          },
          "401": {
            "description": "Invalid/missing API key"
          },
          "403": {
            "description": "Missing scope"
          }
        }
      },
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "Dispatch an agent-scoped read",
        "operationId": "agentDispatchGet",
        "security": [],
        "responses": {
          "200": {
            "description": "Result"
          },
          "401": {
            "description": "Invalid/missing API key"
          }
        }
      }
    },
    "/agent/keys": {
      "post": {
        "summary": "Create agent API key",
        "operationId": "createAgentKey",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "permissions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "name",
                  "permissions"
                ]
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Key created — raw value shown once"
          }
        },
        "tags": [
          "Agents"
        ]
      },
      "get": {
        "operationId": "listAgentKeys",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Keys"
          }
        },
        "tags": [
          "Agents"
        ],
        "summary": "List agent API keys"
      }
    },
    "/agent/keys/{id}/revoke": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Revoke an agent API key",
        "operationId": "revokeAgentKey",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked"
          }
        }
      }
    },
    "/agent/list": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "List all agent tokens with status",
        "operationId": "agentList",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Agents"
          }
        }
      }
    },
    "/agent/pending": {
      "get": {
        "responses": {
          "200": {
            "description": "Pending agents"
          }
        },
        "tags": [
          "Agents"
        ],
        "summary": "List pending agent tokens",
        "operationId": "agentPending",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/agent/reject": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Reject (delete) a pending agent token",
        "operationId": "agentReject",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  }
                },
                "required": [
                  "id"
                ]
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Rejected"
          }
        }
      }
    },
    "/agent/reminders": {
      "post": {
        "summary": "Create a reminder as an agent",
        "operationId": "agentCreateReminder",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "reminder_time": {
                    "type": "string"
                  }
                },
                "required": [
                  "title",
                  "reminder_time"
                ]
              }
            }
          }
        },
        "security": [],
        "responses": {
          "201": {
            "description": "Reminder created"
          }
        },
        "tags": [
          "Agents",
          "Reminders"
        ]
      },
      "get": {
        "responses": {
          "200": {
            "description": "Reminders"
          }
        },
        "tags": [
          "Agents",
          "Reminders"
        ],
        "summary": "List the agent user's reminders",
        "operationId": "agentListReminders",
        "parameters": [
          {
            "name": "include_fired",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "security": []
      }
    },
    "/agent/tasks": {
      "get": {
        "security": [],
        "responses": {
          "200": {
            "description": "Tasks"
          },
          "401": {
            "description": "Invalid API key"
          },
          "403": {
            "description": "Missing scope"
          }
        },
        "tags": [
          "Agents",
          "Tasks"
        ],
        "summary": "List tasks assigned to the agent's user",
        "operationId": "agentListTasks"
      }
    },
    "/agent/tasks/{id}": {
      "patch": {
        "summary": "Update a task assigned to the agent's user",
        "operationId": "agentUpdateTask",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Updated"
          },
          "403": {
            "description": "Not assigned / missing scope"
          },
          "404": {
            "description": "Not found"
          }
        },
        "tags": [
          "Agents",
          "Tasks"
        ]
      }
    },
    "/agent/{id}": {
      "delete": {
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          }
        },
        "tags": [
          "Agents"
        ],
        "summary": "Revoke an agent token",
        "operationId": "agentRevoke",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "name": "id",
            "in": "path",
            "required": true
          }
        ]
      }
    },
    "/api-tokens": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "List personal API tokens",
        "operationId": "listApiTokens",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tokens (hash truncated)"
          }
        }
      },
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Create a personal API token",
        "operationId": "createApiToken",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expires_at": {
                    "type": "string",
                    "nullable": true
                  },
                  "name": {
                    "type": "string"
                  },
                  "permissions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "name",
                  "permissions"
                ]
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Token created — raw value shown once"
          }
        }
      }
    },
    "/api-tokens/{id}": {
      "delete": {
        "responses": {
          "200": {
            "description": "Deleted"
          }
        },
        "tags": [
          "Agents"
        ],
        "summary": "Delete a personal API token",
        "operationId": "deleteApiToken",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api-tokens/{id}/toggle": {
      "patch": {
        "tags": [
          "Agents"
        ],
        "summary": "Enable/disable a personal API token",
        "operationId": "toggleApiToken",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Toggled"
          }
        }
      }
    },
    "/bootstrap": {
      "get": {
        "tags": [
          "Bootstrap"
        ],
        "summary": "Load all data for the app boot in one call",
        "description": "Returns the raw records for every collection the UI renders at boot (tasks, items, notes, labels, shops, users, invites, reminders, settings), scoped to the authenticated user's family. Privacy rules are re-applied server-side because the hook query path bypasses collection listRules. Settings is a single record or null; the client creates its default when missing.",
        "operationId": "loadBootstrap",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Boot payload",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tasks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Task"
                      }
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Item"
                      }
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Note"
                      }
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Label"
                      }
                    },
                    "reminders": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Reminder"
                      }
                    },
                    "shops": {
                      "items": {
                        "$ref": "#/components/schemas/Shop"
                      },
                      "type": "array"
                    },
                    "users": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      }
                    },
                    "invites": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invite"
                      }
                    },
                    "settings": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "user": {
                          "type": "string"
                        },
                        "setup_complete": {
                          "type": "boolean",
                          "default": false
                        },
                        "sprint_duration": {
                          "type": "string",
                          "example": "2weeks"
                        },
                        "sprint_start_day": {
                          "type": "integer",
                          "default": 1
                        },
                        "language": {
                          "type": "string",
                          "example": "en"
                        },
                        "archive_retention_days": {
                          "type": "integer",
                          "default": 30
                        },
                        "auto_cleanup": {
                          "type": "boolean",
                          "default": true
                        },
                        "theme": {
                          "type": "string",
                          "example": "light"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "API token lacks entries/tasks/groceries read permission"
          }
        }
      }
    },
    "/calendar.ics": {
      "get": {
        "responses": {
          "200": {
            "description": "iCalendar feed",
            "content": {
              "text/calendar": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match matched)"
          },
          "401": {
            "description": "Missing, invalid or expired token"
          },
          "403": {
            "description": "Token not limited to calendar:read (URL tokens) or lacks calendar:read"
          }
        },
        "tags": [
          "Calendar"
        ],
        "summary": "Subscribable calendar feed (text/calendar)",
        "description": "Family calendar for calendar apps. Pass an API token limited to calendar:read as ?token= (calendar apps cannot send headers), or use a session / Bearer token with calendar:read. Supports ETag/If-None-Match (304) and Last-Modified. Contains the same privacy-checked events as the app: own tasks plus non-private family tasks.",
        "operationId": "calendarFeed",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "description": "API token whose only permission is calendar:read",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/collections/{collection}/records": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list"
          },
          "400": {
            "description": "Invalid filter"
          },
          "403": {
            "description": "Forbidden by collection API rules"
          }
        },
        "tags": [
          "System"
        ],
        "summary": "List/search records in a collection",
        "operationId": "listCollectionRecords",
        "description": "Generic PocketBase collection listing. Valid collection names: tasks, items, calendar_events, families, goals, labels, notes, projects, reminders, rewards, shops, sprints, users, invite_codes, app_settings, external_references, agent_keys, api_tokens.",
        "parameters": [
          {
            "name": "collection",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "tasks"
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "perPage",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 30
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "example": "-created"
          },
          {
            "name": "filter",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "example": "status='todo'"
          },
          {
            "name": "expand",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ]
      },
      "post": {
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "example": "tasks",
            "name": "collection",
            "in": "path",
            "required": true
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Validation error"
          },
          "403": {
            "description": "Forbidden by collection API rules"
          }
        },
        "tags": [
          "System"
        ],
        "summary": "Create a record in a collection",
        "operationId": "createCollectionRecord"
      }
    },
    "/collections/{collection}/records/{id}": {
      "patch": {
        "summary": "Update a record",
        "operationId": "updateCollectionRecord",
        "parameters": [
          {
            "name": "collection",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          },
          "404": {
            "description": "Not found"
          }
        },
        "tags": [
          "System"
        ]
      },
      "delete": {
        "operationId": "deleteCollectionRecord",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "name": "collection",
            "in": "path",
            "required": true
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found"
          }
        },
        "tags": [
          "System"
        ],
        "summary": "Delete a record"
      },
      "get": {
        "summary": "Get a single record",
        "operationId": "getCollectionRecord",
        "parameters": [
          {
            "name": "collection",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "required": true,
            "schema": {
              "type": "string"
            },
            "name": "id",
            "in": "path"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Record"
          },
          "404": {
            "description": "Not found"
          }
        },
        "tags": [
          "System"
        ]
      }
    },
    "/companion/devices/register": {
      "post": {
        "tags": [
          "System"
        ],
        "summary": "Register a companion device for push/notification delivery",
        "operationId": "registerCompanionDevice",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Registered"
          }
        }
      }
    },
    "/companion/notifications/test": {
      "post": {
        "operationId": "testCompanionNotification",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Sent"
          }
        },
        "tags": [
          "System"
        ],
        "summary": "Send a test notification to the caller's companion devices"
      }
    },
    "/docs": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Swagger UI",
        "description": "Swagger UI HTML page for interactive API exploration.",
        "operationId": "getSwaggerUi",
        "responses": {
          "200": {
            "description": "Swagger UI HTML page"
          }
        }
      }
    },
    "/entries": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of entries: a bare array, or { page, perPage, totalItems, totalPages, items } when page/perPage is given.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Entry"
                      }
                    },
                    {
                      "type": "object",
                      "properties": {
                        "totalItems": {
                          "type": "integer"
                        },
                        "totalPages": {
                          "type": "integer"
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Entry"
                          }
                        },
                        "page": {
                          "type": "integer"
                        },
                        "perPage": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid updated_since, page or perPage"
          }
        },
        "tags": [
          "Entries"
        ],
        "summary": "List unified entries",
        "description": "Returns all tasks and groceries for the authenticated user's family, with optional filters.",
        "operationId": "listEntries",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "task",
                "grocery"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "assignee_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "schema": {
              "type": "string"
            },
            "name": "label"
          },
          {
            "schema": {
              "type": "string"
            },
            "name": "shop_id",
            "in": "query"
          },
          {
            "name": "updated_since",
            "in": "query",
            "description": "ISO 8601 timestamp; only entries updated at or after it (incremental sync).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "1-based page. Supplying page or perPage switches the response to a paginated object.",
            "schema": {
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "name": "perPage",
            "in": "query",
            "description": "Page size (default 100, max 500).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          }
        ]
      }
    },
    "/groceries": {
      "post": {
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "title"
                ],
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": "integer"
                  },
                  "shop_id": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Item created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Item"
                }
              }
            }
          }
        },
        "tags": [
          "Items"
        ],
        "summary": "Create grocery item (fast path)",
        "operationId": "createGroceryFast"
      }
    },
    "/groceries/{itemId}": {
      "patch": {
        "parameters": [
          {
            "name": "itemId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Item updated"
          },
          "404": {
            "description": "Not found"
          }
        },
        "tags": [
          "Items"
        ],
        "summary": "Update grocery item (fast path)",
        "operationId": "updateGroceryFast"
      }
    },
    "/hook-health": {
      "get": {
        "description": "Returns { ok: true } if the hooks are alive. No auth required.",
        "operationId": "hookHealth",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "example": true,
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "System"
        ],
        "summary": "Health check"
      }
    },
    "/ics-export": {
      "get": {
        "operationId": "icsExport",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ ics, count }",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "ics": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            }
          }
        },
        "tags": [
          "Calendar"
        ],
        "summary": "Export tasks with due dates (JSON-wrapped .ics for the app)"
      }
    },
    "/ics-import": {
      "post": {
        "summary": "Import parsed .ics VEVENTs as tasks",
        "operationId": "icsImport",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Import result"
          },
          "400": {
            "description": "Bad request"
          }
        },
        "tags": [
          "Calendar"
        ]
      }
    },
    "/integrations/mail/webhook": {
      "post": {
        "security": [],
        "responses": {
          "503": {
            "description": "Webhook secret not configured"
          },
          "201": {
            "description": "Task created"
          },
          "400": {
            "description": "Missing from/subject"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "No user found for sender"
          }
        },
        "tags": [
          "System"
        ],
        "summary": "Email-to-task webhook",
        "operationId": "mailWebhook",
        "description": "Creates a task from an inbound email. Requires Bearer MAIL_WEBHOOK_SECRET.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "nullable": true
                  },
                  "family_id": {
                    "type": "string",
                    "nullable": true
                  },
                  "from": {
                    "type": "string"
                  },
                  "subject": {
                    "type": "string"
                  }
                },
                "required": [
                  "from",
                  "subject"
                ]
              }
            }
          }
        }
      }
    },
    "/invites/create": {
      "post": {
        "tags": [
          "Invites"
        ],
        "summary": "Create invite code (admin, server-side)",
        "description": "Admin/owner only. Generates a random uppercase invite code with 7-day expiry for the caller's family, bypassing PB API rules.",
        "operationId": "createInviteAdmin",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "human"
                    ],
                    "default": "human"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Invite created"
          },
          "400": {
            "description": "Bad request"
          },
          "403": {
            "description": "Admin only"
          }
        }
      }
    },
    "/members/{userId}/token": {
      "delete": {
        "tags": [
          "Users"
        ],
        "summary": "Revoke a member's personal API token",
        "operationId": "deleteMemberToken",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked"
          }
        }
      },
      "get": {
        "operationId": "getMemberToken",
        "parameters": [
          {
            "required": true,
            "schema": {
              "type": "string"
            },
            "name": "userId",
            "in": "path"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Token metadata (no secret)"
          }
        },
        "tags": [
          "Users"
        ],
        "summary": "Get member's personal API token metadata"
      },
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Issue a personal API token for a member",
        "operationId": "createMemberToken",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Token created — raw value shown once"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "OpenAPI spec",
        "description": "Returns this OpenAPI specification document.",
        "operationId": "getOpenApiSpec",
        "responses": {
          "200": {
            "description": "OpenAPI spec"
          }
        }
      }
    },
    "/register": {
      "post": {
        "summary": "Register user",
        "description": "Creates a new user account. First user becomes admin without invite. Subsequent users require an invite code or bootstrap mode.",
        "operationId": "registerUser",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password",
                  "passwordConfirm"
                ],
                "properties": {
                  "invite_code": {
                    "type": "string",
                    "nullable": true
                  },
                  "user_type": {
                    "type": "string",
                    "enum": [
                      "family_member",
                      "family_assistant"
                    ],
                    "default": "family_member"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "user@example.com"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "example": "securepass123"
                  },
                  "passwordConfirm": {
                    "type": "string",
                    "example": "securepass123"
                  },
                  "name": {
                    "type": "string",
                    "nullable": true
                  },
                  "family_name": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "User created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "role": {
                          "type": "string"
                        },
                        "family_id": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "tags": [
          "Auth"
        ]
      }
    },
    "/setup-status": {
      "get": {
        "tags": [
          "System",
          "Auth"
        ],
        "summary": "Setup status",
        "description": "Public endpoint returning bootstrap status for first-run onboarding detection.",
        "operationId": "setupStatus",
        "responses": {
          "200": {
            "description": "Setup status",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "has_users": {
                      "type": "boolean"
                    },
                    "setup_complete": {
                      "type": "boolean"
                    }
                  },
                  "example": {
                    "has_users": false,
                    "setup_complete": false
                  },
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/subtasks/{subtaskId}": {
      "patch": {
        "parameters": [
          {
            "name": "subtaskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Subtask updated"
          },
          "404": {
            "description": "Not found"
          }
        },
        "tags": [
          "Tasks"
        ],
        "summary": "Update subtask",
        "operationId": "updateSubtask"
      }
    },
    "/swagger": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Swagger UI (alias)",
        "description": "Alias of /api/docs.",
        "operationId": "getSwaggerUiAlias",
        "responses": {
          "200": {
            "description": "Swagger UI HTML page"
          }
        }
      }
    },
    "/tasks": {
      "post": {
        "summary": "Create task (with optional subtasks)",
        "operationId": "createTaskFast",
        "description": "Fast create endpoint. Accepts Bearer API token or PB session auth.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "linked_to": {
                    "type": "string",
                    "nullable": true
                  },
                  "linked_type": {
                    "nullable": true,
                    "type": "string"
                  },
                  "horizon": {
                    "type": "string"
                  },
                  "sprint_id": {
                    "nullable": true,
                    "type": "string"
                  },
                  "labels": {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  "is_private": {
                    "type": "boolean"
                  },
                  "flag": {
                    "type": "boolean"
                  },
                  "title": {
                    "type": "string"
                  },
                  "due_date": {
                    "type": "string",
                    "nullable": true
                  },
                  "completed_at": {
                    "type": "string",
                    "nullable": true
                  },
                  "linked_note_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "status": {
                    "type": "string"
                  },
                  "blocked": {
                    "type": "boolean"
                  },
                  "linked_item_ids": {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  "blocked_comment": {
                    "type": "string",
                    "nullable": true
                  },
                  "priority": {
                    "type": "string"
                  },
                  "assigned_to": {
                    "type": "string",
                    "nullable": true
                  },
                  "repeat_interval": {
                    "type": "string",
                    "nullable": true
                  },
                  "archived": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "title"
                ]
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Task created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "description": "title is required"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Missing permission: tasks:write"
          }
        },
        "tags": [
          "Tasks"
        ]
      }
    },
    "/tasks/{taskId}": {
      "patch": {
        "operationId": "updateTaskFast",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "blocked": {
                    "type": "boolean"
                  },
                  "horizon": {
                    "type": "string"
                  },
                  "sprint_id": {
                    "type": "string",
                    "nullable": true
                  },
                  "is_private": {
                    "type": "boolean"
                  },
                  "title": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string"
                  },
                  "repeat_interval": {
                    "type": "string",
                    "nullable": true
                  },
                  "completed_at": {
                    "nullable": true,
                    "type": "string"
                  },
                  "linked_to": {
                    "type": "string",
                    "nullable": true
                  },
                  "blocked_comment": {
                    "type": "string",
                    "nullable": true
                  },
                  "due_date": {
                    "type": "string",
                    "nullable": true
                  },
                  "archived": {
                    "type": "boolean"
                  },
                  "flag": {
                    "type": "boolean"
                  },
                  "linked_type": {
                    "type": "string",
                    "nullable": true
                  },
                  "linked_note_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "priority": {
                    "type": "string"
                  },
                  "assigned_to": {
                    "type": "string",
                    "nullable": true
                  },
                  "labels": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "linked_item_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "404": {
            "description": "Not found"
          },
          "200": {
            "description": "Task updated"
          }
        },
        "tags": [
          "Tasks"
        ],
        "summary": "Update task (fast path)"
      }
    },
    "/tasks/{taskId}/subtasks": {
      "post": {
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  }
                },
                "required": [
                  "title"
                ]
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Subtask created"
          },
          "404": {
            "description": "Parent task not found"
          }
        },
        "tags": [
          "Tasks"
        ],
        "summary": "Add subtask to a task",
        "operationId": "createSubtask",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1": {
      "post": {
        "tags": [
          "Entries"
        ],
        "summary": "Unified action dispatcher",
        "description": "Single endpoint for all entry (task+grocery) operations: list, create, update, complete, assign, delete, filters, add_subtask, set_role, set_user_block, delete_user. Unknown ids answer 404; invalid status/priority/due_date answer 400.",
        "operationId": "unifiedApi",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "description": {
                    "type": "string"
                  },
                  "linked_type": {
                    "type": "string",
                    "enum": [
                      "task",
                      "item",
                      "note"
                    ],
                    "description": "create: type of linked_to"
                  },
                  "subtask_id": {
                    "type": "string",
                    "description": "add_subtask: child task id"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "owner",
                      "admin",
                      "member",
                      "agent"
                    ],
                    "description": "set_role"
                  },
                  "shop_id": {
                    "type": "string"
                  },
                  "complete": {
                    "type": "boolean"
                  },
                  "priority": {
                    "description": "create/update",
                    "type": "string",
                    "enum": [
                      "low",
                      "medium",
                      "high"
                    ]
                  },
                  "linked_to": {
                    "description": "create: parent task id (must be accessible to the caller)",
                    "type": "string"
                  },
                  "user_id": {
                    "type": "string",
                    "description": "set_role / set_user_block / delete_user: target member"
                  },
                  "updated_since": {
                    "type": "string",
                    "format": "date-time",
                    "description": "list: only entries updated at or after this time"
                  },
                  "type": {
                    "enum": [
                      "task",
                      "grocery"
                    ],
                    "type": "string"
                  },
                  "assignee_id": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": "integer"
                  },
                  "task_id": {
                    "description": "add_subtask: parent task id",
                    "type": "string"
                  },
                  "perPage": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 500,
                    "description": "list: page size (default 100)"
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "list",
                      "create",
                      "update",
                      "complete",
                      "assign",
                      "delete",
                      "filters",
                      "add_subtask",
                      "set_role",
                      "set_user_block",
                      "delete_user"
                    ]
                  },
                  "title": {
                    "type": "string"
                  },
                  "labels": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "due_date": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true,
                    "description": "create/update; null or empty clears it on update"
                  },
                  "blocked": {
                    "type": "boolean",
                    "description": "set_user_block"
                  },
                  "page": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "list: 1-based page (switches to a paginated response)"
                  },
                  "id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Bad request (missing/invalid field)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Missing permission"
          },
          "404": {
            "description": "Entry, task or member not found (or not accessible)"
          },
          "200": {
            "description": "Success"
          }
        }
      }
    },
    "/v1/tasks/batch-delete": {
      "post": {
        "summary": "Batch delete tasks",
        "operationId": "batchDeleteTasks",
        "description": "Deletes multiple tasks in a single call (frontend 'Delete completed', GH#87). Ported from legacy pb_hooks/routes/tasks.js into the loaded 12_api_routes.pb.js in GH#31. Verifies every id exists and is owned by the caller before mutating anything; detaches deleted subtasks from surviving parents. Accepts Bearer API token with tasks:write or PB session auth.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "abc123",
                      "def456"
                    ]
                  }
                },
                "required": [
                  "ids"
                ]
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "404": {
            "description": "Task not found"
          },
          "413": {
            "description": "Payload too large: max 500 tasks per batch"
          },
          "200": {
            "description": "Batch deleted",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "deleted": {
                      "type": "integer"
                    },
                    "ids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "ids must be a non-empty array"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden (task not owned) or missing permission: tasks:write"
          }
        },
        "tags": [
          "Tasks"
        ]
      }
    },
    "/validate-invite": {
      "get": {
        "tags": [
          "Auth",
          "Invites"
        ],
        "summary": "Validate invite code",
        "description": "Public endpoint to check if an invite code is valid.",
        "operationId": "validateInvite",
        "parameters": [
          {
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "123456",
            "name": "code",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Invite status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "valid",
                        "not_found",
                        "used",
                        "expired"
                      ]
                    },
                    "message": {
                      "type": "string"
                    },
                    "invite": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "code": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "inviter": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string"
                            },
                            "name": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/version": {
      "get": {
        "description": "Returns branch/commit/env metadata for comparing deployments. No auth required.",
        "operationId": "getVersion",
        "responses": {
          "200": {
            "description": "Version info",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "branch": {
                      "type": "string"
                    },
                    "commit": {
                      "type": "string"
                    },
                    "env": {
                      "type": "string"
                    },
                    "pb": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "System"
        ],
        "summary": "Deployment version info"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "description": "PocketBase JWT token from auth-with-password or auth-refresh.",
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "cookieAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "pb_token",
        "description": "PocketBase auth cookie (set after login)."
      }
    },
    "schemas": {
      "Family": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "example": "My Family"
          },
          "created_by": {
            "type": "string"
          }
        }
      },
      "Sprint": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer"
          },
          "user": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "example": "Sprint 5"
          },
          "start_date": {
            "type": "string",
            "format": "date"
          },
          "end_date": {
            "type": "string",
            "format": "date"
          },
          "duration": {
            "type": "string",
            "example": "2weeks"
          },
          "week_number": {
            "type": "integer"
          }
        }
      },
      "Invite": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "example": "123456"
          },
          "used": {
            "type": "boolean",
            "default": false
          },
          "used_by": {
            "type": "string",
            "nullable": true
          },
          "used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "user": {
            "description": "Creator user ID",
            "type": "string"
          }
        }
      },
      "AiConfig": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "configured": {
            "type": "boolean"
          },
          "provider": {
            "type": "string",
            "example": "openai"
          },
          "api_url": {
            "type": "string",
            "example": "https://api.openai.com/v1"
          },
          "model": {
            "type": "string",
            "example": "gpt-4o-mini"
          },
          "max_tokens": {
            "type": "integer",
            "default": 1024
          },
          "temperature": {
            "type": "number",
            "default": 0.7
          }
        }
      },
      "Filters": {
        "type": "object",
        "properties": {
          "labels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Label"
            }
          },
          "shops": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Shop"
            }
          },
          "users": {
            "items": {
              "properties": {
                "active": {
                  "type": "boolean"
                },
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "role": {
                  "type": "string"
                }
              },
              "type": "object"
            },
            "type": "array"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "example": "Unauthorized",
            "type": "string"
          }
        }
      },
      "Shop": {
        "type": "object",
        "properties": {
          "color": {
            "type": "string",
            "example": "#6366f1"
          },
          "user": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "example": "Supermarket"
          }
        }
      },
      "Project": {
        "type": "object",
        "properties": {
          "task_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "due_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "user": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "example": "Home renovation"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "example": "#6366f1"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "completed",
              "archived"
            ]
          }
        }
      },
      "Entry": {
        "type": "object",
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "type": {
            "type": "string",
            "enum": [
              "task",
              "grocery"
            ],
            "description": "Entry type"
          },
          "title": {
            "type": "string",
            "example": "Buy milk"
          },
          "status": {
            "type": "string",
            "enum": [
              "todo",
              "done",
              "backlog"
            ],
            "description": "Tasks only; \"in_progress\" is accepted on input as an alias of \"todo\"",
            "example": "todo"
          },
          "assignee_id": {
            "type": "string",
            "description": "Assigned user ID",
            "nullable": true
          },
          "labels": {
            "example": [
              "shopping"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "shop_id": {
            "description": "Shop ID (grocery only)",
            "nullable": true,
            "type": "string"
          },
          "quantity": {
            "type": "integer",
            "description": "Quantity (grocery only)",
            "nullable": true
          },
          "created_by": {
            "type": "string",
            "description": "Creator user ID"
          },
          "id": {
            "description": "Record ID",
            "type": "string"
          },
          "description": {
            "example": "2% organic",
            "type": "string"
          },
          "completed_by": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CalendarEvent": {
        "type": "object",
        "properties": {
          "end_time": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "all_day": {
            "type": "boolean",
            "default": false
          },
          "task_id": {
            "type": "string",
            "nullable": true,
            "description": "Linked task ID"
          },
          "user": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "title": {
            "example": "Team standup",
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "start_time": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Note": {
        "type": "object",
        "properties": {
          "user": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "nullable": true,
            "example": "Meeting notes"
          },
          "content": {
            "type": "string",
            "example": "# Notes\nAction items..."
          },
          "pinned": {
            "type": "boolean",
            "default": false
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "linked_type": {
            "type": "string",
            "nullable": true
          },
          "linked_to": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Reward": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "example": "Star sticker"
          },
          "points": {
            "type": "integer",
            "default": 0
          },
          "earned_by": {
            "type": "string",
            "nullable": true
          },
          "task_id": {
            "type": "string",
            "nullable": true
          },
          "user": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "awarded_by": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "earned_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Reminder": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "example": "Doctor appointment"
          },
          "reminder_time": {
            "format": "date-time",
            "type": "string"
          },
          "fired": {
            "type": "boolean",
            "default": false
          },
          "dismissed": {
            "type": "boolean",
            "default": false
          },
          "repeat_interval": {
            "type": "string",
            "nullable": true
          },
          "user": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "nullable": true
          },
          "linked_type": {
            "nullable": true,
            "type": "string"
          },
          "linked_to": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Task": {
        "type": "object",
        "properties": {
          "flag": {
            "type": "boolean",
            "default": false
          },
          "linked_to": {
            "type": "string",
            "nullable": true,
            "description": "Linked entity ID"
          },
          "linked_item_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "linked_note_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "blocked_comment": {
            "type": "string",
            "nullable": true
          },
          "horizon": {
            "type": "string",
            "enum": [
              "week",
              "month",
              "3months",
              "6months",
              "year"
            ],
            "example": "week"
          },
          "due_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "sprint_id": {
            "nullable": true,
            "type": "string"
          },
          "is_private": {
            "type": "boolean",
            "default": false
          },
          "archived": {
            "type": "boolean",
            "default": false
          },
          "blocked": {
            "example": false,
            "type": "boolean"
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "example": "medium"
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "assigned_to": {
            "type": "string",
            "nullable": true
          },
          "user": {
            "type": "string",
            "description": "Creator user ID"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "example": "Fix login bug"
          },
          "status": {
            "example": "todo",
            "type": "string",
            "enum": [
              "todo",
              "done",
              "backlog"
            ]
          },
          "repeat_interval": {
            "type": "string",
            "nullable": true,
            "example": "weekly"
          },
          "linked_type": {
            "type": "string",
            "nullable": true,
            "description": "Linked entity type"
          },
          "completed_at": {
            "nullable": true,
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Item": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "completed": {
            "type": "boolean",
            "default": false
          },
          "shop_id": {
            "type": "string",
            "nullable": true
          },
          "priority": {
            "type": "string",
            "nullable": true
          },
          "assigned_to": {
            "type": "string",
            "nullable": true
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "user": {
            "type": "string"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "title": {
            "example": "Apples",
            "type": "string"
          },
          "quantity": {
            "type": "integer",
            "default": 1
          },
          "due_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "is_private": {
            "type": "boolean",
            "default": false
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member",
              "agent"
            ]
          },
          "family_id": {
            "type": "string",
            "nullable": true
          },
          "active": {
            "type": "boolean",
            "default": true
          },
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string"
          },
          "avatar": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Goal": {
        "type": "object",
        "properties": {
          "points_current": {
            "type": "integer",
            "default": 0
          },
          "target_user": {
            "nullable": true,
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "example": "Read 12 books"
          },
          "completed": {
            "type": "boolean",
            "default": false
          },
          "completed_at": {
            "nullable": true,
            "type": "string",
            "format": "date-time"
          },
          "user": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "points_required": {
            "type": "integer",
            "default": 0
          }
        }
      },
      "Label": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "example": "shopping"
          },
          "color": {
            "type": "string",
            "example": "#6366f1"
          },
          "is_private": {
            "type": "boolean",
            "default": false
          },
          "user": {
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        }
      }
    }
  }
}