{
  "components": {
    "schemas": {
      "ActivityEntry": {
        "description": "One row of the agent activity ledger.\n\n``before`` / ``after`` are the staged images (L5) parsed back from the stored\nJSON: ``before`` is the pre-mutation state and ``after`` the value written (a\ndelete has ``after == null`` with ``before`` set; a create has ``before ==\n{}``). Both are ``null`` for envelope-only captures. ``reversible`` is true\nonly when a before/after image was staged. ``reverted_at`` / ``reverted_by``\nare set once a later entry has undone this one.",
        "properties": {
          "after": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "After"
          },
          "before": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Before"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "method": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Method"
          },
          "operation_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Operation Id"
          },
          "path": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Path"
          },
          "principal_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Principal Id"
          },
          "principal_kind": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Principal Kind"
          },
          "principal_label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Principal Label"
          },
          "resource_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Resource Id"
          },
          "reversible": {
            "title": "Reversible",
            "type": "boolean"
          },
          "reverted_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reverted At"
          },
          "reverted_by": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reverted By"
          },
          "summary": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Summary"
          },
          "ts": {
            "title": "Ts",
            "type": "string"
          }
        },
        "required": [
          "id",
          "ts",
          "principal_kind",
          "principal_id",
          "principal_label",
          "operation_id",
          "method",
          "path",
          "resource_id",
          "summary",
          "before",
          "after",
          "reversible",
          "reverted_at",
          "reverted_by"
        ],
        "title": "ActivityEntry",
        "type": "object"
      },
      "ActivityListResponse": {
        "description": "A page of ledger entries, newest first. No pagination (L12).",
        "properties": {
          "entries": {
            "items": {
              "$ref": "#/components/schemas/ActivityEntry"
            },
            "title": "Entries",
            "type": "array"
          }
        },
        "required": [
          "entries"
        ],
        "title": "ActivityListResponse",
        "type": "object"
      },
      "AmbiguousItem": {
        "properties": {
          "candidates": {
            "items": {
              "$ref": "#/components/schemas/StatementDbMatch"
            },
            "title": "Candidates",
            "type": "array"
          },
          "cleaned_description": {
            "title": "Cleaned Description",
            "type": "string"
          },
          "enrichable": {
            "title": "Enrichable",
            "type": "boolean"
          },
          "index": {
            "title": "Index",
            "type": "integer"
          },
          "raw_description": {
            "title": "Raw Description",
            "type": "string"
          },
          "reason": {
            "title": "Reason",
            "type": "string"
          },
          "row_id": {
            "title": "Row Id",
            "type": "string"
          },
          "statement_txn": {
            "$ref": "#/components/schemas/RawStatementTxn"
          },
          "suggested_category": {
            "title": "Suggested Category",
            "type": "string"
          }
        },
        "required": [
          "index",
          "row_id",
          "statement_txn",
          "candidates",
          "reason",
          "cleaned_description",
          "raw_description",
          "suggested_category",
          "enrichable"
        ],
        "title": "AmbiguousItem",
        "type": "object"
      },
      "AppConfigResponse": {
        "properties": {
          "ai_categorization_enabled": {
            "title": "Ai Categorization Enabled",
            "type": "boolean"
          },
          "ai_extraction_enabled": {
            "title": "Ai Extraction Enabled",
            "type": "boolean"
          },
          "ai_receipt_parsing_enabled": {
            "default": false,
            "title": "Ai Receipt Parsing Enabled",
            "type": "boolean"
          },
          "ai_statement_parsing_enabled": {
            "default": false,
            "title": "Ai Statement Parsing Enabled",
            "type": "boolean"
          },
          "auth_bypass_for_dev": {
            "default": false,
            "title": "Auth Bypass For Dev",
            "type": "boolean"
          },
          "aws_available": {
            "title": "Aws Available",
            "type": "boolean"
          },
          "categorization_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Categorization Model"
          },
          "categorization_provider": {
            "default": "disabled",
            "enum": [
              "openai",
              "codex",
              "disabled"
            ],
            "title": "Categorization Provider",
            "type": "string"
          },
          "categorization_reasoning_effort": {
            "anyOf": [
              {
                "enum": [
                  "none",
                  "minimal",
                  "low",
                  "medium",
                  "high",
                  "xhigh",
                  "max"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Categorization Reasoning Effort"
          },
          "chatgpt_oauth_connected": {
            "title": "Chatgpt Oauth Connected",
            "type": "boolean"
          },
          "chatgpt_oauth_email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Chatgpt Oauth Email"
          },
          "claude_cli_available": {
            "title": "Claude Cli Available",
            "type": "boolean"
          },
          "codex_available": {
            "title": "Codex Available",
            "type": "boolean"
          },
          "daily_summary_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Daily Summary Model"
          },
          "daily_summary_provider": {
            "default": "disabled",
            "enum": [
              "claude_cli",
              "openai",
              "codex",
              "gemini_cli",
              "disabled"
            ],
            "title": "Daily Summary Provider",
            "type": "string"
          },
          "daily_summary_reasoning_effort": {
            "anyOf": [
              {
                "enum": [
                  "none",
                  "minimal",
                  "low",
                  "medium",
                  "high",
                  "xhigh",
                  "max"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Daily Summary Reasoning Effort"
          },
          "daily_summary_schedule_time": {
            "default": "19:00",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "title": "Daily Summary Schedule Time",
            "type": "string"
          },
          "demo_mode": {
            "title": "Demo Mode",
            "type": "boolean"
          },
          "document_parsing_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Document Parsing Model"
          },
          "document_parsing_provider": {
            "default": "disabled",
            "enum": [
              "claude_cli",
              "openai",
              "codex",
              "gemini_cli",
              "disabled"
            ],
            "title": "Document Parsing Provider",
            "type": "string"
          },
          "document_parsing_reasoning_effort": {
            "anyOf": [
              {
                "enum": [
                  "none",
                  "minimal",
                  "low",
                  "medium",
                  "high",
                  "xhigh",
                  "max"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Document Parsing Reasoning Effort"
          },
          "enable_daily_summaries": {
            "title": "Enable Daily Summaries",
            "type": "boolean"
          },
          "gemini_cli_available": {
            "title": "Gemini Cli Available",
            "type": "boolean"
          },
          "insights_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Insights Model"
          },
          "insights_provider": {
            "default": "disabled",
            "enum": [
              "claude_cli",
              "openai",
              "codex",
              "gemini_cli",
              "disabled"
            ],
            "title": "Insights Provider",
            "type": "string"
          },
          "insights_reasoning_effort": {
            "anyOf": [
              {
                "enum": [
                  "none",
                  "minimal",
                  "low",
                  "medium",
                  "high",
                  "xhigh",
                  "max"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Insights Reasoning Effort"
          },
          "insights_user_memo": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Insights User Memo"
          },
          "openai_enabled": {
            "title": "Openai Enabled",
            "type": "boolean"
          },
          "s3_backup_bucket": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "S3 Backup Bucket"
          },
          "s3_backup_enabled": {
            "default": false,
            "title": "S3 Backup Enabled",
            "type": "boolean"
          },
          "s3_backup_prefix": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "S3 Backup Prefix"
          },
          "storage": {
            "title": "Storage",
            "type": "string"
          },
          "tax_tracking_enabled": {
            "default": true,
            "title": "Tax Tracking Enabled",
            "type": "boolean"
          },
          "timezone": {
            "title": "Timezone",
            "type": "string"
          },
          "user_id": {
            "title": "User Id",
            "type": "string"
          }
        },
        "required": [
          "storage",
          "demo_mode",
          "user_id",
          "openai_enabled",
          "aws_available",
          "claude_cli_available",
          "codex_available",
          "gemini_cli_available",
          "chatgpt_oauth_connected",
          "chatgpt_oauth_email",
          "enable_daily_summaries",
          "ai_categorization_enabled",
          "ai_extraction_enabled",
          "timezone"
        ],
        "title": "AppConfigResponse",
        "type": "object"
      },
      "AppConfigUpdateRequest": {
        "properties": {
          "ai_categorization_enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ai Categorization Enabled"
          },
          "ai_extraction_enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ai Extraction Enabled"
          },
          "ai_receipt_parsing_enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ai Receipt Parsing Enabled"
          },
          "ai_statement_parsing_enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ai Statement Parsing Enabled"
          },
          "auth_bypass_for_dev": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Auth Bypass For Dev"
          },
          "categorization_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Categorization Model"
          },
          "categorization_provider": {
            "anyOf": [
              {
                "enum": [
                  "openai",
                  "codex",
                  "disabled"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Categorization Provider"
          },
          "categorization_reasoning_effort": {
            "anyOf": [
              {
                "enum": [
                  "none",
                  "minimal",
                  "low",
                  "medium",
                  "high",
                  "xhigh",
                  "max"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Categorization Reasoning Effort"
          },
          "daily_summary_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Daily Summary Model"
          },
          "daily_summary_provider": {
            "anyOf": [
              {
                "enum": [
                  "claude_cli",
                  "openai",
                  "codex",
                  "gemini_cli",
                  "disabled"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Daily Summary Provider"
          },
          "daily_summary_reasoning_effort": {
            "anyOf": [
              {
                "enum": [
                  "none",
                  "minimal",
                  "low",
                  "medium",
                  "high",
                  "xhigh",
                  "max"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Daily Summary Reasoning Effort"
          },
          "daily_summary_schedule_time": {
            "anyOf": [
              {
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Daily Summary Schedule Time"
          },
          "demo_mode": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Demo Mode"
          },
          "document_parsing_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Document Parsing Model"
          },
          "document_parsing_provider": {
            "anyOf": [
              {
                "enum": [
                  "claude_cli",
                  "openai",
                  "codex",
                  "gemini_cli",
                  "disabled"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Document Parsing Provider"
          },
          "document_parsing_reasoning_effort": {
            "anyOf": [
              {
                "enum": [
                  "none",
                  "minimal",
                  "low",
                  "medium",
                  "high",
                  "xhigh",
                  "max"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Document Parsing Reasoning Effort"
          },
          "enable_daily_summaries": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enable Daily Summaries"
          },
          "insights_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Insights Model"
          },
          "insights_provider": {
            "anyOf": [
              {
                "enum": [
                  "claude_cli",
                  "openai",
                  "codex",
                  "gemini_cli",
                  "disabled"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Insights Provider"
          },
          "insights_reasoning_effort": {
            "anyOf": [
              {
                "enum": [
                  "none",
                  "minimal",
                  "low",
                  "medium",
                  "high",
                  "xhigh",
                  "max"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Insights Reasoning Effort"
          },
          "insights_user_memo": {
            "anyOf": [
              {
                "maxLength": 2000,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Insights User Memo"
          },
          "s3_backup_bucket": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "S3 Backup Bucket"
          },
          "s3_backup_enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "S3 Backup Enabled"
          },
          "s3_backup_prefix": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "S3 Backup Prefix"
          },
          "storage": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Storage"
          },
          "tax_tracking_enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tax Tracking Enabled"
          },
          "timezone": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Timezone"
          },
          "user_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "User Id"
          }
        },
        "title": "AppConfigUpdateRequest",
        "type": "object"
      },
      "AttachmentDeleteResponse": {
        "properties": {
          "id": {
            "title": "Id",
            "type": "string"
          },
          "status": {
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "id",
          "status"
        ],
        "title": "AttachmentDeleteResponse",
        "type": "object"
      },
      "AttachmentListResponse": {
        "properties": {
          "attachments": {
            "items": {
              "$ref": "#/components/schemas/AttachmentResponse"
            },
            "title": "Attachments",
            "type": "array"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          }
        },
        "required": [
          "count",
          "attachments"
        ],
        "title": "AttachmentListResponse",
        "type": "object"
      },
      "AttachmentResponse": {
        "description": "A single attachment row as seen by the API.",
        "properties": {
          "content_type": {
            "title": "Content Type",
            "type": "string"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "kind": {
            "title": "Kind",
            "type": "string"
          },
          "original_filename": {
            "title": "Original Filename",
            "type": "string"
          },
          "parse_error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Parse Error"
          },
          "parse_json": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Parsed receipt data, when present.",
            "title": "Parse Json"
          },
          "parse_status": {
            "title": "Parse Status",
            "type": "string"
          },
          "sha256": {
            "title": "Sha256",
            "type": "string"
          },
          "size_bytes": {
            "title": "Size Bytes",
            "type": "integer"
          },
          "tx_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Linked transaction id; null when unfiled.",
            "title": "Tx Id"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "id",
          "original_filename",
          "content_type",
          "size_bytes",
          "sha256",
          "kind",
          "parse_status",
          "created_at",
          "updated_at"
        ],
        "title": "AttachmentResponse",
        "type": "object"
      },
      "AttentionListResponse": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "month": {
            "title": "Month",
            "type": "string"
          },
          "transactions": {
            "items": {
              "$ref": "#/components/schemas/TransactionResponse"
            },
            "title": "Transactions",
            "type": "array"
          }
        },
        "required": [
          "month",
          "count",
          "transactions"
        ],
        "title": "AttentionListResponse",
        "type": "object"
      },
      "Body_previewBackupImport": {
        "properties": {
          "file": {
            "contentMediaType": "application/octet-stream",
            "title": "File",
            "type": "string"
          }
        },
        "required": [
          "file"
        ],
        "title": "Body_previewBackupImport",
        "type": "object"
      },
      "Body_uploadAttachment": {
        "properties": {
          "file": {
            "contentMediaType": "application/octet-stream",
            "title": "File",
            "type": "string"
          },
          "kind": {
            "default": "receipt",
            "title": "Kind",
            "type": "string"
          },
          "tx_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tx Id"
          }
        },
        "required": [
          "file"
        ],
        "title": "Body_uploadAttachment",
        "type": "object"
      },
      "Body_uploadEml": {
        "properties": {
          "file": {
            "contentMediaType": "application/octet-stream",
            "title": "File",
            "type": "string"
          }
        },
        "required": [
          "file"
        ],
        "title": "Body_uploadEml",
        "type": "object"
      },
      "Body_uploadStatement": {
        "properties": {
          "file": {
            "contentMediaType": "application/octet-stream",
            "title": "File",
            "type": "string"
          }
        },
        "required": [
          "file"
        ],
        "title": "Body_uploadStatement",
        "type": "object"
      },
      "BriefingFigure": {
        "description": "A single dollar amount or percentage extracted from a briefing.\n\n`kind` is \"dollar\" or \"percent\"; `value` is the parsed magnitude; `snippet`\nis the surrounding markdown for human-readable reporting.",
        "properties": {
          "kind": {
            "enum": [
              "dollar",
              "percent"
            ],
            "title": "Kind",
            "type": "string"
          },
          "raw": {
            "title": "Raw",
            "type": "string"
          },
          "snippet": {
            "title": "Snippet",
            "type": "string"
          },
          "value": {
            "title": "Value",
            "type": "number"
          }
        },
        "required": [
          "raw",
          "kind",
          "value",
          "snippet"
        ],
        "title": "BriefingFigure",
        "type": "object"
      },
      "BriefingValidation": {
        "description": "Figure-check result for a saved briefing.\n\nPopulated when a `<stem>.validation.json` sidecar exists next to the saved\nmarkdown. `ok` is true when every extracted figure traces back to the\ncontext that fed the prompt; `unmatched` lists only the figures that did not\n(with snippets), keeping the payload light.",
        "properties": {
          "ok": {
            "title": "Ok",
            "type": "boolean"
          },
          "summary": {
            "$ref": "#/components/schemas/BriefingValidationSummary"
          },
          "unmatched": {
            "items": {
              "$ref": "#/components/schemas/BriefingFigure"
            },
            "title": "Unmatched",
            "type": "array"
          }
        },
        "required": [
          "ok",
          "summary"
        ],
        "title": "BriefingValidation",
        "type": "object"
      },
      "BriefingValidationSummary": {
        "description": "Roll-up counts for a briefing's figure check.",
        "properties": {
          "matched": {
            "title": "Matched",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          },
          "unmatched": {
            "title": "Unmatched",
            "type": "integer"
          }
        },
        "required": [
          "total",
          "matched",
          "unmatched"
        ],
        "title": "BriefingValidationSummary",
        "type": "object"
      },
      "BudgetCategoryConfig": {
        "properties": {
          "category_type": {
            "enum": [
              "fixed",
              "variable",
              "lumpy"
            ],
            "title": "Category Type",
            "type": "string"
          },
          "input_mode": {
            "enum": [
              "monthly",
              "yearly"
            ],
            "title": "Input Mode",
            "type": "string"
          },
          "monthly_amount": {
            "title": "Monthly Amount",
            "type": "number"
          },
          "target": {
            "title": "Target",
            "type": "number"
          }
        },
        "required": [
          "target",
          "input_mode",
          "monthly_amount",
          "category_type"
        ],
        "title": "BudgetCategoryConfig",
        "type": "object"
      },
      "BudgetCategoryConfigInput": {
        "properties": {
          "category_type": {
            "enum": [
              "fixed",
              "variable",
              "lumpy"
            ],
            "title": "Category Type",
            "type": "string"
          },
          "input_mode": {
            "enum": [
              "monthly",
              "yearly"
            ],
            "title": "Input Mode",
            "type": "string"
          },
          "target": {
            "title": "Target",
            "type": "number"
          }
        },
        "required": [
          "target",
          "input_mode",
          "category_type"
        ],
        "title": "BudgetCategoryConfigInput",
        "type": "object"
      },
      "BudgetConfigResponse": {
        "examples": [
          {
            "allocated_total": 31200.0,
            "categories": {
              "groceries": {
                "category_type": "variable",
                "input_mode": "monthly",
                "monthly_amount": 600.0,
                "target": 7200.0
              },
              "rent": {
                "category_type": "fixed",
                "input_mode": "monthly",
                "monthly_amount": 2000.0,
                "target": 24000.0
              }
            },
            "groups": [
              {
                "categories": [
                  "groceries",
                  "rent"
                ],
                "name": "Essentials"
              }
            ],
            "groups_version": 1,
            "spending_ceiling": 48000.0,
            "targets_version": 3,
            "unallocated": 16800.0,
            "year": 2026
          }
        ],
        "properties": {
          "allocated_total": {
            "title": "Allocated Total",
            "type": "number"
          },
          "categories": {
            "additionalProperties": {
              "$ref": "#/components/schemas/BudgetCategoryConfig"
            },
            "title": "Categories",
            "type": "object"
          },
          "groups": {
            "items": {
              "$ref": "#/components/schemas/BudgetGroupConfig"
            },
            "title": "Groups",
            "type": "array"
          },
          "groups_version": {
            "title": "Groups Version",
            "type": "integer"
          },
          "spending_ceiling": {
            "title": "Spending Ceiling",
            "type": "number"
          },
          "targets_version": {
            "title": "Targets Version",
            "type": "integer"
          },
          "unallocated": {
            "title": "Unallocated",
            "type": "number"
          },
          "year": {
            "title": "Year",
            "type": "integer"
          }
        },
        "required": [
          "year",
          "spending_ceiling",
          "categories",
          "groups",
          "targets_version",
          "groups_version",
          "allocated_total",
          "unallocated"
        ],
        "title": "BudgetConfigResponse",
        "type": "object"
      },
      "BudgetConfigUpdateRequest": {
        "properties": {
          "categories": {
            "additionalProperties": {
              "$ref": "#/components/schemas/BudgetCategoryConfigInput"
            },
            "title": "Categories",
            "type": "object"
          },
          "groups": {
            "items": {
              "$ref": "#/components/schemas/BudgetGroupConfig"
            },
            "title": "Groups",
            "type": "array"
          },
          "groups_version": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Groups Version"
          },
          "spending_ceiling": {
            "title": "Spending Ceiling",
            "type": "number"
          },
          "targets_version": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Targets Version"
          }
        },
        "required": [
          "spending_ceiling",
          "categories",
          "groups",
          "targets_version",
          "groups_version"
        ],
        "title": "BudgetConfigUpdateRequest",
        "type": "object"
      },
      "BudgetGroupConfig": {
        "properties": {
          "categories": {
            "items": {
              "type": "string"
            },
            "title": "Categories",
            "type": "array"
          },
          "name": {
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "name",
          "categories"
        ],
        "title": "BudgetGroupConfig",
        "type": "object"
      },
      "BudgetStatusResponse": {
        "properties": {
          "as_of": {
            "title": "As Of",
            "type": "string"
          },
          "compare_year": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Compare Year"
          },
          "elapsed_year_fraction": {
            "title": "Elapsed Year Fraction",
            "type": "number"
          },
          "groups": {
            "items": {
              "$ref": "#/components/schemas/GroupPace"
            },
            "title": "Groups",
            "type": "array"
          },
          "monthly_totals": {
            "items": {
              "type": "number"
            },
            "title": "Monthly Totals",
            "type": "array"
          },
          "overall": {
            "$ref": "#/components/schemas/PaceStatus"
          },
          "prior_year_total": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Prior Year Total"
          },
          "unbudgeted": {
            "items": {
              "$ref": "#/components/schemas/UnbudgetedCategory"
            },
            "title": "Unbudgeted",
            "type": "array"
          },
          "year": {
            "title": "Year",
            "type": "integer"
          }
        },
        "required": [
          "year",
          "as_of",
          "elapsed_year_fraction",
          "overall",
          "groups",
          "unbudgeted",
          "monthly_totals",
          "prior_year_total",
          "compare_year"
        ],
        "title": "BudgetStatusResponse",
        "type": "object"
      },
      "BulkActionUpdateItem": {
        "properties": {
          "action": {
            "enum": [
              "import",
              "skip",
              "enrich",
              "update"
            ],
            "title": "Action",
            "type": "string"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "tx_index": {
            "title": "Tx Index",
            "type": "integer"
          }
        },
        "required": [
          "tx_index",
          "action"
        ],
        "title": "BulkActionUpdateItem",
        "type": "object"
      },
      "BulkCategoryUpdateItem": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          }
        },
        "required": [
          "forwarded_to",
          "date_file_name",
          "category"
        ],
        "title": "BulkCategoryUpdateItem",
        "type": "object"
      },
      "BulkCategoryUpdateRequest": {
        "properties": {
          "source": {
            "default": "manual_bulk",
            "title": "Source",
            "type": "string"
          },
          "updates": {
            "items": {
              "$ref": "#/components/schemas/BulkCategoryUpdateItem"
            },
            "title": "Updates",
            "type": "array"
          }
        },
        "required": [
          "updates"
        ],
        "title": "BulkCategoryUpdateRequest",
        "type": "object"
      },
      "BulkCategoryUpdateResponse": {
        "properties": {
          "failed": {
            "title": "Failed",
            "type": "integer"
          },
          "results": {
            "items": {
              "$ref": "#/components/schemas/BulkCategoryUpdateResult"
            },
            "title": "Results",
            "type": "array"
          },
          "succeeded": {
            "title": "Succeeded",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "total",
          "succeeded",
          "failed",
          "results"
        ],
        "title": "BulkCategoryUpdateResponse",
        "type": "object"
      },
      "BulkCategoryUpdateResult": {
        "properties": {
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "new_category": {
            "title": "New Category",
            "type": "string"
          },
          "ok": {
            "title": "Ok",
            "type": "boolean"
          },
          "old_category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Old Category"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name",
          "new_category",
          "ok"
        ],
        "title": "BulkCategoryUpdateResult",
        "type": "object"
      },
      "BulkTransactionUpdate": {
        "properties": {
          "updates": {
            "items": {
              "$ref": "#/components/schemas/BulkActionUpdateItem"
            },
            "title": "Updates",
            "type": "array"
          }
        },
        "required": [
          "updates"
        ],
        "title": "BulkTransactionUpdate",
        "type": "object"
      },
      "BulkTransactionUpdateResponse": {
        "properties": {
          "ok": {
            "title": "Ok",
            "type": "boolean"
          },
          "updated": {
            "title": "Updated",
            "type": "integer"
          }
        },
        "required": [
          "ok",
          "updated"
        ],
        "title": "BulkTransactionUpdateResponse",
        "type": "object"
      },
      "CaptureBucket": {
        "description": "Overall caught/total/rate roll-up for the capture summary.",
        "properties": {
          "caught": {
            "title": "Caught",
            "type": "integer"
          },
          "rate": {
            "title": "Rate",
            "type": "number"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "caught",
          "total",
          "rate"
        ],
        "title": "CaptureBucket",
        "type": "object"
      },
      "CaptureBucketInstitution": {
        "description": "Per-institution capture-rate breakdown.",
        "properties": {
          "caught": {
            "title": "Caught",
            "type": "integer"
          },
          "institution": {
            "title": "Institution",
            "type": "string"
          },
          "rate": {
            "title": "Rate",
            "type": "number"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "institution",
          "caught",
          "total",
          "rate"
        ],
        "title": "CaptureBucketInstitution",
        "type": "object"
      },
      "CaptureBucketType": {
        "description": "Per-transaction-type capture-rate breakdown (withdrawal / deposit).",
        "properties": {
          "caught": {
            "title": "Caught",
            "type": "integer"
          },
          "rate": {
            "title": "Rate",
            "type": "number"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          },
          "type": {
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "caught",
          "total",
          "rate"
        ],
        "title": "CaptureBucketType",
        "type": "object"
      },
      "CaptureSummary": {
        "description": "Passive email-capture rate measured from statement reconciliation.\n\nPresent only when the user has imported statements (SQLite-only ledger);\n``CoverageResponse.capture`` is ``None`` on the DynamoDB path or when no\nreconciled rows exist.",
        "properties": {
          "by_institution": {
            "items": {
              "$ref": "#/components/schemas/CaptureBucketInstitution"
            },
            "title": "By Institution",
            "type": "array"
          },
          "by_type": {
            "items": {
              "$ref": "#/components/schemas/CaptureBucketType"
            },
            "title": "By Type",
            "type": "array"
          },
          "overall": {
            "$ref": "#/components/schemas/CaptureBucket"
          }
        },
        "required": [
          "overall",
          "by_institution",
          "by_type"
        ],
        "title": "CaptureSummary",
        "type": "object"
      },
      "CategoriesManagementResponse": {
        "properties": {
          "categories": {
            "items": {
              "$ref": "#/components/schemas/CategoryWithGroup"
            },
            "title": "Categories",
            "type": "array"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "groups": {
            "items": {
              "type": "string"
            },
            "title": "Groups",
            "type": "array"
          },
          "version": {
            "title": "Version",
            "type": "integer"
          }
        },
        "required": [
          "categories",
          "count",
          "version",
          "groups"
        ],
        "title": "CategoriesManagementResponse",
        "type": "object"
      },
      "CategoriesResponse": {
        "properties": {
          "categories": {
            "items": {
              "type": "string"
            },
            "title": "Categories",
            "type": "array"
          }
        },
        "required": [
          "categories"
        ],
        "title": "CategoriesResponse",
        "type": "object"
      },
      "CategoryAddRequest": {
        "properties": {
          "group": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Group"
          },
          "name": {
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "CategoryAddRequest",
        "type": "object"
      },
      "CategoryAnomaly": {
        "description": "A quietly-detected deviation from the prior 6-month baseline.\n\n`annotated_amount` is the portion of the month the user has already\nexplained via transaction comments; it is > 0 only when a comment partially\n(not fully) explains the deviation, so the anomaly is kept but noted.",
        "properties": {
          "annotated_amount": {
            "default": 0,
            "title": "Annotated Amount",
            "type": "number"
          },
          "baseline": {
            "title": "Baseline",
            "type": "number"
          },
          "category": {
            "title": "Category",
            "type": "string"
          },
          "current": {
            "title": "Current",
            "type": "number"
          },
          "reason": {
            "title": "Reason",
            "type": "string"
          },
          "severity": {
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "title": "Severity",
            "type": "string"
          }
        },
        "required": [
          "category",
          "current",
          "baseline",
          "severity",
          "reason"
        ],
        "title": "CategoryAnomaly",
        "type": "object"
      },
      "CategoryAudit": {
        "properties": {
          "confidence": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Confidence"
          },
          "fallback_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Fallback Reason"
          },
          "matched_rule": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Matched Rule"
          },
          "model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Model"
          },
          "previous_category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Previous Category"
          },
          "previous_source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Previous Source"
          },
          "reviewed_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reviewed At"
          },
          "schema_version": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Schema Version"
          },
          "source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source"
          },
          "tier": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tier"
          }
        },
        "title": "CategoryAudit",
        "type": "object"
      },
      "CategoryDeleteResponse": {
        "properties": {
          "deleted_name": {
            "title": "Deleted Name",
            "type": "string"
          },
          "reassigned_to": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reassigned To"
          },
          "transactions_reassigned": {
            "title": "Transactions Reassigned",
            "type": "integer"
          }
        },
        "required": [
          "deleted_name",
          "transactions_reassigned",
          "reassigned_to"
        ],
        "title": "CategoryDeleteResponse",
        "type": "object"
      },
      "CategoryDelta": {
        "description": "Month-over-month spend change for a single category.",
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "current": {
            "title": "Current",
            "type": "number"
          },
          "delta_amount": {
            "title": "Delta Amount",
            "type": "number"
          },
          "delta_pct": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Delta Pct"
          },
          "previous": {
            "title": "Previous",
            "type": "number"
          }
        },
        "required": [
          "category",
          "current",
          "previous",
          "delta_amount"
        ],
        "title": "CategoryDelta",
        "type": "object"
      },
      "CategoryGroupUpdateRequest": {
        "properties": {
          "group": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Group"
          }
        },
        "required": [
          "group"
        ],
        "title": "CategoryGroupUpdateRequest",
        "type": "object"
      },
      "CategoryGroupUpdateResponse": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "new_group": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "New Group"
          },
          "old_group": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Old Group"
          }
        },
        "required": [
          "category",
          "old_group",
          "new_group"
        ],
        "title": "CategoryGroupUpdateResponse",
        "type": "object"
      },
      "CategoryIconsResponse": {
        "properties": {
          "icons": {
            "additionalProperties": {
              "type": "string"
            },
            "title": "Icons",
            "type": "object"
          },
          "version": {
            "title": "Version",
            "type": "integer"
          }
        },
        "required": [
          "icons",
          "version"
        ],
        "title": "CategoryIconsResponse",
        "type": "object"
      },
      "CategoryPaceDetail": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "category_type": {
            "enum": [
              "fixed",
              "variable",
              "lumpy"
            ],
            "title": "Category Type",
            "type": "string"
          },
          "current_month_expected": {
            "title": "Current Month Expected",
            "type": "number"
          },
          "current_month_spent": {
            "title": "Current Month Spent",
            "type": "number"
          },
          "forecast_lower": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "P25 of the projection. None when history is too thin.",
            "title": "Forecast Lower"
          },
          "forecast_month_total": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Projected month-end total. Variable and fixed categories only.",
            "title": "Forecast Month Total"
          },
          "forecast_pct": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Projection as a percent of the monthly budget. None when the budget is 0.",
            "title": "Forecast Pct"
          },
          "forecast_quality": {
            "anyOf": [
              {
                "enum": [
                  "forecast",
                  "historical",
                  "limited",
                  "committed"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "forecast = curve-based projection; historical = typical-month average (early month or few transactions); limited = linear extrapolation from <2 months of history; committed = a recurring-dominated category (\u226570% of its trailing mean is expected recurring charges), projected as spent + still-expected committed charges + everyday remainder rather than a smeared curve (bounds are None).",
            "title": "Forecast Quality"
          },
          "forecast_upper": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "P75 of the projection. None when history is too thin.",
            "title": "Forecast Upper"
          },
          "input_mode": {
            "enum": [
              "monthly",
              "yearly"
            ],
            "title": "Input Mode",
            "type": "string"
          },
          "monthly_amount": {
            "title": "Monthly Amount",
            "type": "number"
          },
          "monthly_spent": {
            "items": {
              "type": "number"
            },
            "title": "Monthly Spent",
            "type": "array"
          },
          "pace_percent": {
            "title": "Pace Percent",
            "type": "number"
          },
          "prior_year_total": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Prior Year Total"
          },
          "status": {
            "enum": [
              "under",
              "on_track",
              "over"
            ],
            "title": "Status",
            "type": "string"
          },
          "target": {
            "title": "Target",
            "type": "number"
          },
          "variance": {
            "title": "Variance",
            "type": "number"
          },
          "ytd_expected": {
            "title": "Ytd Expected",
            "type": "number"
          },
          "ytd_spent": {
            "title": "Ytd Spent",
            "type": "number"
          }
        },
        "required": [
          "category",
          "target",
          "input_mode",
          "monthly_amount",
          "category_type",
          "current_month_spent",
          "current_month_expected",
          "ytd_spent",
          "ytd_expected",
          "variance",
          "pace_percent",
          "status",
          "monthly_spent",
          "prior_year_total"
        ],
        "title": "CategoryPaceDetail",
        "type": "object"
      },
      "CategoryRenameRequest": {
        "properties": {
          "new_name": {
            "title": "New Name",
            "type": "string"
          }
        },
        "required": [
          "new_name"
        ],
        "title": "CategoryRenameRequest",
        "type": "object"
      },
      "CategoryRenameResponse": {
        "properties": {
          "budget_groups_updated": {
            "title": "Budget Groups Updated",
            "type": "boolean"
          },
          "new_name": {
            "title": "New Name",
            "type": "string"
          },
          "old_name": {
            "title": "Old Name",
            "type": "string"
          },
          "overrides_updated": {
            "title": "Overrides Updated",
            "type": "integer"
          },
          "transactions_updated": {
            "title": "Transactions Updated",
            "type": "integer"
          }
        },
        "required": [
          "old_name",
          "new_name",
          "transactions_updated",
          "overrides_updated",
          "budget_groups_updated"
        ],
        "title": "CategoryRenameResponse",
        "type": "object"
      },
      "CategorySummary": {
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          }
        },
        "required": [
          "amount",
          "count"
        ],
        "title": "CategorySummary",
        "type": "object"
      },
      "CategoryUpdateRequest": {
        "description": "Partial PATCH for a transaction.\n\nAny field omitted = no change. ``state`` consolidates the three legacy\n``/review``, ``/ignore``, ``/delete`` POST endpoints into a single\nstate machine. ``category`` is the original purpose of this endpoint\nand remains supported.",
        "examples": [
          {
            "category": "Groceries"
          },
          {
            "state": "ignored"
          },
          {
            "state": "trashed"
          },
          {
            "reviewed": true
          },
          {
            "category": "Restaurants",
            "reviewed": true
          }
        ],
        "properties": {
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "reviewed": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reviewed"
          },
          "state": {
            "anyOf": [
              {
                "enum": [
                  "active",
                  "ignored",
                  "trashed"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "State"
          }
        },
        "title": "CategoryUpdateRequest",
        "type": "object"
      },
      "CategoryUpdateResponse": {
        "description": "Response shape for ``PATCH /transactions/{tx_id}``.\n\n``old_category`` / ``new_category`` are populated only when the request\nincluded ``category``. ``state`` / ``reviewed`` / ``deleted_at`` are\npopulated only when the corresponding request fields were set.",
        "properties": {
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "deleted_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Deleted At"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "new_category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "New Category"
          },
          "old_category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Old Category"
          },
          "reviewed": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reviewed"
          },
          "state": {
            "anyOf": [
              {
                "enum": [
                  "active",
                  "ignored",
                  "trashed"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "State"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name"
        ],
        "title": "CategoryUpdateResponse",
        "type": "object"
      },
      "CategoryUsageResponse": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "in_budget": {
            "title": "In Budget",
            "type": "boolean"
          },
          "in_group": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "In Group"
          },
          "override_count": {
            "title": "Override Count",
            "type": "integer"
          },
          "transaction_count": {
            "title": "Transaction Count",
            "type": "integer"
          }
        },
        "required": [
          "category",
          "transaction_count",
          "override_count",
          "in_budget",
          "in_group"
        ],
        "title": "CategoryUsageResponse",
        "type": "object"
      },
      "CategoryWithGroup": {
        "properties": {
          "group": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Group"
          },
          "name": {
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "name",
          "group"
        ],
        "title": "CategoryWithGroup",
        "type": "object"
      },
      "ChatgptLoginStatusResponse": {
        "properties": {
          "connected": {
            "title": "Connected",
            "type": "boolean"
          },
          "email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Email"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "pending": {
            "title": "Pending",
            "type": "boolean"
          },
          "user_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "User Code"
          },
          "verification_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Verification Url"
          }
        },
        "required": [
          "connected",
          "pending",
          "email",
          "error",
          "verification_url",
          "user_code"
        ],
        "title": "ChatgptLoginStatusResponse",
        "type": "object"
      },
      "CombinedTransactionsResponse": {
        "properties": {
          "attention": {
            "$ref": "#/components/schemas/AttentionListResponse"
          },
          "month": {
            "title": "Month",
            "type": "string"
          },
          "transactions": {
            "$ref": "#/components/schemas/TransactionListResponse"
          },
          "trash": {
            "$ref": "#/components/schemas/TransactionListResponse"
          }
        },
        "required": [
          "month",
          "transactions",
          "attention",
          "trash"
        ],
        "title": "CombinedTransactionsResponse",
        "type": "object"
      },
      "CommentRequest": {
        "properties": {
          "comment": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Comment"
          }
        },
        "title": "CommentRequest",
        "type": "object"
      },
      "CommentResponse": {
        "properties": {
          "comment": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Comment"
          },
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name",
          "comment"
        ],
        "title": "CommentResponse",
        "type": "object"
      },
      "CompanySummary": {
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "category": {
            "title": "Category",
            "type": "string"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          }
        },
        "required": [
          "amount",
          "count",
          "category"
        ],
        "title": "CompanySummary",
        "type": "object"
      },
      "ConfigPreview": {
        "description": "Summary of the config blobs that would be replaced on import.",
        "properties": {
          "budget_years_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Budget Years Count"
          },
          "categories_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Categories Count"
          },
          "merchant_aliases_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Merchant Aliases Count"
          },
          "overrides_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Overrides Count"
          }
        },
        "title": "ConfigPreview",
        "type": "object"
      },
      "CoverageInstitution": {
        "description": "One institution's modeled alert cadence over the trailing window.\n\n``median_gap_days`` / ``threshold_gap_days`` / ``dormant_cutoff_days`` are\n``None`` for ``irregular`` institutions \u2014 below the eligibility bar they have\nno meaningful cadence, so no thresholds are computed.",
        "properties": {
          "days_since_last_seen": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Days Since Last Seen"
          },
          "dormant_cutoff_days": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Dormant Cutoff Days"
          },
          "event_days": {
            "title": "Event Days",
            "type": "integer"
          },
          "institution": {
            "title": "Institution",
            "type": "string"
          },
          "last_seen_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Seen At"
          },
          "median_gap_days": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Median Gap Days"
          },
          "status": {
            "enum": [
              "active",
              "quiet",
              "dormant",
              "irregular"
            ],
            "title": "Status",
            "type": "string"
          },
          "threshold_gap_days": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Threshold Gap Days"
          }
        },
        "required": [
          "institution",
          "status",
          "last_seen_at",
          "days_since_last_seen",
          "median_gap_days",
          "threshold_gap_days",
          "dormant_cutoff_days",
          "event_days"
        ],
        "title": "CoverageInstitution",
        "type": "object"
      },
      "CoverageResponse": {
        "description": "Full payload for ``GET /api/v1/coverage``.",
        "properties": {
          "capture": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CaptureSummary"
              },
              {
                "type": "null"
              }
            ]
          },
          "checked_at": {
            "title": "Checked At",
            "type": "string"
          },
          "institutions": {
            "items": {
              "$ref": "#/components/schemas/CoverageInstitution"
            },
            "title": "Institutions",
            "type": "array"
          },
          "window_months": {
            "title": "Window Months",
            "type": "integer"
          }
        },
        "required": [
          "institutions",
          "capture",
          "window_months",
          "checked_at"
        ],
        "title": "CoverageResponse",
        "type": "object"
      },
      "DaySummariesResponse": {
        "properties": {
          "month": {
            "title": "Month",
            "type": "string"
          },
          "summaries": {
            "additionalProperties": {
              "type": "string"
            },
            "title": "Summaries",
            "type": "object"
          }
        },
        "required": [
          "month",
          "summaries"
        ],
        "title": "DaySummariesResponse",
        "type": "object"
      },
      "DaySummaryGenerateRequest": {
        "properties": {
          "dates": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Dates"
          },
          "force": {
            "default": false,
            "title": "Force",
            "type": "boolean"
          },
          "month": {
            "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
            "title": "Month",
            "type": "string"
          }
        },
        "required": [
          "month"
        ],
        "title": "DaySummaryGenerateRequest",
        "type": "object"
      },
      "DaySummaryGenerateResponse": {
        "properties": {
          "dates_queued": {
            "title": "Dates Queued",
            "type": "integer"
          },
          "month": {
            "title": "Month",
            "type": "string"
          },
          "status": {
            "enum": [
              "running",
              "idle"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status",
          "month",
          "dates_queued"
        ],
        "title": "DaySummaryGenerateResponse",
        "type": "object"
      },
      "DaySummaryStatusResponse": {
        "properties": {
          "completed": {
            "default": 0,
            "title": "Completed",
            "type": "integer"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "month": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Month"
          },
          "status": {
            "enum": [
              "idle",
              "running",
              "error"
            ],
            "title": "Status",
            "type": "string"
          },
          "total": {
            "default": 0,
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "status"
        ],
        "title": "DaySummaryStatusResponse",
        "type": "object"
      },
      "DeleteRequest": {
        "properties": {
          "deleted": {
            "title": "Deleted",
            "type": "boolean"
          }
        },
        "required": [
          "deleted"
        ],
        "title": "DeleteRequest",
        "type": "object"
      },
      "DeleteResponse": {
        "examples": [
          {
            "date_file_name": "2026.04.15_09.32_email_01.eml",
            "deleted_at": "2026-04-20T18:42:11Z",
            "forwarded_to": "default@local",
            "tx_id": "ZGVmYXVsdEBsb2NhbHwyMDI2LjA0LjE1XzA5LjMyX2VtYWlsXzAxLmVtbA"
          }
        ],
        "properties": {
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "deleted_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Deleted At"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name",
          "deleted_at"
        ],
        "title": "DeleteResponse",
        "type": "object"
      },
      "DepositSourceSummary": {
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          }
        },
        "required": [
          "amount",
          "count"
        ],
        "title": "DepositSourceSummary",
        "type": "object"
      },
      "DisconnectChatgptResponse": {
        "properties": {
          "ok": {
            "title": "Ok",
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "DisconnectChatgptResponse",
        "type": "object"
      },
      "DismissResponse": {
        "properties": {
          "failure_id": {
            "title": "Failure Id",
            "type": "string"
          },
          "status": {
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "failure_id",
          "status"
        ],
        "title": "DismissResponse",
        "type": "object"
      },
      "DismissSuggestionRequest": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "company": {
            "title": "Company",
            "type": "string"
          }
        },
        "required": [
          "company",
          "category"
        ],
        "title": "DismissSuggestionRequest",
        "type": "object"
      },
      "DismissedIgnoreRuleSuggestion": {
        "description": "A single dismissed suggestion, shown in the management view.\n\n``merchant`` carries the original casing recorded at dismissal time;\n``dismissed_at`` is an ISO timestamp (empty only for legacy backups that\nnever stored one).",
        "properties": {
          "dismissed_at": {
            "title": "Dismissed At",
            "type": "string"
          },
          "merchant": {
            "title": "Merchant",
            "type": "string"
          }
        },
        "required": [
          "merchant",
          "dismissed_at"
        ],
        "title": "DismissedIgnoreRuleSuggestion",
        "type": "object"
      },
      "DismissedIgnoreRuleSuggestionsResponse": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "dismissed": {
            "items": {
              "$ref": "#/components/schemas/DismissedIgnoreRuleSuggestion"
            },
            "title": "Dismissed",
            "type": "array"
          }
        },
        "required": [
          "dismissed",
          "count"
        ],
        "title": "DismissedIgnoreRuleSuggestionsResponse",
        "type": "object"
      },
      "ExpectedChargeInfo": {
        "description": "One expected recurring charge for the current month (see ``UpcomingService``).\n\nStatus is derived at query time, never stored: ``upcoming`` (day still\nahead), ``arrived`` (matched an observed row \u2014 carries ``actual_*``),\n``assumed`` (day passed, statement-observed, awaiting import \u2014 counts in the\nprojection, never alarming), ``unrecorded`` (day + grace passed for an\nemail-observed merchant \u2014 a quiet note that counts in NO committed term).",
        "properties": {
          "actual_amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Actual Amount"
          },
          "actual_date": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Actual Date"
          },
          "amount_estimate": {
            "title": "Amount Estimate",
            "type": "number"
          },
          "cadence": {
            "title": "Cadence",
            "type": "string"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "channel": {
            "title": "Channel",
            "type": "string"
          },
          "display_name": {
            "title": "Display Name",
            "type": "string"
          },
          "expected_day": {
            "title": "Expected Day",
            "type": "integer"
          },
          "merchant": {
            "title": "Merchant",
            "type": "string"
          },
          "previous_amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Previous Amount"
          },
          "status": {
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "merchant",
          "display_name",
          "amount_estimate",
          "expected_day",
          "status",
          "channel",
          "cadence"
        ],
        "title": "ExpectedChargeInfo",
        "type": "object"
      },
      "ExpenseCategoryRow": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "companies": {
            "items": {
              "$ref": "#/components/schemas/IncomeCompanyRow"
            },
            "title": "Companies",
            "type": "array"
          },
          "months": {
            "items": {
              "type": "number"
            },
            "title": "Months",
            "type": "array"
          },
          "total": {
            "title": "Total",
            "type": "number"
          }
        },
        "required": [
          "category",
          "months",
          "total",
          "companies"
        ],
        "title": "ExpenseCategoryRow",
        "type": "object"
      },
      "ExpenseSectionResponse": {
        "properties": {
          "annual_total": {
            "title": "Annual Total",
            "type": "number"
          },
          "categories": {
            "items": {
              "$ref": "#/components/schemas/ExpenseCategoryRow"
            },
            "title": "Categories",
            "type": "array"
          },
          "display_name": {
            "title": "Display Name",
            "type": "string"
          },
          "monthly_totals": {
            "items": {
              "type": "number"
            },
            "title": "Monthly Totals",
            "type": "array"
          },
          "type_name": {
            "title": "Type Name",
            "type": "string"
          }
        },
        "required": [
          "type_name",
          "display_name",
          "categories",
          "monthly_totals",
          "annual_total"
        ],
        "title": "ExpenseSectionResponse",
        "type": "object"
      },
      "ExtractionAudit": {
        "description": "Provenance for a transaction recovered via the AI extraction fallback.\n\nPresent only on rows the parsers couldn't read but the constrained LLM\nextraction recovered (parse-failure-quarantine path). Absent on\nparser-extracted and statement-imported rows.",
        "properties": {
          "extracted_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Extracted At"
          },
          "method": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Method"
          },
          "model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Model"
          },
          "schema_version": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Schema Version"
          },
          "validated": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Validated"
          }
        },
        "title": "ExtractionAudit",
        "type": "object"
      },
      "FixedCharge": {
        "description": "A recurring flat merchant/category charge, stable across the 6-month baseline.",
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "company": {
            "title": "Company",
            "type": "string"
          },
          "monthly_amount": {
            "title": "Monthly Amount",
            "type": "number"
          },
          "months_active": {
            "title": "Months Active",
            "type": "integer"
          }
        },
        "required": [
          "company",
          "category",
          "monthly_amount",
          "months_active"
        ],
        "title": "FixedCharge",
        "type": "object"
      },
      "FixtureFromFailureRequest": {
        "description": "Optional target institution for a to-fixture write.\n\nWhen present, its slug (lowercase, spaces\u2192underscores, non-alphanumerics\nstripped) names the ``tests/test_data/<dir>/`` folder. When omitted, the\nfailure's ``detected_institution`` lowercased is used instead.",
        "properties": {
          "institution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Target institution; falls back to the failure's detected institution.",
            "title": "Institution"
          }
        },
        "title": "FixtureFromFailureRequest",
        "type": "object"
      },
      "FixtureFromFailureResponse": {
        "description": "Repo-relative paths of the two fixture files written.",
        "properties": {
          "json_path": {
            "title": "Json Path",
            "type": "string"
          },
          "txt_path": {
            "title": "Txt Path",
            "type": "string"
          }
        },
        "required": [
          "txt_path",
          "json_path"
        ],
        "title": "FixtureFromFailureResponse",
        "type": "object"
      },
      "GroupPace": {
        "properties": {
          "budgeted_total": {
            "title": "Budgeted Total",
            "type": "number"
          },
          "categories": {
            "items": {
              "$ref": "#/components/schemas/CategoryPaceDetail"
            },
            "title": "Categories",
            "type": "array"
          },
          "expected_pace": {
            "title": "Expected Pace",
            "type": "number"
          },
          "monthly_totals": {
            "items": {
              "type": "number"
            },
            "title": "Monthly Totals",
            "type": "array"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "prior_year_total": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Prior Year Total"
          },
          "status": {
            "enum": [
              "under",
              "on_track",
              "over"
            ],
            "title": "Status",
            "type": "string"
          },
          "variance": {
            "title": "Variance",
            "type": "number"
          },
          "ytd_spent": {
            "title": "Ytd Spent",
            "type": "number"
          }
        },
        "required": [
          "name",
          "budgeted_total",
          "ytd_spent",
          "expected_pace",
          "variance",
          "status",
          "categories",
          "monthly_totals",
          "prior_year_total"
        ],
        "title": "GroupPace",
        "type": "object"
      },
      "GroupsResponse": {
        "properties": {
          "groups": {
            "items": {
              "$ref": "#/components/schemas/BudgetGroupConfig"
            },
            "title": "Groups",
            "type": "array"
          },
          "version": {
            "title": "Version",
            "type": "integer"
          },
          "year": {
            "title": "Year",
            "type": "integer"
          }
        },
        "required": [
          "year",
          "groups",
          "version"
        ],
        "title": "GroupsResponse",
        "type": "object"
      },
      "GroupsUpdateRequest": {
        "properties": {
          "groups": {
            "items": {
              "$ref": "#/components/schemas/BudgetGroupConfig"
            },
            "title": "Groups",
            "type": "array"
          },
          "version": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Version"
          }
        },
        "required": [
          "groups",
          "version"
        ],
        "title": "GroupsUpdateRequest",
        "type": "object"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "title": "HTTPValidationError",
        "type": "object"
      },
      "HealthResponse": {
        "description": "Liveness + last-activity probe. Unauthenticated; no PII.\n\nFields are all primitives so the payload is trivially cacheable and\nparseable by uptime monitors. Timestamps are ISO-8601 UTC strings\n(``YYYY-MM-DDTHH:MM:SSZ`` or with offset), or ``null`` when the\nunderlying source has never been populated.",
        "properties": {
          "ai_categorization_status": {
            "anyOf": [
              {
                "enum": [
                  "ok",
                  "degraded"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ai Categorization Status"
          },
          "ai_last_error_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ai Last Error Reason"
          },
          "auth_required": {
            "title": "Auth Required",
            "type": "boolean"
          },
          "backend": {
            "title": "Backend",
            "type": "string"
          },
          "checked_at": {
            "title": "Checked At",
            "type": "string"
          },
          "imap_last_poll": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Imap Last Poll"
          },
          "imap_poll_age_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Imap Poll Age Seconds"
          },
          "last_transaction_age_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Transaction Age Seconds"
          },
          "last_transaction_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Transaction At"
          },
          "parse_failures_7d": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Parse Failures 7D"
          },
          "quiet_institutions": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Quiet Institutions"
          },
          "status": {
            "enum": [
              "ok",
              "degraded",
              "stale"
            ],
            "title": "Status",
            "type": "string"
          },
          "version": {
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "status",
          "version",
          "backend",
          "imap_last_poll",
          "imap_poll_age_seconds",
          "last_transaction_at",
          "last_transaction_age_seconds",
          "parse_failures_7d",
          "checked_at",
          "auth_required"
        ],
        "title": "HealthResponse",
        "type": "object"
      },
      "HistoricalAveragesResponse": {
        "properties": {
          "categories": {
            "additionalProperties": {
              "$ref": "#/components/schemas/HistoricalCategoryAverage"
            },
            "title": "Categories",
            "type": "object"
          },
          "months_analyzed": {
            "title": "Months Analyzed",
            "type": "integer"
          },
          "period": {
            "additionalProperties": {
              "type": "string"
            },
            "title": "Period",
            "type": "object"
          }
        },
        "required": [
          "months_analyzed",
          "period",
          "categories"
        ],
        "title": "HistoricalAveragesResponse",
        "type": "object"
      },
      "HistoricalCategoryAverage": {
        "properties": {
          "monthly_avg": {
            "title": "Monthly Avg",
            "type": "number"
          },
          "months_active": {
            "title": "Months Active",
            "type": "integer"
          },
          "suggested_annual": {
            "title": "Suggested Annual",
            "type": "number"
          },
          "suggested_monthly": {
            "title": "Suggested Monthly",
            "type": "number"
          },
          "suggested_type": {
            "title": "Suggested Type",
            "type": "string"
          },
          "total": {
            "title": "Total",
            "type": "number"
          }
        },
        "required": [
          "monthly_avg",
          "total",
          "months_active",
          "suggested_type",
          "suggested_monthly",
          "suggested_annual"
        ],
        "title": "HistoricalCategoryAverage",
        "type": "object"
      },
      "IgnoreRequest": {
        "properties": {
          "ignored": {
            "title": "Ignored",
            "type": "boolean"
          }
        },
        "required": [
          "ignored"
        ],
        "title": "IgnoreRequest",
        "type": "object"
      },
      "IgnoreResponse": {
        "examples": [
          {
            "date_file_name": "2026.04.15_09.32_email_01.eml",
            "forwarded_to": "default@local",
            "ignored": true,
            "tx_id": "ZGVmYXVsdEBsb2NhbHwyMDI2LjA0LjE1XzA5LjMyX2VtYWlsXzAxLmVtbA"
          }
        ],
        "properties": {
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "ignored": {
            "title": "Ignored",
            "type": "boolean"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name",
          "ignored"
        ],
        "title": "IgnoreResponse",
        "type": "object"
      },
      "IgnoreRuleAddRequest": {
        "properties": {
          "pattern": {
            "title": "Pattern",
            "type": "string"
          }
        },
        "required": [
          "pattern"
        ],
        "title": "IgnoreRuleAddRequest",
        "type": "object"
      },
      "IgnoreRuleApplyRequest": {
        "description": "Backfill Ignored on existing transactions.\n\nWhen ``pattern`` is set, only that rule is applied; when null, every rule\nis applied. Only rows currently un-ignored are changed, so a rerun is a\nno-op and the counts report exactly what moved.",
        "properties": {
          "pattern": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pattern"
          }
        },
        "title": "IgnoreRuleApplyRequest",
        "type": "object"
      },
      "IgnoreRuleApplyResponse": {
        "properties": {
          "results": {
            "items": {
              "$ref": "#/components/schemas/IgnoreRuleApplyResult"
            },
            "title": "Results",
            "type": "array"
          },
          "total_matched": {
            "title": "Total Matched",
            "type": "integer"
          },
          "total_updated": {
            "title": "Total Updated",
            "type": "integer"
          }
        },
        "required": [
          "results",
          "total_matched",
          "total_updated"
        ],
        "title": "IgnoreRuleApplyResponse",
        "type": "object"
      },
      "IgnoreRuleApplyResult": {
        "properties": {
          "matched": {
            "title": "Matched",
            "type": "integer"
          },
          "pattern": {
            "title": "Pattern",
            "type": "string"
          },
          "updated": {
            "title": "Updated",
            "type": "integer"
          }
        },
        "required": [
          "pattern",
          "matched",
          "updated"
        ],
        "title": "IgnoreRuleApplyResult",
        "type": "object"
      },
      "IgnoreRuleDeleteResponse": {
        "properties": {
          "detail": {
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "detail"
        ],
        "title": "IgnoreRuleDeleteResponse",
        "type": "object"
      },
      "IgnoreRuleEntry": {
        "properties": {
          "pattern": {
            "title": "Pattern",
            "type": "string"
          }
        },
        "required": [
          "pattern"
        ],
        "title": "IgnoreRuleEntry",
        "type": "object"
      },
      "IgnoreRuleListResponse": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "rules": {
            "items": {
              "$ref": "#/components/schemas/IgnoreRuleEntry"
            },
            "title": "Rules",
            "type": "array"
          },
          "version": {
            "title": "Version",
            "type": "integer"
          }
        },
        "required": [
          "rules",
          "count",
          "version"
        ],
        "title": "IgnoreRuleListResponse",
        "type": "object"
      },
      "IgnoreRuleSuggestion": {
        "properties": {
          "ignored_count": {
            "title": "Ignored Count",
            "type": "integer"
          },
          "merchant": {
            "title": "Merchant",
            "type": "string"
          },
          "share": {
            "title": "Share",
            "type": "number"
          },
          "total_count": {
            "title": "Total Count",
            "type": "integer"
          }
        },
        "required": [
          "merchant",
          "total_count",
          "ignored_count",
          "share"
        ],
        "title": "IgnoreRuleSuggestion",
        "type": "object"
      },
      "IgnoreRuleSuggestionDismissRequest": {
        "description": "Dismiss a suggested merchant so it stops being surfaced.\n\nThe dismissal persists until reversed via the un-dismiss endpoint \u2014 unlike\noverride-suggestion dismissals, an ignore suggestion never resurfaces on its\nown (there is no per-merchant \"newer correction\" signal to compare against).",
        "properties": {
          "merchant": {
            "title": "Merchant",
            "type": "string"
          }
        },
        "required": [
          "merchant"
        ],
        "title": "IgnoreRuleSuggestionDismissRequest",
        "type": "object"
      },
      "IgnoreRuleSuggestionDismissResponse": {
        "properties": {
          "detail": {
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "detail"
        ],
        "title": "IgnoreRuleSuggestionDismissResponse",
        "type": "object"
      },
      "IgnoreRuleSuggestionUndismissResponse": {
        "properties": {
          "detail": {
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "detail"
        ],
        "title": "IgnoreRuleSuggestionUndismissResponse",
        "type": "object"
      },
      "IgnoreRuleSuggestionsResponse": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "suggestions": {
            "items": {
              "$ref": "#/components/schemas/IgnoreRuleSuggestion"
            },
            "title": "Suggestions",
            "type": "array"
          }
        },
        "required": [
          "suggestions",
          "count"
        ],
        "title": "IgnoreRuleSuggestionsResponse",
        "type": "object"
      },
      "ImportAction": {
        "properties": {
          "action": {
            "enum": [
              "import",
              "skip",
              "enrich",
              "update"
            ],
            "title": "Action",
            "type": "string"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "date_file_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Date File Name"
          },
          "forwarded_to": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Forwarded To"
          },
          "index": {
            "title": "Index",
            "type": "integer"
          }
        },
        "required": [
          "index",
          "action"
        ],
        "title": "ImportAction",
        "type": "object"
      },
      "ImportCommitRequest": {
        "properties": {
          "apply_config": {
            "default": true,
            "title": "Apply Config",
            "type": "boolean"
          },
          "strategy": {
            "default": "skip",
            "enum": [
              "skip",
              "overwrite",
              "keep_both"
            ],
            "title": "Strategy",
            "type": "string"
          },
          "token": {
            "title": "Token",
            "type": "string"
          }
        },
        "required": [
          "token"
        ],
        "title": "ImportCommitRequest",
        "type": "object"
      },
      "ImportPreviewCounts": {
        "properties": {
          "duplicates": {
            "title": "Duplicates",
            "type": "integer"
          },
          "invalid": {
            "title": "Invalid",
            "type": "integer"
          },
          "new": {
            "title": "New",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "total",
          "new",
          "duplicates",
          "invalid"
        ],
        "title": "ImportPreviewCounts",
        "type": "object"
      },
      "ImportPreviewResponse": {
        "description": "Dry-run of an import. Call /data/import/commit with `token` to apply.",
        "properties": {
          "config": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ConfigPreview"
              },
              {
                "type": "null"
              }
            ]
          },
          "counts": {
            "$ref": "#/components/schemas/ImportPreviewCounts"
          },
          "filename": {
            "title": "Filename",
            "type": "string"
          },
          "sample_duplicates": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/ImportPreviewSample"
            },
            "title": "Sample Duplicates",
            "type": "array"
          },
          "sample_invalid": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/ImportPreviewSample"
            },
            "title": "Sample Invalid",
            "type": "array"
          },
          "sample_new": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/ImportPreviewSample"
            },
            "title": "Sample New",
            "type": "array"
          },
          "source_kind": {
            "title": "Source Kind",
            "type": "string"
          },
          "token": {
            "description": "Staged-import token. Commit within 15 minutes.",
            "title": "Token",
            "type": "string"
          }
        },
        "required": [
          "token",
          "filename",
          "source_kind",
          "counts"
        ],
        "title": "ImportPreviewResponse",
        "type": "object"
      },
      "ImportPreviewSample": {
        "description": "One row from the preview buckets, trimmed for display.",
        "properties": {
          "amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Amount"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "date": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Date"
          },
          "reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reason"
          }
        },
        "title": "ImportPreviewSample",
        "type": "object"
      },
      "ImportRequest": {
        "properties": {
          "actions": {
            "items": {
              "$ref": "#/components/schemas/ImportAction"
            },
            "title": "Actions",
            "type": "array"
          },
          "filename": {
            "title": "Filename",
            "type": "string"
          },
          "metadata": {
            "$ref": "#/components/schemas/StatementMetadata"
          },
          "statement_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Statement Id"
          },
          "transactions": {
            "items": {
              "$ref": "#/components/schemas/StatementTransaction"
            },
            "title": "Transactions",
            "type": "array"
          }
        },
        "required": [
          "actions",
          "metadata",
          "transactions",
          "filename"
        ],
        "title": "ImportRequest",
        "type": "object"
      },
      "ImportResponse": {
        "properties": {
          "duplicates": {
            "title": "Duplicates",
            "type": "integer"
          },
          "enriched": {
            "default": 0,
            "title": "Enriched",
            "type": "integer"
          },
          "imported": {
            "title": "Imported",
            "type": "integer"
          },
          "skipped": {
            "title": "Skipped",
            "type": "integer"
          },
          "updated": {
            "default": 0,
            "title": "Updated",
            "type": "integer"
          }
        },
        "required": [
          "imported",
          "skipped",
          "duplicates"
        ],
        "title": "ImportResponse",
        "type": "object"
      },
      "ImportResult": {
        "properties": {
          "config_applied": {
            "default": false,
            "title": "Config Applied",
            "type": "boolean"
          },
          "config_details": {
            "additionalProperties": true,
            "title": "Config Details",
            "type": "object"
          },
          "errors": {
            "default": 0,
            "title": "Errors",
            "type": "integer"
          },
          "inserted": {
            "title": "Inserted",
            "type": "integer"
          },
          "invalid": {
            "title": "Invalid",
            "type": "integer"
          },
          "skipped": {
            "title": "Skipped",
            "type": "integer"
          },
          "updated": {
            "title": "Updated",
            "type": "integer"
          }
        },
        "required": [
          "inserted",
          "updated",
          "skipped",
          "invalid"
        ],
        "title": "ImportResult",
        "type": "object"
      },
      "IncomeCompanyRow": {
        "properties": {
          "company": {
            "title": "Company",
            "type": "string"
          },
          "months": {
            "items": {
              "type": "number"
            },
            "title": "Months",
            "type": "array"
          },
          "total": {
            "title": "Total",
            "type": "number"
          }
        },
        "required": [
          "company",
          "months",
          "total"
        ],
        "title": "IncomeCompanyRow",
        "type": "object"
      },
      "IncomeSectionResponse": {
        "properties": {
          "annual_total": {
            "title": "Annual Total",
            "type": "number"
          },
          "companies": {
            "items": {
              "$ref": "#/components/schemas/IncomeCompanyRow"
            },
            "title": "Companies",
            "type": "array"
          },
          "monthly_totals": {
            "items": {
              "type": "number"
            },
            "title": "Monthly Totals",
            "type": "array"
          }
        },
        "required": [
          "companies",
          "monthly_totals",
          "annual_total"
        ],
        "title": "IncomeSectionResponse",
        "type": "object"
      },
      "IncomeStatementResponse": {
        "properties": {
          "committed_floor": {
            "title": "Committed Floor",
            "type": "number"
          },
          "expense_sections": {
            "items": {
              "$ref": "#/components/schemas/ExpenseSectionResponse"
            },
            "title": "Expense Sections",
            "type": "array"
          },
          "income": {
            "$ref": "#/components/schemas/IncomeSectionResponse"
          },
          "months": {
            "items": {
              "type": "string"
            },
            "title": "Months",
            "type": "array"
          },
          "net_annual": {
            "title": "Net Annual",
            "type": "number"
          },
          "net_monthly": {
            "items": {
              "type": "number"
            },
            "title": "Net Monthly",
            "type": "array"
          },
          "projection": {
            "$ref": "#/components/schemas/ProjectionResponse"
          },
          "savings_rate_annual": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Savings Rate Annual"
          },
          "savings_rate_monthly": {
            "items": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ]
            },
            "title": "Savings Rate Monthly",
            "type": "array"
          },
          "total_expenses_annual": {
            "title": "Total Expenses Annual",
            "type": "number"
          },
          "total_expenses_monthly": {
            "items": {
              "type": "number"
            },
            "title": "Total Expenses Monthly",
            "type": "array"
          },
          "year": {
            "title": "Year",
            "type": "integer"
          }
        },
        "required": [
          "year",
          "months",
          "income",
          "expense_sections",
          "total_expenses_monthly",
          "total_expenses_annual",
          "net_monthly",
          "net_annual",
          "savings_rate_monthly",
          "savings_rate_annual",
          "projection",
          "committed_floor"
        ],
        "title": "IncomeStatementResponse",
        "type": "object"
      },
      "InsightsContextResponse": {
        "description": "Raw aggregation context handed to the AI analysis.\n\nThe nested dicts (current_month, trend entries, historical_averages, etc.)\ncome directly from the spending-summary and budget services; they are not\ntightly typed here because their shape is owned by those services.",
        "properties": {
          "anomalies": {
            "items": {
              "$ref": "#/components/schemas/CategoryAnomaly"
            },
            "title": "Anomalies",
            "type": "array"
          },
          "budget": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Budget"
          },
          "category_deltas": {
            "items": {
              "$ref": "#/components/schemas/CategoryDelta"
            },
            "title": "Category Deltas",
            "type": "array"
          },
          "commented_transactions": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Commented Transactions",
            "type": "array"
          },
          "current_month": {
            "additionalProperties": true,
            "title": "Current Month",
            "type": "object"
          },
          "delta": {
            "additionalProperties": {
              "type": "number"
            },
            "title": "Delta",
            "type": "object"
          },
          "fixed_charges": {
            "items": {
              "$ref": "#/components/schemas/FixedCharge"
            },
            "title": "Fixed Charges",
            "type": "array"
          },
          "generated_at": {
            "title": "Generated At",
            "type": "string"
          },
          "historical_averages": {
            "additionalProperties": true,
            "title": "Historical Averages",
            "type": "object"
          },
          "largest_transactions": {
            "items": {
              "$ref": "#/components/schemas/LargestTransaction"
            },
            "title": "Largest Transactions",
            "type": "array"
          },
          "month": {
            "title": "Month",
            "type": "string"
          },
          "pace": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PaceBlock"
              },
              {
                "type": "null"
              }
            ]
          },
          "previous_briefing": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PreviousBriefing"
              },
              {
                "type": "null"
              }
            ]
          },
          "previous_month": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Previous Month"
          },
          "recurring_annual": {
            "items": {
              "$ref": "#/components/schemas/RecurringAnnualCategory"
            },
            "title": "Recurring Annual",
            "type": "array"
          },
          "same_month_last_year": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SameMonthLastYear"
              },
              {
                "type": "null"
              }
            ]
          },
          "suspected_ignored": {
            "items": {
              "$ref": "#/components/schemas/SuspectedIgnored"
            },
            "title": "Suspected Ignored",
            "type": "array"
          },
          "trend": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Trend",
            "type": "array"
          },
          "user_memo": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "User Memo"
          }
        },
        "required": [
          "generated_at",
          "month",
          "current_month",
          "delta",
          "trend",
          "historical_averages",
          "category_deltas",
          "anomalies",
          "commented_transactions"
        ],
        "title": "InsightsContextResponse",
        "type": "object"
      },
      "InsightsGenerateResponse": {
        "description": "Response when kicking off a background insights generation job.",
        "properties": {
          "month": {
            "title": "Month",
            "type": "string"
          },
          "status": {
            "description": "Always \"running\" on successful start.",
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status",
          "month"
        ],
        "title": "InsightsGenerateResponse",
        "type": "object"
      },
      "InsightsStatusResponse": {
        "description": "Current state of the single-slot insights generation task.\n\n`status` is one of: \"idle\", \"running\", \"error\". Other fields are populated\nbased on status \u2014 `month`/`started_at` when running, `error` when errored.",
        "properties": {
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "month": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Month"
          },
          "started_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Started At"
          },
          "status": {
            "enum": [
              "idle",
              "running",
              "error"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "title": "InsightsStatusResponse",
        "type": "object"
      },
      "JournalDay": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "date": {
            "title": "Date",
            "type": "string"
          },
          "day_total": {
            "title": "Day Total",
            "type": "number"
          },
          "mtd_total": {
            "title": "Mtd Total",
            "type": "number"
          },
          "transactions": {
            "items": {
              "$ref": "#/components/schemas/TransactionResponse"
            },
            "title": "Transactions",
            "type": "array"
          }
        },
        "required": [
          "date",
          "day_total",
          "count",
          "mtd_total",
          "transactions"
        ],
        "title": "JournalDay",
        "type": "object"
      },
      "JournalResponse": {
        "properties": {
          "budget_ceiling": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Budget Ceiling"
          },
          "days": {
            "items": {
              "$ref": "#/components/schemas/JournalDay"
            },
            "title": "Days",
            "type": "array"
          },
          "month": {
            "title": "Month",
            "type": "string"
          },
          "month_total": {
            "title": "Month Total",
            "type": "number"
          },
          "transaction_count": {
            "title": "Transaction Count",
            "type": "integer"
          }
        },
        "required": [
          "month",
          "days",
          "month_total",
          "transaction_count",
          "budget_ceiling"
        ],
        "title": "JournalResponse",
        "type": "object"
      },
      "LargestTransaction": {
        "description": "One of the month's largest live spending transactions.",
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "category": {
            "title": "Category",
            "type": "string"
          },
          "comment": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Comment"
          },
          "company": {
            "title": "Company",
            "type": "string"
          },
          "date": {
            "title": "Date",
            "type": "string"
          }
        },
        "required": [
          "date",
          "company",
          "amount",
          "category"
        ],
        "title": "LargestTransaction",
        "type": "object"
      },
      "LatestTimestampResponse": {
        "description": "Lightweight freshness probe \u2014 callers compare `latest` against their last seen value.\n\n`latest` is the raw DateFileName string (lex-sortable by time), or None when\nthe table has no matching rows.",
        "properties": {
          "latest": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Latest"
          },
          "month": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Month"
          }
        },
        "required": [
          "month",
          "latest"
        ],
        "title": "LatestTimestampResponse",
        "type": "object"
      },
      "LinkAttachmentRequest": {
        "description": "Link an attachment to a transaction, or unlink it with ``tx_id: null``.",
        "properties": {
          "tx_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Transaction id to link to; null unlinks.",
            "title": "Tx Id"
          }
        },
        "title": "LinkAttachmentRequest",
        "type": "object"
      },
      "ManualResolveRequest": {
        "description": "Hand-entered values for a quarantined email the parsers couldn't read.\n\nMirrors ``ManualTransactionRequest`` (``src/api/models/ingestion.py``) so the\nresolve endpoint can reuse the same manual-transaction dict-build. ``name``\nis intentionally omitted \u2014 the resolve flow defaults the cardholder to the\nconfigured user.",
        "properties": {
          "amount": {
            "description": "Transaction amount",
            "exclusiveMinimum": 0.0,
            "title": "Amount",
            "type": "number"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Category (auto-detected from overrides if omitted)",
            "title": "Category"
          },
          "company": {
            "description": "Merchant/company name",
            "minLength": 1,
            "title": "Company",
            "type": "string"
          },
          "date": {
            "description": "Transaction date in YYYY-MM-DD format",
            "title": "Date",
            "type": "string"
          },
          "institution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Financial institution; defaults to the failure's detected institution, else 'Manual'",
            "title": "Institution"
          },
          "transaction_type": {
            "default": "purchase",
            "description": "purchase, withdrawal, preauth, e-transfer, or deposit",
            "enum": [
              "purchase",
              "withdrawal",
              "preauth",
              "e-transfer",
              "deposit"
            ],
            "title": "Transaction Type",
            "type": "string"
          }
        },
        "required": [
          "date",
          "amount",
          "company"
        ],
        "title": "ManualResolveRequest",
        "type": "object"
      },
      "ManualResolveResponse": {
        "properties": {
          "date_file_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Date File Name"
          },
          "failure_id": {
            "title": "Failure Id",
            "type": "string"
          },
          "status": {
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "failure_id",
          "status"
        ],
        "title": "ManualResolveResponse",
        "type": "object"
      },
      "ManualTransactionRequest": {
        "properties": {
          "amount": {
            "description": "Transaction amount",
            "exclusiveMinimum": 0.0,
            "title": "Amount",
            "type": "number"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Category (auto-detected if omitted)",
            "title": "Category"
          },
          "company": {
            "description": "Merchant/company name",
            "minLength": 1,
            "title": "Company",
            "type": "string"
          },
          "date": {
            "description": "Transaction date in YYYY-MM-DD format",
            "title": "Date",
            "type": "string"
          },
          "institution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Financial institution",
            "title": "Institution"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cardholder name",
            "title": "Name"
          },
          "transaction_type": {
            "default": "purchase",
            "description": "purchase, withdrawal, preauth, e-transfer, or deposit",
            "enum": [
              "purchase",
              "withdrawal",
              "preauth",
              "e-transfer",
              "deposit"
            ],
            "title": "Transaction Type",
            "type": "string"
          }
        },
        "required": [
          "date",
          "amount",
          "company"
        ],
        "title": "ManualTransactionRequest",
        "type": "object"
      },
      "ManualTransactionResponse": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "status": {
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "forwarded_to",
          "date_file_name",
          "category",
          "status"
        ],
        "title": "ManualTransactionResponse",
        "type": "object"
      },
      "MatchedItem": {
        "properties": {
          "cleaned_description": {
            "title": "Cleaned Description",
            "type": "string"
          },
          "company_differs": {
            "title": "Company Differs",
            "type": "boolean"
          },
          "db_match": {
            "$ref": "#/components/schemas/StatementDbMatch"
          },
          "index": {
            "title": "Index",
            "type": "integer"
          },
          "raw_description": {
            "title": "Raw Description",
            "type": "string"
          },
          "row_id": {
            "title": "Row Id",
            "type": "string"
          },
          "statement_txn": {
            "$ref": "#/components/schemas/RawStatementTxn"
          },
          "suggested_category": {
            "title": "Suggested Category",
            "type": "string"
          }
        },
        "required": [
          "index",
          "row_id",
          "statement_txn",
          "db_match",
          "company_differs",
          "cleaned_description",
          "raw_description",
          "suggested_category"
        ],
        "title": "MatchedItem",
        "type": "object"
      },
      "MerchantAliasEntry": {
        "properties": {
          "canonical_name": {
            "title": "Canonical Name",
            "type": "string"
          },
          "raw_name": {
            "title": "Raw Name",
            "type": "string"
          }
        },
        "required": [
          "raw_name",
          "canonical_name"
        ],
        "title": "MerchantAliasEntry",
        "type": "object"
      },
      "MerchantAliasListResponse": {
        "properties": {
          "aliases": {
            "items": {
              "$ref": "#/components/schemas/MerchantAliasEntry"
            },
            "title": "Aliases",
            "type": "array"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "version": {
            "title": "Version",
            "type": "integer"
          }
        },
        "required": [
          "aliases",
          "count",
          "version"
        ],
        "title": "MerchantAliasListResponse",
        "type": "object"
      },
      "MerchantAliasMutationResponse": {
        "properties": {
          "ok": {
            "title": "Ok",
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "MerchantAliasMutationResponse",
        "type": "object"
      },
      "MerchantAliasPutRequest": {
        "properties": {
          "canonical_name": {
            "title": "Canonical Name",
            "type": "string"
          }
        },
        "required": [
          "canonical_name"
        ],
        "title": "MerchantAliasPutRequest",
        "type": "object"
      },
      "MerchantIntelligencePeriod": {
        "description": "Inclusive window of months consumed for the calculation.",
        "properties": {
          "from": {
            "title": "From",
            "type": "string"
          },
          "to": {
            "title": "To",
            "type": "string"
          }
        },
        "required": [
          "from",
          "to"
        ],
        "title": "MerchantIntelligencePeriod",
        "type": "object"
      },
      "MerchantIntelligenceResponse": {
        "description": "Full payload for ``GET /api/v1/merchants/intelligence``.",
        "properties": {
          "merchants": {
            "items": {
              "$ref": "#/components/schemas/MerchantRecord"
            },
            "title": "Merchants",
            "type": "array"
          },
          "month": {
            "title": "Month",
            "type": "string"
          },
          "months_analyzed": {
            "title": "Months Analyzed",
            "type": "integer"
          },
          "period": {
            "$ref": "#/components/schemas/MerchantIntelligencePeriod"
          },
          "summary": {
            "$ref": "#/components/schemas/MerchantIntelligenceSummary"
          }
        },
        "required": [
          "month",
          "months_analyzed",
          "period",
          "merchants",
          "summary"
        ],
        "title": "MerchantIntelligenceResponse",
        "type": "object"
      },
      "MerchantIntelligenceSummary": {
        "description": "Aggregate roll-up across all merchants.",
        "properties": {
          "churned_merchants": {
            "items": {
              "type": "string"
            },
            "title": "Churned Merchants",
            "type": "array"
          },
          "discretionary_this_month": {
            "title": "Discretionary This Month",
            "type": "number"
          },
          "new_merchants": {
            "items": {
              "type": "string"
            },
            "title": "New Merchants",
            "type": "array"
          },
          "price_changes": {
            "items": {
              "$ref": "#/components/schemas/MerchantPriceChangeRow"
            },
            "title": "Price Changes",
            "type": "array"
          },
          "recurring_burn_rate": {
            "description": "Sum of avg_amount across all merchants classified as fixed.",
            "title": "Recurring Burn Rate",
            "type": "number"
          },
          "recurring_count": {
            "title": "Recurring Count",
            "type": "integer"
          }
        },
        "required": [
          "recurring_burn_rate",
          "recurring_count",
          "discretionary_this_month",
          "new_merchants",
          "churned_merchants",
          "price_changes"
        ],
        "title": "MerchantIntelligenceSummary",
        "type": "object"
      },
      "MerchantPriceChange": {
        "description": "Latest detected price change for a recurring merchant.",
        "properties": {
          "new_amount": {
            "title": "New Amount",
            "type": "number"
          },
          "old_amount": {
            "title": "Old Amount",
            "type": "number"
          },
          "since_month": {
            "description": "YYYY-MM month the new price first appeared.",
            "title": "Since Month",
            "type": "string"
          }
        },
        "required": [
          "old_amount",
          "new_amount",
          "since_month"
        ],
        "title": "MerchantPriceChange",
        "type": "object"
      },
      "MerchantPriceChangeRow": {
        "description": "Flattened price-change entry for the summary section.",
        "properties": {
          "merchant": {
            "title": "Merchant",
            "type": "string"
          },
          "new_amount": {
            "title": "New Amount",
            "type": "number"
          },
          "old_amount": {
            "title": "Old Amount",
            "type": "number"
          },
          "since_month": {
            "title": "Since Month",
            "type": "string"
          }
        },
        "required": [
          "merchant",
          "old_amount",
          "new_amount",
          "since_month"
        ],
        "title": "MerchantPriceChangeRow",
        "type": "object"
      },
      "MerchantRecord": {
        "description": "Per-merchant intelligence over the analyzed window.",
        "properties": {
          "avg_amount": {
            "title": "Avg Amount",
            "type": "number"
          },
          "category": {
            "title": "Category",
            "type": "string"
          },
          "company": {
            "title": "Company",
            "type": "string"
          },
          "frequency_type": {
            "enum": [
              "fixed",
              "variable",
              "lumpy",
              "none"
            ],
            "title": "Frequency Type",
            "type": "string"
          },
          "is_churned": {
            "title": "Is Churned",
            "type": "boolean"
          },
          "is_new": {
            "title": "Is New",
            "type": "boolean"
          },
          "is_recurring": {
            "title": "Is Recurring",
            "type": "boolean"
          },
          "monthly_amounts": {
            "items": {
              "type": "number"
            },
            "title": "Monthly Amounts",
            "type": "array"
          },
          "monthly_counts": {
            "items": {
              "type": "integer"
            },
            "title": "Monthly Counts",
            "type": "array"
          },
          "months_active": {
            "title": "Months Active",
            "type": "integer"
          },
          "price_change": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MerchantPriceChange"
              },
              {
                "type": "null"
              }
            ]
          },
          "total": {
            "title": "Total",
            "type": "number"
          }
        },
        "required": [
          "company",
          "total",
          "monthly_amounts",
          "monthly_counts",
          "months_active",
          "avg_amount",
          "frequency_type",
          "category",
          "is_recurring",
          "is_new",
          "is_churned"
        ],
        "title": "MerchantRecord",
        "type": "object"
      },
      "MonthPaceBreakdown": {
        "description": "Commitment-aware decomposition of ``projected_month_total`` (L5).\n\n``projected_month_total`` = ``observed_mtd + assumed_committed +\nupcoming_committed + everyday_remainder``. ``observed_mtd`` is the ledger\ntruth (arrived recurring + imported statement rows included); the committed\nterms are point estimates over the expected charges; ``everyday_remainder``\nis the discretionary-only curve projection for the rest of the month.",
        "properties": {
          "assumed_committed": {
            "title": "Assumed Committed",
            "type": "number"
          },
          "charges": {
            "items": {
              "$ref": "#/components/schemas/ExpectedChargeInfo"
            },
            "title": "Charges",
            "type": "array"
          },
          "days_remaining": {
            "title": "Days Remaining",
            "type": "integer"
          },
          "everyday_daily_rate": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Everyday Daily Rate"
          },
          "everyday_remainder": {
            "title": "Everyday Remainder",
            "type": "number"
          },
          "observed_mtd": {
            "title": "Observed Mtd",
            "type": "number"
          },
          "upcoming_committed": {
            "title": "Upcoming Committed",
            "type": "number"
          }
        },
        "required": [
          "observed_mtd",
          "assumed_committed",
          "upcoming_committed",
          "everyday_remainder",
          "days_remaining",
          "charges"
        ],
        "title": "MonthPaceBreakdown",
        "type": "object"
      },
      "MonthPaceInfo": {
        "description": "Mid-month pace context \u2014 non-null only when the requested month is the\ncurrent month (see the summary router). Day *position* for every fraction\nlookup is ``day_of_month / days_in_month``.\n\n``breakdown`` is the commitment-aware decomposition (L6): non-null only when\nthe upcoming-charge derivation succeeds AND penciled charges exist; it\nfails open to ``None`` (curve-only pace) exactly like ``pace`` itself.",
        "properties": {
          "breakdown": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MonthPaceBreakdown"
              },
              {
                "type": "null"
              }
            ]
          },
          "day_of_month": {
            "title": "Day Of Month",
            "type": "integer"
          },
          "days_in_month": {
            "title": "Days In Month",
            "type": "integer"
          },
          "forecast_quality": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Forecast Quality"
          },
          "previous_to_date": {
            "title": "Previous To Date",
            "type": "number"
          },
          "projected_lower": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Projected Lower"
          },
          "projected_month_total": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Projected Month Total"
          },
          "projected_upper": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Projected Upper"
          },
          "typical_to_date": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Typical To Date"
          }
        },
        "required": [
          "day_of_month",
          "days_in_month",
          "previous_to_date",
          "typical_to_date",
          "projected_month_total",
          "projected_lower",
          "projected_upper",
          "forecast_quality"
        ],
        "title": "MonthPaceInfo",
        "type": "object"
      },
      "MonthProgress": {
        "description": "Elapsed-days projection, present only when the target month is current.",
        "properties": {
          "days_elapsed": {
            "title": "Days Elapsed",
            "type": "integer"
          },
          "days_in_month": {
            "title": "Days In Month",
            "type": "integer"
          },
          "projected_month_end": {
            "title": "Projected Month End",
            "type": "number"
          }
        },
        "required": [
          "days_elapsed",
          "days_in_month",
          "projected_month_end"
        ],
        "title": "MonthProgress",
        "type": "object"
      },
      "MonthSummary": {
        "examples": [
          {
            "by_category": {
              "groceries": {
                "amount": 612.45,
                "count": 18
              },
              "restaurants": {
                "amount": 248.9,
                "count": 11
              }
            },
            "by_company": {
              "WHOLE FOODS": {
                "amount": 412.3,
                "category": "groceries",
                "count": 8
              }
            },
            "deposit_count": 2,
            "deposit_total": 5200.0,
            "deposits_by_company": {
              "EMPLOYER PAYROLL": {
                "amount": 5200.0,
                "count": 2
              }
            },
            "spending_count": 84,
            "top_categories": [
              {
                "amount": 612.45,
                "count": 18,
                "name": "groceries"
              },
              {
                "amount": 248.9,
                "count": 11,
                "name": "restaurants"
              }
            ],
            "total_spending": 3142.87,
            "year_month": "2026-04"
          }
        ],
        "properties": {
          "by_category": {
            "additionalProperties": {
              "$ref": "#/components/schemas/CategorySummary"
            },
            "title": "By Category",
            "type": "object"
          },
          "by_company": {
            "additionalProperties": {
              "$ref": "#/components/schemas/CompanySummary"
            },
            "title": "By Company",
            "type": "object"
          },
          "deposit_count": {
            "title": "Deposit Count",
            "type": "integer"
          },
          "deposit_total": {
            "title": "Deposit Total",
            "type": "number"
          },
          "deposits_by_company": {
            "additionalProperties": {
              "$ref": "#/components/schemas/DepositSourceSummary"
            },
            "title": "Deposits By Company",
            "type": "object"
          },
          "spending_count": {
            "title": "Spending Count",
            "type": "integer"
          },
          "top_categories": {
            "items": {
              "$ref": "#/components/schemas/TopCategory"
            },
            "title": "Top Categories",
            "type": "array"
          },
          "total_spending": {
            "title": "Total Spending",
            "type": "number"
          },
          "year_month": {
            "title": "Year Month",
            "type": "string"
          }
        },
        "required": [
          "year_month",
          "total_spending",
          "spending_count",
          "deposit_total",
          "deposit_count",
          "by_category",
          "by_company",
          "deposits_by_company",
          "top_categories"
        ],
        "title": "MonthSummary",
        "type": "object"
      },
      "NewItem": {
        "properties": {
          "cleaned_description": {
            "title": "Cleaned Description",
            "type": "string"
          },
          "index": {
            "title": "Index",
            "type": "integer"
          },
          "raw_description": {
            "title": "Raw Description",
            "type": "string"
          },
          "row_id": {
            "title": "Row Id",
            "type": "string"
          },
          "statement_txn": {
            "$ref": "#/components/schemas/RawStatementTxn"
          },
          "suggested_category": {
            "title": "Suggested Category",
            "type": "string"
          }
        },
        "required": [
          "index",
          "row_id",
          "statement_txn",
          "cleaned_description",
          "raw_description",
          "suggested_category"
        ],
        "title": "NewItem",
        "type": "object"
      },
      "OverrideConsolidateRequest": {
        "description": "Collapse the listed member companies into a single consolidated override.\n\nAtomic: either the consolidated override is created and every member\ndeleted, or nothing changes. Members are matched case-insensitively\nagainst the current overrides map.",
        "properties": {
          "canonical_company": {
            "title": "Canonical Company",
            "type": "string"
          },
          "category": {
            "title": "Category",
            "type": "string"
          },
          "members": {
            "items": {
              "type": "string"
            },
            "title": "Members",
            "type": "array"
          },
          "normalized_key": {
            "title": "Normalized Key",
            "type": "string"
          }
        },
        "required": [
          "normalized_key",
          "canonical_company",
          "category",
          "members"
        ],
        "title": "OverrideConsolidateRequest",
        "type": "object"
      },
      "OverrideConsolidateResponse": {
        "properties": {
          "canonical": {
            "title": "Canonical",
            "type": "string"
          },
          "detail": {
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "detail",
          "canonical"
        ],
        "title": "OverrideConsolidateResponse",
        "type": "object"
      },
      "OverrideDeleteResponse": {
        "properties": {
          "ok": {
            "default": true,
            "title": "Ok",
            "type": "boolean"
          }
        },
        "title": "OverrideDeleteResponse",
        "type": "object"
      },
      "OverrideDuplicateGroup": {
        "description": "Phase 4: overrides that share a normalized key.\n\n`unanimous_category` is non-null when every member agrees on category\n(case-insensitive) \u2014 this group can be consolidated with one click.\nNull means the group is blacklisted from Tier 1 matching and needs\nuser review before consolidation.",
        "properties": {
          "members": {
            "items": {
              "$ref": "#/components/schemas/OverrideDuplicateMember"
            },
            "title": "Members",
            "type": "array"
          },
          "normalized_key": {
            "title": "Normalized Key",
            "type": "string"
          },
          "unanimous_category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Unanimous Category"
          }
        },
        "required": [
          "normalized_key",
          "members",
          "unanimous_category"
        ],
        "title": "OverrideDuplicateGroup",
        "type": "object"
      },
      "OverrideDuplicateMember": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "company": {
            "title": "Company",
            "type": "string"
          }
        },
        "required": [
          "company",
          "category"
        ],
        "title": "OverrideDuplicateMember",
        "type": "object"
      },
      "OverrideDuplicatesResponse": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "groups": {
            "items": {
              "$ref": "#/components/schemas/OverrideDuplicateGroup"
            },
            "title": "Groups",
            "type": "array"
          }
        },
        "required": [
          "groups",
          "count"
        ],
        "title": "OverrideDuplicatesResponse",
        "type": "object"
      },
      "OverrideEntry": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "company": {
            "title": "Company",
            "type": "string"
          }
        },
        "required": [
          "company",
          "category"
        ],
        "title": "OverrideEntry",
        "type": "object"
      },
      "OverrideListResponse": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "overrides": {
            "items": {
              "$ref": "#/components/schemas/OverrideEntry"
            },
            "title": "Overrides",
            "type": "array"
          },
          "version": {
            "title": "Version",
            "type": "integer"
          }
        },
        "required": [
          "overrides",
          "count",
          "version"
        ],
        "title": "OverrideListResponse",
        "type": "object"
      },
      "OverrideMatchCandidate": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "confidence": {
            "title": "Confidence",
            "type": "number"
          },
          "matched_rule": {
            "title": "Matched Rule",
            "type": "string"
          },
          "tier": {
            "enum": [
              "exact",
              "normalized",
              "alias",
              "fuzzy"
            ],
            "title": "Tier",
            "type": "string"
          }
        },
        "required": [
          "category",
          "matched_rule",
          "confidence",
          "tier"
        ],
        "title": "OverrideMatchCandidate",
        "type": "object"
      },
      "OverrideMatchResponse": {
        "description": "Phase 2: preview endpoint response for the add-rule hint widget.\n\n`tier`/`category`/`matched_rule`/`confidence` mirror the top candidate;\n`candidates` is the full sorted list (\u2265 0.70 disclosure threshold,\ncapped at 5). When no tier matches, every field is null and\n`candidates` is `[]`.",
        "properties": {
          "candidates": {
            "items": {
              "$ref": "#/components/schemas/OverrideMatchCandidate"
            },
            "title": "Candidates",
            "type": "array"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "confidence": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Confidence"
          },
          "matched_rule": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Matched Rule"
          },
          "tier": {
            "anyOf": [
              {
                "enum": [
                  "exact",
                  "normalized",
                  "alias",
                  "fuzzy"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tier"
          }
        },
        "required": [
          "category",
          "matched_rule",
          "confidence",
          "tier",
          "candidates"
        ],
        "title": "OverrideMatchResponse",
        "type": "object"
      },
      "OverridePutRequest": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          }
        },
        "required": [
          "category"
        ],
        "title": "OverridePutRequest",
        "type": "object"
      },
      "OverrideSuggestion": {
        "properties": {
          "company": {
            "title": "Company",
            "type": "string"
          },
          "correction_count": {
            "title": "Correction Count",
            "type": "integer"
          },
          "last_corrected": {
            "title": "Last Corrected",
            "type": "string"
          },
          "suggested_category": {
            "title": "Suggested Category",
            "type": "string"
          }
        },
        "required": [
          "company",
          "suggested_category",
          "correction_count",
          "last_corrected"
        ],
        "title": "OverrideSuggestion",
        "type": "object"
      },
      "OverrideSuggestionsResponse": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "suggestions": {
            "items": {
              "$ref": "#/components/schemas/OverrideSuggestion"
            },
            "title": "Suggestions",
            "type": "array"
          }
        },
        "required": [
          "suggestions",
          "count"
        ],
        "title": "OverrideSuggestionsResponse",
        "type": "object"
      },
      "PaceBlock": {
        "description": "Authoritative, precomputed budget-pace math (null when no targets exist).\n\nThe LLM must read every projection/variance from here rather than deriving\nits own \u2014 naive annualization distorts lumpy annual categories.",
        "properties": {
          "categories": {
            "items": {
              "$ref": "#/components/schemas/PaceCategory"
            },
            "title": "Categories",
            "type": "array"
          },
          "ceiling": {
            "$ref": "#/components/schemas/PaceCeiling"
          },
          "month_progress": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MonthProgress"
              },
              {
                "type": "null"
              }
            ]
          },
          "months_elapsed": {
            "title": "Months Elapsed",
            "type": "integer"
          },
          "unbudgeted": {
            "items": {
              "$ref": "#/components/schemas/PaceUnbudgeted"
            },
            "title": "Unbudgeted",
            "type": "array"
          }
        },
        "required": [
          "months_elapsed",
          "ceiling",
          "categories",
          "unbudgeted"
        ],
        "title": "PaceBlock",
        "type": "object"
      },
      "PaceCategory": {
        "description": "Precomputed pace math for one budgeted category.\n\nVariable categories carry `expected_to_date`/`variance_*`; lumpy (annual)\ncategories carry `pct_of_annual`/`remaining_expected` instead \u2014 the unused\nfields stay null.",
        "properties": {
          "annual_target": {
            "title": "Annual Target",
            "type": "number"
          },
          "assessment": {
            "title": "Assessment",
            "type": "string"
          },
          "category": {
            "title": "Category",
            "type": "string"
          },
          "category_type": {
            "title": "Category Type",
            "type": "string"
          },
          "expected_to_date": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expected To Date"
          },
          "month_actual": {
            "title": "Month Actual",
            "type": "number"
          },
          "monthly_target": {
            "title": "Monthly Target",
            "type": "number"
          },
          "pct_of_annual": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pct Of Annual"
          },
          "remaining_expected": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Remaining Expected"
          },
          "variance_amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Variance Amount"
          },
          "variance_pct": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Variance Pct"
          },
          "ytd_actual": {
            "title": "Ytd Actual",
            "type": "number"
          }
        },
        "required": [
          "category",
          "category_type",
          "annual_target",
          "monthly_target",
          "month_actual",
          "ytd_actual",
          "assessment"
        ],
        "title": "PaceCategory",
        "type": "object"
      },
      "PaceCeiling": {
        "description": "Precomputed pace math for the annual spending ceiling.",
        "properties": {
          "annual": {
            "title": "Annual",
            "type": "number"
          },
          "method_note": {
            "title": "Method Note",
            "type": "string"
          },
          "projected_adjusted": {
            "title": "Projected Adjusted",
            "type": "number"
          },
          "projected_naive": {
            "title": "Projected Naive",
            "type": "number"
          },
          "prorated_to_date": {
            "title": "Prorated To Date",
            "type": "number"
          },
          "variance_amount": {
            "title": "Variance Amount",
            "type": "number"
          },
          "variance_pct": {
            "title": "Variance Pct",
            "type": "number"
          },
          "ytd_spent": {
            "title": "Ytd Spent",
            "type": "number"
          }
        },
        "required": [
          "annual",
          "ytd_spent",
          "prorated_to_date",
          "variance_amount",
          "variance_pct",
          "projected_naive",
          "projected_adjusted",
          "method_note"
        ],
        "title": "PaceCeiling",
        "type": "object"
      },
      "PaceStatus": {
        "properties": {
          "expected_pace": {
            "title": "Expected Pace",
            "type": "number"
          },
          "headline": {
            "title": "Headline",
            "type": "string"
          },
          "projected_month_status": {
            "anyOf": [
              {
                "enum": [
                  "under",
                  "on_track",
                  "over"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Projected total vs the same categories' combined monthly budget.",
            "title": "Projected Month Status"
          },
          "projected_month_total": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Projected current-month spend across budgeted non-lumpy categories. None when forecasting is unavailable (non-current year, no history, or error).",
            "title": "Projected Month Total"
          },
          "spending_ceiling": {
            "title": "Spending Ceiling",
            "type": "number"
          },
          "status": {
            "enum": [
              "under",
              "on_track",
              "over"
            ],
            "title": "Status",
            "type": "string"
          },
          "variance": {
            "title": "Variance",
            "type": "number"
          },
          "ytd_spent": {
            "title": "Ytd Spent",
            "type": "number"
          }
        },
        "required": [
          "spending_ceiling",
          "ytd_spent",
          "expected_pace",
          "variance",
          "status",
          "headline"
        ],
        "title": "PaceStatus",
        "type": "object"
      },
      "PaceUnbudgeted": {
        "description": "A spend category with no budget target, for pace completeness.",
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "month_actual": {
            "title": "Month Actual",
            "type": "number"
          },
          "ytd_actual": {
            "title": "Ytd Actual",
            "type": "number"
          }
        },
        "required": [
          "category",
          "ytd_actual",
          "month_actual"
        ],
        "title": "PaceUnbudgeted",
        "type": "object"
      },
      "ParseFailureDetail": {
        "description": "Full view of a quarantined parse failure, including the email body.",
        "properties": {
          "alert_classifier_result": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Alert Classifier Result"
          },
          "body": {
            "title": "Body",
            "type": "string"
          },
          "detected_institution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Detected Institution"
          },
          "failure_stage": {
            "title": "Failure Stage",
            "type": "string"
          },
          "from_email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "From Email"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "received_at": {
            "title": "Received At",
            "type": "string"
          },
          "recovered_date_file_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Recovered Date File Name"
          },
          "status": {
            "title": "Status",
            "type": "string"
          },
          "subject": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Subject"
          }
        },
        "required": [
          "id",
          "received_at",
          "from_email",
          "subject",
          "detected_institution",
          "failure_stage",
          "status",
          "recovered_date_file_name",
          "alert_classifier_result",
          "body"
        ],
        "title": "ParseFailureDetail",
        "type": "object"
      },
      "ParseFailureListResponse": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "failures": {
            "items": {
              "$ref": "#/components/schemas/ParseFailureSummary"
            },
            "title": "Failures",
            "type": "array"
          }
        },
        "required": [
          "count",
          "failures"
        ],
        "title": "ParseFailureListResponse",
        "type": "object"
      },
      "ParseFailureSummary": {
        "description": "Triage view of a quarantined parse failure \u2014 no email body.",
        "properties": {
          "alert_classifier_result": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Alert Classifier Result"
          },
          "detected_institution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Detected Institution"
          },
          "failure_stage": {
            "title": "Failure Stage",
            "type": "string"
          },
          "from_email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "From Email"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "received_at": {
            "title": "Received At",
            "type": "string"
          },
          "recovered_date_file_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Recovered Date File Name"
          },
          "status": {
            "title": "Status",
            "type": "string"
          },
          "subject": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Subject"
          }
        },
        "required": [
          "id",
          "received_at",
          "from_email",
          "subject",
          "detected_institution",
          "failure_stage",
          "status",
          "recovered_date_file_name",
          "alert_classifier_result"
        ],
        "title": "ParseFailureSummary",
        "type": "object"
      },
      "PermanentDeleteResponse": {
        "properties": {
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name"
        ],
        "title": "PermanentDeleteResponse",
        "type": "object"
      },
      "PreviousBriefing": {
        "description": "The most recent saved briefing for the previous month, for continuity.",
        "properties": {
          "excerpt": {
            "title": "Excerpt",
            "type": "string"
          },
          "generated_at": {
            "title": "Generated At",
            "type": "string"
          },
          "month": {
            "title": "Month",
            "type": "string"
          }
        },
        "required": [
          "month",
          "generated_at",
          "excerpt"
        ],
        "title": "PreviousBriefing",
        "type": "object"
      },
      "PreviouslyImportedItem": {
        "properties": {
          "cleaned_description": {
            "title": "Cleaned Description",
            "type": "string"
          },
          "db_match": {
            "$ref": "#/components/schemas/StatementDbMatch"
          },
          "index": {
            "title": "Index",
            "type": "integer"
          },
          "raw_description": {
            "title": "Raw Description",
            "type": "string"
          },
          "row_id": {
            "title": "Row Id",
            "type": "string"
          },
          "statement_txn": {
            "$ref": "#/components/schemas/RawStatementTxn"
          },
          "suggested_category": {
            "title": "Suggested Category",
            "type": "string"
          }
        },
        "required": [
          "index",
          "row_id",
          "statement_txn",
          "db_match",
          "cleaned_description",
          "raw_description",
          "suggested_category"
        ],
        "title": "PreviouslyImportedItem",
        "type": "object"
      },
      "ProjectionResponse": {
        "properties": {
          "annualized_expenses": {
            "title": "Annualized Expenses",
            "type": "number"
          },
          "annualized_income": {
            "title": "Annualized Income",
            "type": "number"
          },
          "annualized_net": {
            "title": "Annualized Net",
            "type": "number"
          },
          "months_elapsed": {
            "title": "Months Elapsed",
            "type": "integer"
          }
        },
        "required": [
          "annualized_income",
          "annualized_expenses",
          "annualized_net",
          "months_elapsed"
        ],
        "title": "ProjectionResponse",
        "type": "object"
      },
      "RawStatementTxn": {
        "description": "Raw transaction emitted by a statement parser (PDF/CSV).\n\nUsed inside reconciliation items (MatchedItem.statement_txn etc.) where the\ncleaned (display) description is carried on the parent item, not the txn\nitself.",
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "balance": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Balance"
          },
          "date": {
            "title": "Date",
            "type": "string"
          },
          "description": {
            "title": "Description",
            "type": "string"
          },
          "type": {
            "enum": [
              "withdrawal",
              "deposit"
            ],
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "date",
          "description",
          "amount",
          "type",
          "balance"
        ],
        "title": "RawStatementTxn",
        "type": "object"
      },
      "ReceiptCandidate": {
        "description": "One transaction a parsed receipt might explain, ranked by the matcher.",
        "properties": {
          "already_has_receipt": {
            "title": "Already Has Receipt",
            "type": "boolean"
          },
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "amount_distance": {
            "title": "Amount Distance",
            "type": "number"
          },
          "category": {
            "title": "Category",
            "type": "string"
          },
          "company": {
            "title": "Company",
            "type": "string"
          },
          "date": {
            "title": "Date",
            "type": "string"
          },
          "day_distance": {
            "title": "Day Distance",
            "type": "integer"
          },
          "tier": {
            "description": "1 = exact match, 2 = amount within window, 3 = tip window.",
            "title": "Tier",
            "type": "integer"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "tier",
          "day_distance",
          "amount_distance",
          "company",
          "amount",
          "date",
          "category",
          "already_has_receipt"
        ],
        "title": "ReceiptCandidate",
        "type": "object"
      },
      "ReceiptCandidatesResponse": {
        "description": "Ranked match candidates for a parsed receipt.\n\n``auto_link_candidate`` is true only when exactly one tier-1 candidate exists\nand the attachment arrived unlinked \u2014 the signal that the client should link\nthe receipt to the first candidate. This GET performs no write; the client\nfires ``POST /attachments/{id}/link`` when the flag is set (the ``read``\nscope's GET-only contract, and the demo dataset, both depend on that).",
        "properties": {
          "attachment_id": {
            "title": "Attachment Id",
            "type": "string"
          },
          "auto_link_candidate": {
            "title": "Auto Link Candidate",
            "type": "boolean"
          },
          "candidates": {
            "items": {
              "$ref": "#/components/schemas/ReceiptCandidate"
            },
            "title": "Candidates",
            "type": "array"
          }
        },
        "required": [
          "attachment_id",
          "auto_link_candidate",
          "candidates"
        ],
        "title": "ReceiptCandidatesResponse",
        "type": "object"
      },
      "ReconcileSummary": {
        "properties": {
          "ambiguous_count": {
            "title": "Ambiguous Count",
            "type": "integer"
          },
          "duplicate_count": {
            "default": 0,
            "title": "Duplicate Count",
            "type": "integer"
          },
          "enriched_count": {
            "default": 0,
            "title": "Enriched Count",
            "type": "integer"
          },
          "imported_count": {
            "default": 0,
            "title": "Imported Count",
            "type": "integer"
          },
          "matched_count": {
            "title": "Matched Count",
            "type": "integer"
          },
          "new_count": {
            "title": "New Count",
            "type": "integer"
          },
          "previously_imported_count": {
            "default": 0,
            "title": "Previously Imported Count",
            "type": "integer"
          },
          "skipped_count": {
            "default": 0,
            "title": "Skipped Count",
            "type": "integer"
          },
          "suspected_duplicate_count": {
            "default": 0,
            "title": "Suspected Duplicate Count",
            "type": "integer"
          },
          "total_parsed": {
            "title": "Total Parsed",
            "type": "integer"
          },
          "updated_count": {
            "default": 0,
            "title": "Updated Count",
            "type": "integer"
          }
        },
        "required": [
          "total_parsed",
          "matched_count",
          "ambiguous_count",
          "new_count"
        ],
        "title": "ReconcileSummary",
        "type": "object"
      },
      "RecurringAnnualCategory": {
        "description": "A category that historically bills around this time of year (once or twice annually).",
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "last_seen": {
            "title": "Last Seen",
            "type": "string"
          },
          "months_seen": {
            "items": {
              "type": "string"
            },
            "title": "Months Seen",
            "type": "array"
          },
          "typical_amount": {
            "title": "Typical Amount",
            "type": "number"
          }
        },
        "required": [
          "category",
          "typical_amount",
          "months_seen",
          "last_seen"
        ],
        "title": "RecurringAnnualCategory",
        "type": "object"
      },
      "RetryAllRequest": {
        "description": "Filter for a bulk retry \u2014 at least one field is required (422 otherwise).",
        "properties": {
          "from_domain": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Retry quarantined rows whose sender email domain matches (suffix) this.",
            "title": "From Domain"
          },
          "institution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Retry quarantined rows whose detected institution equals this.",
            "title": "Institution"
          }
        },
        "title": "RetryAllRequest",
        "type": "object"
      },
      "RetryAllResponse": {
        "properties": {
          "created": {
            "title": "Created",
            "type": "integer"
          },
          "duplicates": {
            "title": "Duplicates",
            "type": "integer"
          },
          "retried": {
            "title": "Retried",
            "type": "integer"
          },
          "still_failing": {
            "title": "Still Failing",
            "type": "integer"
          }
        },
        "required": [
          "retried",
          "created",
          "duplicates",
          "still_failing"
        ],
        "title": "RetryAllResponse",
        "type": "object"
      },
      "RetryResponse": {
        "properties": {
          "date_file_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Date File Name"
          },
          "failure_id": {
            "title": "Failure Id",
            "type": "string"
          },
          "status": {
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "failure_id",
          "status"
        ],
        "title": "RetryResponse",
        "type": "object"
      },
      "RevertResponse": {
        "description": "The outcome of ``POST /api/v1/activity/{id}/revert`` (L8).\n\n``reverted_entry_id`` is the id of the original entry that was undone (now\nstamped ``reverted_at`` / ``reverted_by`` off the response path); ``summary``\nis a short human-readable description of what the revert restored. The revert\nis itself journaled as a new entry (kept for transparency, not itself\nreversible \u2014 redo is out of scope); that new entry's id is assigned by the\nbackground write and is not returned here.",
        "properties": {
          "reverted_entry_id": {
            "title": "Reverted Entry Id",
            "type": "string"
          },
          "summary": {
            "title": "Summary",
            "type": "string"
          }
        },
        "required": [
          "reverted_entry_id",
          "summary"
        ],
        "title": "RevertResponse",
        "type": "object"
      },
      "ReviewResponse": {
        "examples": [
          {
            "date_file_name": "2026.04.15_09.32_email_01.eml",
            "forwarded_to": "default@local",
            "source": "manual",
            "tx_id": "ZGVmYXVsdEBsb2NhbHwyMDI2LjA0LjE1XzA5LjMyX2VtYWlsXzAxLmVtbA"
          }
        ],
        "properties": {
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "source": {
            "title": "Source",
            "type": "string"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name",
          "source"
        ],
        "title": "ReviewResponse",
        "type": "object"
      },
      "S3BackupStatusResponse": {
        "description": "Current S3 attachment-backup config plus the last-run state metadata.",
        "properties": {
          "bucket": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Bucket"
          },
          "consecutive_failures": {
            "title": "Consecutive Failures",
            "type": "integer"
          },
          "deleted_count": {
            "title": "Deleted Count",
            "type": "integer"
          },
          "enabled": {
            "title": "Enabled",
            "type": "boolean"
          },
          "last_attempt_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Attempt At"
          },
          "last_error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Error"
          },
          "last_success_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Success At"
          },
          "objects_total": {
            "title": "Objects Total",
            "type": "integer"
          },
          "prefix": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Prefix"
          },
          "uploaded_count": {
            "title": "Uploaded Count",
            "type": "integer"
          }
        },
        "required": [
          "enabled",
          "bucket",
          "prefix",
          "last_attempt_at",
          "last_success_at",
          "last_error",
          "consecutive_failures",
          "uploaded_count",
          "deleted_count",
          "objects_total"
        ],
        "title": "S3BackupStatusResponse",
        "type": "object"
      },
      "SameMonthLastYear": {
        "description": "The target month one calendar year earlier \u2014 a seasonal comparison anchor.\n\n`by_category` mirrors the trimmed trend-entry shape. `comments` are LAST\nYEAR's user annotations on that month's live spending, carried forward so the\nmodel can cite prior-year context (\"as it did last May\") without the user\nre-annotating.",
        "properties": {
          "by_category": {
            "additionalProperties": true,
            "title": "By Category",
            "type": "object"
          },
          "comments": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Comments",
            "type": "array"
          },
          "spending_count": {
            "title": "Spending Count",
            "type": "integer"
          },
          "total_spending": {
            "title": "Total Spending",
            "type": "number"
          },
          "year_month": {
            "title": "Year Month",
            "type": "string"
          }
        },
        "required": [
          "year_month",
          "total_spending",
          "spending_count",
          "by_category",
          "comments"
        ],
        "title": "SameMonthLastYear",
        "type": "object"
      },
      "SavedInsightDetail": {
        "description": "Full saved insight including the markdown briefing.\n\n`validation` is populated from the sidecar JSON when present, and null\notherwise.",
        "properties": {
          "content": {
            "title": "Content",
            "type": "string"
          },
          "generated_at": {
            "title": "Generated At",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "month": {
            "title": "Month",
            "type": "string"
          },
          "validation": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/BriefingValidation"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "month",
          "generated_at",
          "content"
        ],
        "title": "SavedInsightDetail",
        "type": "object"
      },
      "SavedInsightItem": {
        "description": "Summary entry in the saved insights list.\n\n`figures_ok` is null when no validation sidecar exists (older briefings, or\nbriefings saved before the figure check shipped), true/false otherwise.",
        "properties": {
          "figures_ok": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Figures Ok"
          },
          "generated_at": {
            "title": "Generated At",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "month": {
            "title": "Month",
            "type": "string"
          }
        },
        "required": [
          "id",
          "month",
          "generated_at"
        ],
        "title": "SavedInsightItem",
        "type": "object"
      },
      "SavedInsightListResponse": {
        "description": "Envelope for the saved-insights list \u2014 items plus a count.",
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "items": {
            "items": {
              "$ref": "#/components/schemas/SavedInsightItem"
            },
            "title": "Items",
            "type": "array"
          }
        },
        "required": [
          "items",
          "count"
        ],
        "title": "SavedInsightListResponse",
        "type": "object"
      },
      "SearchByFilterRequest": {
        "description": "POST-body filter for cross-month transaction search.\n\nSibling to ``GET /transactions/search`` \u2014 added in 2026-05 because URL-\nencoding 10+ comma-separated merchant names is awkward for agents and\ntooling. The GET stays for cacheability and existing frontend usage.\n\nArray fields are *any-of* matches. Empty / null arrays are treated as\n\"no filter on this dimension\". String exact-match fields (category,\ninstitution, type) are case-insensitive; ``merchant_in`` is a\ncase-insensitive substring match (each entry is a substring; an item\nmatches if any entry is contained in its company string).",
        "examples": [
          {
            "category_in": [
              "Groceries",
              "Restaurants"
            ],
            "from_month": "2026-01",
            "max_amount": 500,
            "min_amount": 10,
            "to_month": "2026-04"
          },
          {
            "from_month": "2026-04",
            "merchant_in": [
              "whole foods",
              "trader joe",
              "costco"
            ],
            "to_month": "2026-04"
          }
        ],
        "properties": {
          "category_in": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Any-of exact-match (case-insensitive) against Category.",
            "title": "Category In"
          },
          "from_month": {
            "description": "Inclusive start month, YYYY-MM.",
            "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
            "title": "From Month",
            "type": "string"
          },
          "include_deleted": {
            "default": false,
            "description": "If true, include soft-deleted rows.",
            "title": "Include Deleted",
            "type": "boolean"
          },
          "include_ignored": {
            "default": false,
            "description": "If true, include rows flagged Ignored.",
            "title": "Include Ignored",
            "type": "boolean"
          },
          "institution_in": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Any-of exact-match (case-insensitive) against Institution.",
            "title": "Institution In"
          },
          "max_amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Inclusive upper bound on Amount.",
            "title": "Max Amount"
          },
          "merchant_in": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Any-of substring match against Company. Each entry is a case-insensitive substring.",
            "title": "Merchant In"
          },
          "min_amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Inclusive lower bound on Amount.",
            "title": "Min Amount"
          },
          "to_month": {
            "description": "Inclusive end month, YYYY-MM. Capped at 24 months.",
            "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
            "title": "To Month",
            "type": "string"
          },
          "type_in": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Any-of exact-match (case-insensitive) against TransactionType.",
            "title": "Type In"
          }
        },
        "required": [
          "from_month",
          "to_month"
        ],
        "title": "SearchByFilterRequest",
        "type": "object"
      },
      "SearchResponse": {
        "examples": [
          {
            "capped": false,
            "summary": {
              "avg_amount": 26.63,
              "by_category": {
                "groceries": 612.45
              },
              "months_queried": 3,
              "total_amount": 612.45,
              "total_count": 23
            },
            "total_matching": 23,
            "transactions": []
          }
        ],
        "properties": {
          "capped": {
            "default": false,
            "title": "Capped",
            "type": "boolean"
          },
          "summary": {
            "$ref": "#/components/schemas/SearchSummary"
          },
          "total_matching": {
            "default": 0,
            "title": "Total Matching",
            "type": "integer"
          },
          "transactions": {
            "items": {
              "$ref": "#/components/schemas/TransactionResponse"
            },
            "title": "Transactions",
            "type": "array"
          }
        },
        "required": [
          "transactions",
          "summary"
        ],
        "title": "SearchResponse",
        "type": "object"
      },
      "SearchSummary": {
        "properties": {
          "avg_amount": {
            "title": "Avg Amount",
            "type": "number"
          },
          "by_category": {
            "additionalProperties": {
              "type": "number"
            },
            "title": "By Category",
            "type": "object"
          },
          "months_queried": {
            "title": "Months Queried",
            "type": "integer"
          },
          "total_amount": {
            "title": "Total Amount",
            "type": "number"
          },
          "total_count": {
            "title": "Total Count",
            "type": "integer"
          }
        },
        "required": [
          "total_count",
          "total_amount",
          "avg_amount",
          "by_category",
          "months_queried"
        ],
        "title": "SearchSummary",
        "type": "object"
      },
      "SetCategoryIconRequest": {
        "properties": {
          "icon": {
            "title": "Icon",
            "type": "string"
          }
        },
        "required": [
          "icon"
        ],
        "title": "SetCategoryIconRequest",
        "type": "object"
      },
      "StartChatgptLoginResponse": {
        "properties": {
          "user_code": {
            "title": "User Code",
            "type": "string"
          },
          "verification_url": {
            "title": "Verification Url",
            "type": "string"
          }
        },
        "required": [
          "verification_url",
          "user_code"
        ],
        "title": "StartChatgptLoginResponse",
        "type": "object"
      },
      "StatementDbMatch": {
        "description": "Reference to an existing transaction matched against a statement row.",
        "properties": {
          "amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Amount"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          }
        },
        "required": [
          "forwarded_to",
          "date_file_name",
          "company",
          "amount",
          "category"
        ],
        "title": "StatementDbMatch",
        "type": "object"
      },
      "StatementDbMatchWithType": {
        "description": "Variant carrying the existing transaction's type \u2014 used by the\nsuspected-duplicate flow so the frontend can show both the statement's\ninferred type and the existing record's type.",
        "properties": {
          "amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Amount"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "transaction_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Transaction Type"
          }
        },
        "required": [
          "forwarded_to",
          "date_file_name",
          "company",
          "amount",
          "category",
          "transaction_type"
        ],
        "title": "StatementDbMatchWithType",
        "type": "object"
      },
      "StatementDeleteResponse": {
        "properties": {
          "ok": {
            "title": "Ok",
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "StatementDeleteResponse",
        "type": "object"
      },
      "StatementDetailResponse": {
        "properties": {
          "account_type": {
            "title": "Account Type",
            "type": "string"
          },
          "ambiguous_count": {
            "title": "Ambiguous Count",
            "type": "integer"
          },
          "completed_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Completed At"
          },
          "duplicate_count": {
            "title": "Duplicate Count",
            "type": "integer"
          },
          "enriched_count": {
            "title": "Enriched Count",
            "type": "integer"
          },
          "filename": {
            "title": "Filename",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "imported_count": {
            "title": "Imported Count",
            "type": "integer"
          },
          "institution": {
            "title": "Institution",
            "type": "string"
          },
          "matched_count": {
            "title": "Matched Count",
            "type": "integer"
          },
          "new_count": {
            "title": "New Count",
            "type": "integer"
          },
          "parsed_with_ai": {
            "default": false,
            "title": "Parsed With Ai",
            "type": "boolean"
          },
          "period_end": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Period End"
          },
          "period_start": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Period Start"
          },
          "previously_imported_count": {
            "title": "Previously Imported Count",
            "type": "integer"
          },
          "skipped_count": {
            "title": "Skipped Count",
            "type": "integer"
          },
          "status": {
            "title": "Status",
            "type": "string"
          },
          "suspected_duplicate_count": {
            "title": "Suspected Duplicate Count",
            "type": "integer"
          },
          "total_parsed": {
            "title": "Total Parsed",
            "type": "integer"
          },
          "transactions": {
            "items": {
              "$ref": "#/components/schemas/StatementTransactionItem"
            },
            "title": "Transactions",
            "type": "array"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          },
          "updated_count": {
            "title": "Updated Count",
            "type": "integer"
          },
          "uploaded_at": {
            "title": "Uploaded At",
            "type": "string"
          }
        },
        "required": [
          "id",
          "filename",
          "institution",
          "account_type",
          "period_start",
          "period_end",
          "uploaded_at",
          "updated_at",
          "completed_at",
          "total_parsed",
          "matched_count",
          "ambiguous_count",
          "suspected_duplicate_count",
          "new_count",
          "previously_imported_count",
          "imported_count",
          "enriched_count",
          "updated_count",
          "skipped_count",
          "duplicate_count",
          "status",
          "transactions"
        ],
        "title": "StatementDetailResponse",
        "type": "object"
      },
      "StatementListResponse": {
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "statements": {
            "items": {
              "$ref": "#/components/schemas/StatementSummaryItem"
            },
            "title": "Statements",
            "type": "array"
          }
        },
        "required": [
          "statements",
          "count"
        ],
        "title": "StatementListResponse",
        "type": "object"
      },
      "StatementMetadata": {
        "properties": {
          "account_type": {
            "title": "Account Type",
            "type": "string"
          },
          "institution": {
            "title": "Institution",
            "type": "string"
          },
          "parsed_with_ai": {
            "default": false,
            "title": "Parsed With Ai",
            "type": "boolean"
          },
          "period_end": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Period End"
          },
          "period_start": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Period Start"
          },
          "transaction_count": {
            "title": "Transaction Count",
            "type": "integer"
          }
        },
        "required": [
          "institution",
          "account_type",
          "period_start",
          "period_end",
          "transaction_count"
        ],
        "title": "StatementMetadata",
        "type": "object"
      },
      "StatementSummaryItem": {
        "properties": {
          "account_type": {
            "title": "Account Type",
            "type": "string"
          },
          "ambiguous_count": {
            "title": "Ambiguous Count",
            "type": "integer"
          },
          "completed_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Completed At"
          },
          "duplicate_count": {
            "title": "Duplicate Count",
            "type": "integer"
          },
          "enriched_count": {
            "title": "Enriched Count",
            "type": "integer"
          },
          "filename": {
            "title": "Filename",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "imported_count": {
            "title": "Imported Count",
            "type": "integer"
          },
          "institution": {
            "title": "Institution",
            "type": "string"
          },
          "matched_count": {
            "title": "Matched Count",
            "type": "integer"
          },
          "new_count": {
            "title": "New Count",
            "type": "integer"
          },
          "parsed_with_ai": {
            "default": false,
            "title": "Parsed With Ai",
            "type": "boolean"
          },
          "period_end": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Period End"
          },
          "period_start": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Period Start"
          },
          "previously_imported_count": {
            "title": "Previously Imported Count",
            "type": "integer"
          },
          "skipped_count": {
            "title": "Skipped Count",
            "type": "integer"
          },
          "status": {
            "enum": [
              "pending_review",
              "in_progress",
              "complete"
            ],
            "title": "Status",
            "type": "string"
          },
          "suspected_duplicate_count": {
            "title": "Suspected Duplicate Count",
            "type": "integer"
          },
          "total_parsed": {
            "title": "Total Parsed",
            "type": "integer"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          },
          "updated_count": {
            "title": "Updated Count",
            "type": "integer"
          },
          "uploaded_at": {
            "title": "Uploaded At",
            "type": "string"
          }
        },
        "required": [
          "id",
          "filename",
          "institution",
          "account_type",
          "period_start",
          "period_end",
          "uploaded_at",
          "updated_at",
          "completed_at",
          "total_parsed",
          "matched_count",
          "ambiguous_count",
          "suspected_duplicate_count",
          "new_count",
          "previously_imported_count",
          "imported_count",
          "enriched_count",
          "updated_count",
          "skipped_count",
          "duplicate_count",
          "status"
        ],
        "title": "StatementSummaryItem",
        "type": "object"
      },
      "StatementTransaction": {
        "description": "Top-level statement row, enriched with the cleaned display description.\n\nUsed for `StatementUploadResponse.transactions` \u2014 the full list shown in\nthe upload preview before reconciliation tiers are applied.",
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "balance": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Balance"
          },
          "cleaned_description": {
            "title": "Cleaned Description",
            "type": "string"
          },
          "date": {
            "title": "Date",
            "type": "string"
          },
          "description": {
            "title": "Description",
            "type": "string"
          },
          "type": {
            "enum": [
              "withdrawal",
              "deposit"
            ],
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "date",
          "description",
          "amount",
          "type",
          "balance",
          "cleaned_description"
        ],
        "title": "StatementTransaction",
        "type": "object"
      },
      "StatementTransactionItem": {
        "properties": {
          "acted_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Acted At"
          },
          "action": {
            "title": "Action",
            "type": "string"
          },
          "action_result": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Action Result"
          },
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "balance": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Balance"
          },
          "candidates": {
            "anyOf": [
              {
                "items": {
                  "$ref": "#/components/schemas/StatementDbMatch"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Candidates"
          },
          "cleaned_description": {
            "title": "Cleaned Description",
            "type": "string"
          },
          "company_differs": {
            "title": "Company Differs",
            "type": "boolean"
          },
          "date": {
            "title": "Date",
            "type": "string"
          },
          "db_amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Db Amount"
          },
          "db_category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Db Category"
          },
          "db_company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Db Company"
          },
          "db_date_file_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Db Date File Name"
          },
          "db_forwarded_to": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Db Forwarded To"
          },
          "db_transaction_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Db Transaction Type"
          },
          "edited_category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Edited Category"
          },
          "edited_company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Edited Company"
          },
          "enrichable": {
            "title": "Enrichable",
            "type": "boolean"
          },
          "raw_description": {
            "title": "Raw Description",
            "type": "string"
          },
          "reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reason"
          },
          "reconcile_tier": {
            "enum": [
              "matched",
              "ambiguous",
              "suspected_duplicate",
              "new",
              "previously_imported"
            ],
            "title": "Reconcile Tier",
            "type": "string"
          },
          "row_id": {
            "title": "Row Id",
            "type": "string"
          },
          "suggested_category": {
            "title": "Suggested Category",
            "type": "string"
          },
          "tx_index": {
            "title": "Tx Index",
            "type": "integer"
          },
          "type": {
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "tx_index",
          "row_id",
          "reconcile_tier",
          "date",
          "raw_description",
          "cleaned_description",
          "amount",
          "type",
          "balance",
          "db_forwarded_to",
          "db_date_file_name",
          "db_company",
          "db_amount",
          "db_category",
          "db_transaction_type",
          "company_differs",
          "enrichable",
          "reason",
          "candidates",
          "suggested_category",
          "action",
          "edited_company",
          "edited_category",
          "action_result",
          "acted_at"
        ],
        "title": "StatementTransactionItem",
        "type": "object"
      },
      "StatementUploadResponse": {
        "properties": {
          "ambiguous": {
            "items": {
              "$ref": "#/components/schemas/AmbiguousItem"
            },
            "title": "Ambiguous",
            "type": "array"
          },
          "matched": {
            "items": {
              "$ref": "#/components/schemas/MatchedItem"
            },
            "title": "Matched",
            "type": "array"
          },
          "metadata": {
            "$ref": "#/components/schemas/StatementMetadata"
          },
          "new": {
            "items": {
              "$ref": "#/components/schemas/NewItem"
            },
            "title": "New",
            "type": "array"
          },
          "previously_imported": {
            "items": {
              "$ref": "#/components/schemas/PreviouslyImportedItem"
            },
            "title": "Previously Imported",
            "type": "array"
          },
          "statement_id": {
            "title": "Statement Id",
            "type": "string"
          },
          "summary": {
            "$ref": "#/components/schemas/ReconcileSummary"
          },
          "suspected_duplicates": {
            "items": {
              "$ref": "#/components/schemas/SuspectedDuplicateItem"
            },
            "title": "Suspected Duplicates",
            "type": "array"
          },
          "transactions": {
            "items": {
              "$ref": "#/components/schemas/StatementTransaction"
            },
            "title": "Transactions",
            "type": "array"
          }
        },
        "required": [
          "statement_id",
          "transactions",
          "metadata",
          "matched",
          "ambiguous",
          "suspected_duplicates",
          "new",
          "previously_imported",
          "summary"
        ],
        "title": "StatementUploadResponse",
        "type": "object"
      },
      "SuggestionDismissResponse": {
        "properties": {
          "detail": {
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "detail"
        ],
        "title": "SuggestionDismissResponse",
        "type": "object"
      },
      "SuggestionUndismissResponse": {
        "properties": {
          "detail": {
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "detail"
        ],
        "title": "SuggestionUndismissResponse",
        "type": "object"
      },
      "SummaryComparisonResponse": {
        "properties": {
          "current": {
            "$ref": "#/components/schemas/MonthSummary"
          },
          "delta_amount": {
            "title": "Delta Amount",
            "type": "number"
          },
          "delta_percent": {
            "title": "Delta Percent",
            "type": "number"
          },
          "pace": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MonthPaceInfo"
              },
              {
                "type": "null"
              }
            ]
          },
          "previous": {
            "$ref": "#/components/schemas/MonthSummary"
          }
        },
        "required": [
          "current",
          "previous",
          "delta_amount",
          "delta_percent"
        ],
        "title": "SummaryComparisonResponse",
        "type": "object"
      },
      "SuspectedDuplicateItem": {
        "properties": {
          "cleaned_description": {
            "title": "Cleaned Description",
            "type": "string"
          },
          "db_match": {
            "$ref": "#/components/schemas/StatementDbMatchWithType"
          },
          "index": {
            "title": "Index",
            "type": "integer"
          },
          "raw_description": {
            "title": "Raw Description",
            "type": "string"
          },
          "reason": {
            "title": "Reason",
            "type": "string"
          },
          "row_id": {
            "title": "Row Id",
            "type": "string"
          },
          "statement_txn": {
            "$ref": "#/components/schemas/RawStatementTxn"
          },
          "suggested_category": {
            "title": "Suggested Category",
            "type": "string"
          }
        },
        "required": [
          "index",
          "row_id",
          "statement_txn",
          "db_match",
          "cleaned_description",
          "raw_description",
          "suggested_category",
          "reason"
        ],
        "title": "SuspectedDuplicateItem",
        "type": "object"
      },
      "SuspectedIgnored": {
        "description": "A merchant active this month that resembles ones the user usually ignores.",
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "company": {
            "title": "Company",
            "type": "string"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "historical_ignored_share": {
            "title": "Historical Ignored Share",
            "type": "number"
          }
        },
        "required": [
          "company",
          "amount",
          "count",
          "historical_ignored_share"
        ],
        "title": "SuspectedIgnored",
        "type": "object"
      },
      "TaxEvidenceCounts": {
        "description": "How many of a line's transactions carry each kind of evidence.",
        "properties": {
          "email": {
            "title": "Email",
            "type": "integer"
          },
          "receipt": {
            "title": "Receipt",
            "type": "integer"
          },
          "statement": {
            "title": "Statement",
            "type": "integer"
          }
        },
        "required": [
          "receipt",
          "email",
          "statement"
        ],
        "title": "TaxEvidenceCounts",
        "type": "object"
      },
      "TaxLineOption": {
        "description": "A selectable claim line (for the include-override picker).",
        "properties": {
          "key": {
            "title": "Key",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          }
        },
        "required": [
          "key",
          "label"
        ],
        "title": "TaxLineOption",
        "type": "object"
      },
      "TaxLineResponse": {
        "description": "One claim line with its matched transactions for the year.",
        "properties": {
          "categories": {
            "items": {
              "type": "string"
            },
            "title": "Categories",
            "type": "array"
          },
          "cra_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Tax line reference, rendered verbatim.",
            "title": "Cra Ref"
          },
          "evidence_counts": {
            "$ref": "#/components/schemas/TaxEvidenceCounts"
          },
          "excluded_transactions": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/TaxPackTransaction"
            },
            "title": "Excluded Transactions",
            "type": "array"
          },
          "key": {
            "title": "Key",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "note": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional one-sentence note from the mapping seed.",
            "title": "Note"
          },
          "total": {
            "title": "Total",
            "type": "number"
          },
          "transaction_count": {
            "title": "Transaction Count",
            "type": "integer"
          },
          "transactions": {
            "items": {
              "$ref": "#/components/schemas/TaxPackTransaction"
            },
            "title": "Transactions",
            "type": "array"
          }
        },
        "required": [
          "key",
          "label",
          "categories",
          "total",
          "transaction_count",
          "evidence_counts",
          "transactions"
        ],
        "title": "TaxLineResponse",
        "type": "object"
      },
      "TaxLinesResponse": {
        "description": "The selectable claim lines: the seed lines plus the synthetic other line.",
        "properties": {
          "lines": {
            "items": {
              "$ref": "#/components/schemas/TaxLineOption"
            },
            "title": "Lines",
            "type": "array"
          }
        },
        "required": [
          "lines"
        ],
        "title": "TaxLinesResponse",
        "type": "object"
      },
      "TaxOverrideRequest": {
        "description": "Force a transaction into a line (``include``) or drop it out (``exclude``).",
        "properties": {
          "line_key": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Target line key; required when mode is include.",
            "title": "Line Key"
          },
          "mode": {
            "enum": [
              "include",
              "exclude"
            ],
            "title": "Mode",
            "type": "string"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "mode"
        ],
        "title": "TaxOverrideRequest",
        "type": "object"
      },
      "TaxPackResponse": {
        "description": "The full calendar-year tax pack: every mapped line plus a grand total.",
        "properties": {
          "grand_total": {
            "title": "Grand Total",
            "type": "number"
          },
          "lines": {
            "items": {
              "$ref": "#/components/schemas/TaxLineResponse"
            },
            "title": "Lines",
            "type": "array"
          },
          "year": {
            "title": "Year",
            "type": "integer"
          }
        },
        "required": [
          "year",
          "grand_total",
          "lines"
        ],
        "title": "TaxPackResponse",
        "type": "object"
      },
      "TaxPackTransaction": {
        "description": "One claimable transaction inside a tax line.",
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "category": {
            "title": "Category",
            "type": "string"
          },
          "company": {
            "title": "Company",
            "type": "string"
          },
          "date": {
            "description": "Transaction date, YYYY-MM-DD.",
            "title": "Date",
            "type": "string"
          },
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "evidence": {
            "title": "Evidence",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "manual": {
            "default": false,
            "title": "Manual",
            "type": "boolean"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "date",
          "company",
          "amount",
          "category",
          "evidence",
          "forwarded_to",
          "date_file_name"
        ],
        "title": "TaxPackTransaction",
        "type": "object"
      },
      "TestOpenAIRequest": {
        "properties": {
          "api_key": {
            "title": "Api Key",
            "type": "string"
          }
        },
        "required": [
          "api_key"
        ],
        "title": "TestOpenAIRequest",
        "type": "object"
      },
      "TestOpenAIResponse": {
        "properties": {
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "ok": {
            "title": "Ok",
            "type": "boolean"
          }
        },
        "required": [
          "ok",
          "error"
        ],
        "title": "TestOpenAIResponse",
        "type": "object"
      },
      "TestS3BackupRequest": {
        "properties": {
          "bucket": {
            "title": "Bucket",
            "type": "string"
          },
          "prefix": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Prefix"
          }
        },
        "required": [
          "bucket"
        ],
        "title": "TestS3BackupRequest",
        "type": "object"
      },
      "TestS3BackupResponse": {
        "properties": {
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "ok": {
            "title": "Ok",
            "type": "boolean"
          },
          "warnings": {
            "items": {
              "type": "string"
            },
            "title": "Warnings",
            "type": "array"
          }
        },
        "required": [
          "ok",
          "error",
          "warnings"
        ],
        "title": "TestS3BackupResponse",
        "type": "object"
      },
      "TopCategory": {
        "properties": {
          "amount": {
            "title": "Amount",
            "type": "number"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "name": {
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "name",
          "amount",
          "count"
        ],
        "title": "TopCategory",
        "type": "object"
      },
      "TransactionActionUpdate": {
        "properties": {
          "action": {
            "enum": [
              "import",
              "skip",
              "enrich",
              "update"
            ],
            "title": "Action",
            "type": "string"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          }
        },
        "required": [
          "action"
        ],
        "title": "TransactionActionUpdate",
        "type": "object"
      },
      "TransactionActionUpdateResponse": {
        "properties": {
          "action": {
            "title": "Action",
            "type": "string"
          },
          "ok": {
            "title": "Ok",
            "type": "boolean"
          },
          "row_id": {
            "title": "Row Id",
            "type": "string"
          },
          "tx_index": {
            "title": "Tx Index",
            "type": "integer"
          }
        },
        "required": [
          "ok",
          "tx_index",
          "row_id",
          "action"
        ],
        "title": "TransactionActionUpdateResponse",
        "type": "object"
      },
      "TransactionContext": {
        "properties": {
          "category_budget_pct": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category Budget Pct"
          },
          "category_budget_target": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category Budget Target"
          },
          "category_month_total": {
            "title": "Category Month Total",
            "type": "number"
          },
          "merchant_month_count": {
            "title": "Merchant Month Count",
            "type": "integer"
          }
        },
        "required": [
          "category_month_total",
          "merchant_month_count",
          "category_budget_target",
          "category_budget_pct"
        ],
        "title": "TransactionContext",
        "type": "object"
      },
      "TransactionDetailResponse": {
        "properties": {
          "body": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Body"
          },
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "from_email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "From Email"
          },
          "from_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "From Name"
          },
          "subject": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Subject"
          },
          "to_email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "To Email"
          },
          "to_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "To Name"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name",
          "subject",
          "body",
          "from_name",
          "from_email",
          "to_name",
          "to_email"
        ],
        "title": "TransactionDetailResponse",
        "type": "object"
      },
      "TransactionFieldsOldValues": {
        "properties": {
          "amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Amount"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "transaction_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Transaction Type"
          }
        },
        "required": [
          "company",
          "amount",
          "transaction_type"
        ],
        "title": "TransactionFieldsOldValues",
        "type": "object"
      },
      "TransactionFieldsUpdateRequest": {
        "properties": {
          "amount": {
            "anyOf": [
              {
                "exclusiveMinimum": 0.0,
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Amount"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "transaction_type": {
            "anyOf": [
              {
                "enum": [
                  "purchase",
                  "withdrawal",
                  "preauth",
                  "e-transfer",
                  "deposit"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Transaction Type"
          }
        },
        "title": "TransactionFieldsUpdateRequest",
        "type": "object"
      },
      "TransactionFieldsUpdateResponse": {
        "properties": {
          "amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Amount"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "old_values": {
            "$ref": "#/components/schemas/TransactionFieldsOldValues"
          },
          "transaction_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Transaction Type"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name",
          "company",
          "amount",
          "transaction_type",
          "category",
          "old_values"
        ],
        "title": "TransactionFieldsUpdateResponse",
        "type": "object"
      },
      "TransactionListResponse": {
        "examples": [
          {
            "count": 2,
            "month": "2026-04",
            "transactions": [
              {
                "amount": 42.31,
                "category": "groceries",
                "company": "WHOLE FOODS",
                "date": "04/15/2026 09:32 -0700",
                "date_file_name": "2026.04.15_09.32_email_01.eml",
                "forwarded_to": "default@local",
                "ignored": false,
                "institution": "RBC",
                "name": "default",
                "transaction_type": "purchase",
                "tx_id": "ZGVmYXVsdEBsb2NhbHwyMDI2LjA0LjE1XzA5LjMyX2VtYWlsXzAxLmVtbA"
              }
            ]
          }
        ],
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "month": {
            "title": "Month",
            "type": "string"
          },
          "transactions": {
            "items": {
              "$ref": "#/components/schemas/TransactionResponse"
            },
            "title": "Transactions",
            "type": "array"
          }
        },
        "required": [
          "month",
          "count",
          "transactions"
        ],
        "title": "TransactionListResponse",
        "type": "object"
      },
      "TransactionResponse": {
        "properties": {
          "amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Amount"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "category_audit": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CategoryAudit"
              },
              {
                "type": "null"
              }
            ]
          },
          "comment": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Comment"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "context": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/TransactionContext"
              },
              {
                "type": "null"
              }
            ]
          },
          "date": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Date"
          },
          "date_file_name": {
            "title": "Date File Name",
            "type": "string"
          },
          "deleted_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Deleted At"
          },
          "extraction_audit": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ExtractionAudit"
              },
              {
                "type": "null"
              }
            ]
          },
          "forwarded_to": {
            "title": "Forwarded To",
            "type": "string"
          },
          "ignored": {
            "title": "Ignored",
            "type": "boolean"
          },
          "institution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Institution"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "statement_source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Statement Source"
          },
          "transaction_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Transaction Type"
          },
          "tx_id": {
            "title": "Tx Id",
            "type": "string"
          }
        },
        "required": [
          "tx_id",
          "forwarded_to",
          "date_file_name",
          "date",
          "amount",
          "company",
          "category",
          "institution",
          "transaction_type",
          "name",
          "ignored",
          "comment",
          "deleted_at"
        ],
        "title": "TransactionResponse",
        "type": "object"
      },
      "TrendMonthEntry": {
        "properties": {
          "by_category": {
            "additionalProperties": {
              "$ref": "#/components/schemas/CategorySummary"
            },
            "title": "By Category",
            "type": "object"
          },
          "spending_count": {
            "title": "Spending Count",
            "type": "integer"
          },
          "total_spending": {
            "title": "Total Spending",
            "type": "number"
          },
          "year_month": {
            "title": "Year Month",
            "type": "string"
          }
        },
        "required": [
          "year_month",
          "total_spending",
          "spending_count",
          "by_category"
        ],
        "title": "TrendMonthEntry",
        "type": "object"
      },
      "TrendResponse": {
        "properties": {
          "months": {
            "items": {
              "$ref": "#/components/schemas/TrendMonthEntry"
            },
            "title": "Months",
            "type": "array"
          }
        },
        "required": [
          "months"
        ],
        "title": "TrendResponse",
        "type": "object"
      },
      "UnbudgetedCategory": {
        "properties": {
          "category": {
            "title": "Category",
            "type": "string"
          },
          "current_month_spent": {
            "title": "Current Month Spent",
            "type": "number"
          },
          "monthly_avg_historical": {
            "title": "Monthly Avg Historical",
            "type": "number"
          },
          "ytd_spent": {
            "title": "Ytd Spent",
            "type": "number"
          }
        },
        "required": [
          "category",
          "ytd_spent",
          "monthly_avg_historical",
          "current_month_spent"
        ],
        "title": "UnbudgetedCategory",
        "type": "object"
      },
      "UploadEmlResponse": {
        "properties": {
          "amount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Amount"
          },
          "category": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Category"
          },
          "company": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company"
          },
          "date_file_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Date File Name"
          },
          "detail": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Detail"
          },
          "status": {
            "description": "\"created\" on success, \"duplicate\" if already present",
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "title": "UploadEmlResponse",
        "type": "object"
      },
      "ValidationError": {
        "properties": {
          "ctx": {
            "title": "Context",
            "type": "object"
          },
          "input": {
            "title": "Input"
          },
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Location",
            "type": "array"
          },
          "msg": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "title": "Error Type",
            "type": "string"
          }
        },
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError",
        "type": "object"
      },
      "WhoamiResponse": {
        "description": "The resolved identity of the current caller.\n\n``token_id`` / ``label`` / ``scope`` are populated only for token principals\n(``kind == \"token\"``); they are ``null`` for cookie sessions, TOFU bootstrap,\nand the dev bypass. ``last_used_at`` is read from the token record and may lag\nbehind the current request by up to the ``mark_used`` throttle window.",
        "properties": {
          "kind": {
            "enum": [
              "token",
              "session",
              "tofu",
              "dev-bypass"
            ],
            "title": "Kind",
            "type": "string"
          },
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label"
          },
          "last_used_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Used At"
          },
          "scope": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Scope"
          },
          "token_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Token Id"
          }
        },
        "required": [
          "kind",
          "token_id",
          "label",
          "scope",
          "last_used_at"
        ],
        "title": "WhoamiResponse",
        "type": "object"
      },
      "_AuthResponse": {
        "properties": {
          "status": {
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "title": "_AuthResponse",
        "type": "object"
      },
      "_LoginIn": {
        "properties": {
          "password": {
            "minLength": 1,
            "title": "Password",
            "type": "string"
          }
        },
        "required": [
          "password"
        ],
        "title": "_LoginIn",
        "type": "object"
      },
      "_SetPasswordIn": {
        "properties": {
          "current_password": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Current Password"
          },
          "password": {
            "minLength": 8,
            "title": "Password",
            "type": "string"
          }
        },
        "required": [
          "password"
        ],
        "title": "_SetPasswordIn",
        "type": "object"
      },
      "_SignOutAllIn": {
        "properties": {
          "current_password": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Current Password"
          }
        },
        "title": "_SignOutAllIn",
        "type": "object"
      }
    }
  },
  "info": {
    "description": "Self-hosted personal finance dashboard API. Ingests bank transaction emails, categorizes them (with optional OpenAI enrichment), and exposes transactions, budgets, summaries, and AI spending insights over a consistent JSON HTTP interface.\n\n**Storage:** dual-backend \u2014 DynamoDB (AWS) or SQLite (local/demo). Selection is driven by `data/config.json`. All endpoints return identical shapes regardless of backend.\n\n**Versioning:** routes are prefixed with `/api/v1/`. Breaking changes will ship as `/api/v2/` rather than in-place.",
    "title": "Finance Dashboard API",
    "version": "0.1.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/activity": {
      "get": {
        "description": "Return ledger entries newest-first, filtered by the query params (L12).\n\n``principal`` accepts a raw token id or ``me``. For ``me`` on a token\nprincipal it resolves to that token's id and uses the store's principal\nfilter; for a non-token principal (session/tofu/dev-bypass) there is no\nstable id, so the store narrows by ``principal_kind`` \u2014 applied before the\nlimit, so the caller's own feed is never crowded out by token entries that\nwould otherwise consume the page. No pagination.",
        "operationId": "listActivity",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 500,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Inclusive ISO-8601 lower bound on entry timestamp",
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Inclusive ISO-8601 lower bound on entry timestamp",
              "title": "Since"
            }
          },
          {
            "description": "Filter by principal id (token id), or the literal 'me' for the current caller",
            "in": "query",
            "name": "principal",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by principal id (token id), or the literal 'me' for the current caller",
              "title": "Principal"
            }
          },
          {
            "description": "Filter by operation_id (exact match)",
            "in": "query",
            "name": "operation",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by operation_id (exact match)",
              "title": "Operation"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActivityListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List agent activity ledger entries, newest first",
        "tags": [
          "activity"
        ]
      }
    },
    "/api/v1/activity/{entry_id}/revert": {
      "post": {
        "description": "Undo one ledger entry by re-applying its recorded ``before`` image (L8).\n\n404 for an unknown id; 409 when the entry was already reverted, is not\nreversible, or its operation has no revert function. When the resource changed\nsince the entry was written the revert returns ``409 stale_revert`` (with the\nstale ``tx_id`` list for bulk) unless ``force=true``.\n\nDouble-revert safety is in-process: the handler stamps the original entry's\n``reverted_at`` / ``reverted_by`` synchronously (``store.mark_reverted``)\nbefore returning, and there is no ``await`` between ``get_entry`` and that\nstamp, so two concurrent reverts serialize on the event loop \u2014 the second\nreads ``reverted_at`` already set and 409s. The link points at a pre-generated\nid so it holds regardless of when the receipt is written.\n\nThe revert itself flows through the normal capture path, so it is journaled as\na new entry with its images kept for transparency (``summary = \"revert of\n<id>\"``) but marked ``reversible: false`` \u2014 redo is out of scope. Accepted\ntrade-off: that revert receipt is still written fire-and-forget, so a process\ndeath between the synchronous ``mark_reverted`` and the receipt write would\nleave ``reverted_by`` pointing at a receipt that never landed; the ledger is\nfail-open and the UI renders the reverted state from ``reverted_at`` alone.",
        "operationId": "revertActivity",
        "parameters": [
          {
            "in": "path",
            "name": "entry_id",
            "required": true,
            "schema": {
              "title": "Entry Id",
              "type": "string"
            }
          },
          {
            "description": "Override the stale-revert guard and apply the revert anyway",
            "in": "query",
            "name": "force",
            "required": false,
            "schema": {
              "default": false,
              "description": "Override the stale-revert guard and apply the revert anyway",
              "title": "Force",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevertResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Revert a single ledger entry, restoring the resource's prior state",
        "tags": [
          "activity"
        ]
      }
    },
    "/api/v1/attachments": {
      "get": {
        "operationId": "listAttachments",
        "parameters": [
          {
            "in": "query",
            "name": "unlinked",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Unlinked"
            }
          },
          {
            "in": "query",
            "name": "kind",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Kind"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List attachments, optionally filtered by link status and kind",
        "tags": [
          "attachments"
        ]
      },
      "post": {
        "description": "Store an uploaded receipt/document; link it immediately when ``tx_id`` is given.",
        "operationId": "uploadAttachment",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_uploadAttachment"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Upload a receipt or document, optionally linked to a transaction",
        "tags": [
          "attachments"
        ]
      }
    },
    "/api/v1/attachments/{attachment_id}": {
      "delete": {
        "operationId": "deleteAttachment",
        "parameters": [
          {
            "in": "path",
            "name": "attachment_id",
            "required": true,
            "schema": {
              "title": "Attachment Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentDeleteResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete an attachment and its file from disk",
        "tags": [
          "attachments"
        ]
      }
    },
    "/api/v1/attachments/{attachment_id}/candidates": {
      "get": {
        "description": "Return ranked match candidates for a parsed receipt.\n\n409 when the receipt has not been parsed. Read-only: when exactly one tier-1\ncandidate exists and the attachment arrived unlinked, ``auto_link_candidate``\nis true to signal the client should link the first candidate via\n``POST /attachments/{id}/link``; this handler performs no write, so the\n``read`` scope's GET-only contract holds and the demo dataset is untouched.",
        "operationId": "listReceiptMatchCandidates",
        "parameters": [
          {
            "in": "path",
            "name": "attachment_id",
            "required": true,
            "schema": {
              "title": "Attachment Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceiptCandidatesResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Rank the transactions a parsed receipt might explain",
        "tags": [
          "attachments"
        ]
      }
    },
    "/api/v1/attachments/{attachment_id}/file": {
      "get": {
        "operationId": "downloadAttachmentFile",
        "parameters": [
          {
            "in": "path",
            "name": "attachment_id",
            "required": true,
            "schema": {
              "title": "Attachment Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Download the attachment file, served inline with our stored content-type",
        "tags": [
          "attachments"
        ]
      }
    },
    "/api/v1/attachments/{attachment_id}/link": {
      "post": {
        "operationId": "linkAttachment",
        "parameters": [
          {
            "in": "path",
            "name": "attachment_id",
            "required": true,
            "schema": {
              "title": "Attachment Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkAttachmentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Link an attachment to a transaction, or unlink it",
        "tags": [
          "attachments"
        ]
      }
    },
    "/api/v1/attachments/{attachment_id}/parse": {
      "post": {
        "description": "Send the receipt to the user's AI provider and persist the parsed result.\n\nSynchronous (the statement-upload precedent runs AI inline). Requires the\n``ai_receipt_parsing_enabled`` consent; failures persist ``parse_status=failed``\nwith the error and return 422.",
        "operationId": "parseReceipt",
        "parameters": [
          {
            "in": "path",
            "name": "attachment_id",
            "required": true,
            "schema": {
              "title": "Attachment Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Parse a receipt attachment with the configured AI provider",
        "tags": [
          "attachments"
        ]
      }
    },
    "/api/v1/auth/chatgpt/disconnect": {
      "post": {
        "operationId": "disconnectChatgpt",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisconnectChatgptResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Disconnect a previously-linked ChatGPT account",
        "tags": [
          "auth"
        ]
      }
    },
    "/api/v1/auth/chatgpt/start": {
      "post": {
        "operationId": "startChatgptLogin",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StartChatgptLoginResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Begin the ChatGPT device-code sign-in via the Codex CLI",
        "tags": [
          "auth"
        ]
      }
    },
    "/api/v1/auth/chatgpt/status": {
      "get": {
        "operationId": "getChatgptLoginStatus",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatgptLoginStatusResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Poll the ChatGPT sign-in state",
        "tags": [
          "auth"
        ]
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "operationId": "webappLogin",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/_LoginIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/_AuthResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Issue a webapp session cookie",
        "tags": [
          "webapp-auth"
        ]
      }
    },
    "/api/v1/auth/logout": {
      "post": {
        "operationId": "webappLogout",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/_AuthResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Clear the caller's webapp session cookie",
        "tags": [
          "webapp-auth"
        ]
      }
    },
    "/api/v1/auth/set-password": {
      "post": {
        "operationId": "setAppPassword",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/_SetPasswordIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/_AuthResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Set or change the webapp password (TOFU on first set)",
        "tags": [
          "webapp-auth"
        ]
      }
    },
    "/api/v1/auth/sign-out-all": {
      "post": {
        "operationId": "webappSignOutAll",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/_SignOutAllIn"
                  },
                  {
                    "type": "null"
                  }
                ],
                "title": "Body"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/_AuthResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Bump session_version, invalidating all existing cookies",
        "tags": [
          "webapp-auth"
        ]
      }
    },
    "/api/v1/budget/config": {
      "get": {
        "operationId": "getBudgetConfig",
        "parameters": [
          {
            "in": "query",
            "name": "year",
            "required": true,
            "schema": {
              "maximum": 2099,
              "minimum": 2020,
              "title": "Year",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get budget targets and groups for a year",
        "tags": [
          "budget"
        ]
      },
      "put": {
        "operationId": "putBudgetConfig",
        "parameters": [
          {
            "in": "query",
            "name": "year",
            "required": true,
            "schema": {
              "maximum": 2099,
              "minimum": 2020,
              "title": "Year",
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BudgetConfigUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Replace budget targets and groups for a year",
        "tags": [
          "budget"
        ]
      }
    },
    "/api/v1/budget/historical-averages": {
      "get": {
        "operationId": "getBudgetHistoricalAverages",
        "parameters": [
          {
            "in": "query",
            "name": "months",
            "required": false,
            "schema": {
              "default": 6,
              "maximum": 12,
              "minimum": 2,
              "title": "Months",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistoricalAveragesResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Per-category historical monthly averages over the last N months",
        "tags": [
          "budget"
        ]
      }
    },
    "/api/v1/budget/status": {
      "get": {
        "operationId": "getBudgetStatus",
        "parameters": [
          {
            "in": "query",
            "name": "year",
            "required": true,
            "schema": {
              "maximum": 2099,
              "minimum": 2020,
              "title": "Year",
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "compare_year",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maximum": 2099,
                  "minimum": 2020,
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Compare Year"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetStatusResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "YTD budget pace status with per-category and per-group breakdown",
        "tags": [
          "budget"
        ]
      }
    },
    "/api/v1/categories": {
      "get": {
        "operationId": "listCategories",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoriesResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List predefined categories",
        "tags": [
          "categories"
        ]
      },
      "post": {
        "operationId": "addCategory",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryAddRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoriesManagementResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Add a category, optionally placing it in a budget group",
        "tags": [
          "category-management"
        ]
      }
    },
    "/api/v1/categories/icons": {
      "delete": {
        "description": "Remove the icon override for a category (revert to default).",
        "operationId": "clearCategoryIcon",
        "parameters": [
          {
            "description": "Category name (case-insensitive key)",
            "in": "query",
            "name": "name",
            "required": true,
            "schema": {
              "description": "Category name (case-insensitive key)",
              "title": "Name",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryIconsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Remove a category's icon override (revert to default)",
        "tags": [
          "category-management"
        ]
      },
      "get": {
        "description": "Return the full map of user-set category icon overrides.",
        "operationId": "listCategoryIcons",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryIconsResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Map of user-set category icon overrides",
        "tags": [
          "category-management"
        ]
      },
      "put": {
        "description": "Set or update the icon override for a category.\n\nName is a query parameter so category names containing slashes\n(e.g., \"Restaurant/Dining\") work without FastAPI path-param issues.",
        "operationId": "setCategoryIcon",
        "parameters": [
          {
            "description": "Category name (case-insensitive key)",
            "in": "query",
            "name": "name",
            "required": true,
            "schema": {
              "description": "Category name (case-insensitive key)",
              "title": "Name",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetCategoryIconRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryIconsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Set or update a category's icon override",
        "tags": [
          "category-management"
        ]
      }
    },
    "/api/v1/categories/managed": {
      "get": {
        "operationId": "listManagedCategories",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoriesManagementResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List categories with their group memberships and version",
        "tags": [
          "category-management"
        ]
      }
    },
    "/api/v1/categories/{name}": {
      "delete": {
        "operationId": "deleteCategory",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "title": "Name",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "reassign_to",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Reassign To"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryDeleteResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete a category, optionally reassigning its transactions",
        "tags": [
          "category-management"
        ]
      }
    },
    "/api/v1/categories/{name}/group": {
      "put": {
        "description": "Move a category to a different budget group, or remove from all groups.",
        "operationId": "updateCategoryGroup",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "title": "Name",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryGroupUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryGroupUpdateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Move a category to a different budget group, or remove from all groups",
        "tags": [
          "category-management"
        ]
      }
    },
    "/api/v1/categories/{name}/usage": {
      "get": {
        "operationId": "getCategoryUsage",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "title": "Name",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryUsageResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Counts of transactions, overrides, and budget membership for a category",
        "tags": [
          "category-management"
        ]
      }
    },
    "/api/v1/categories/{old_name}": {
      "put": {
        "operationId": "renameCategory",
        "parameters": [
          {
            "in": "path",
            "name": "old_name",
            "required": true,
            "schema": {
              "title": "Old Name",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryRenameRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryRenameResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Rename a category and cascade across overrides, budget, transactions",
        "tags": [
          "category-management"
        ]
      }
    },
    "/api/v1/config": {
      "get": {
        "operationId": "getAppConfig",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Get runtime app configuration (storage, demo mode, user_id, features)",
        "tags": [
          "config"
        ]
      },
      "put": {
        "operationId": "putAppConfig",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppConfigUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update runtime app configuration",
        "tags": [
          "config"
        ]
      }
    },
    "/api/v1/config/test-openai": {
      "post": {
        "description": "Validate an OpenAI API key and persist it to data/.env on success.",
        "operationId": "testOpenAIKey",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestOpenAIRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestOpenAIResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Validate an OpenAI API key and persist it to data/.env on success",
        "tags": [
          "config"
        ]
      }
    },
    "/api/v1/config/test-s3-backup": {
      "post": {
        "description": "Run stateless bucket checks. Persists nothing \u2014 the client saves via PUT /config.",
        "operationId": "testS3Backup",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestS3BackupRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestS3BackupResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Verify an S3 bucket is a usable attachment-backup target",
        "tags": [
          "config"
        ]
      }
    },
    "/api/v1/coverage": {
      "get": {
        "description": "Return each institution's modeled alert cadence \u2014 whether it is ``active``,\n``quiet``, ``dormant``, or ``irregular`` \u2014 plus the passive capture rate\nmeasured from statement reconciliation (``null`` where no statements exist).\n\nRead-only; served from a 1-hour in-memory cache.",
        "operationId": "getCoverage",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoverageResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Per-institution bank-alert cadence and passive email-capture rate",
        "tags": [
          "coverage"
        ]
      }
    },
    "/api/v1/data/export": {
      "post": {
        "description": "Stream a full-data backup zip. Disabled in demo mode.",
        "operationId": "exportFullBackup",
        "responses": {
          "200": {
            "description": "Successful Response"
          }
        },
        "summary": "Stream a full-data backup zip (transactions + config)",
        "tags": [
          "data"
        ]
      }
    },
    "/api/v1/data/import/commit": {
      "post": {
        "description": "Apply a previously previewed import using the chosen strategy.",
        "operationId": "commitBackupImport",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportCommitRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportResult"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Apply a previously-previewed import using the chosen strategy",
        "tags": [
          "data"
        ]
      }
    },
    "/api/v1/data/import/preview": {
      "post": {
        "description": "Upload a backup zip or CSV, stage it, and return a dry-run summary.",
        "operationId": "previewBackupImport",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_previewBackupImport"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportPreviewResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Stage a backup upload and return a dry-run dedup summary",
        "tags": [
          "data"
        ]
      }
    },
    "/api/v1/data/s3-backup-status": {
      "get": {
        "description": "Merge the S3-backup config keys with the sync engine's state file.\n\n``enabled``/``bucket``/``prefix`` come from config; the run metadata comes\nfrom the state file (zeroed defaults when it's absent or corrupt). Disabled\nin demo mode, matching the rest of this router.",
        "operationId": "getS3BackupStatus",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/S3BackupStatusResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Read the S3 attachment-backup config and last-run state",
        "tags": [
          "data"
        ]
      }
    },
    "/api/v1/groups": {
      "get": {
        "operationId": "getGroups",
        "parameters": [
          {
            "in": "query",
            "name": "year",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maximum": 2099,
                  "minimum": 2020,
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Year"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get budget category groups for a year",
        "tags": [
          "groups"
        ]
      },
      "put": {
        "operationId": "putGroups",
        "parameters": [
          {
            "in": "query",
            "name": "year",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maximum": 2099,
                  "minimum": 2020,
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Year"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupsUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Replace budget category groups for a year",
        "tags": [
          "groups"
        ]
      }
    },
    "/api/v1/health": {
      "get": {
        "description": "Return liveness + last-activity snapshot. Unauthenticated.\n\nReads three cheap signals \u2014 the IMAP heartbeat, the most-recent\ntransaction, and the count of emails quarantined in the last 7 days \u2014 and\nfolds them into ``status``. Every read is fail-open: a broken storage\nbackend (including a missing parse-failures table on a fresh boot) is\nlogged and degrades that field to ``None`` rather than 500-ing the probe.",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Liveness probe with last-activity snapshot",
        "tags": [
          "health"
        ]
      }
    },
    "/api/v1/ignore-rules": {
      "get": {
        "operationId": "listIgnoreRules",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgnoreRuleListResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List all merchant auto-ignore rules",
        "tags": [
          "ignore-rules"
        ]
      },
      "post": {
        "operationId": "addIgnoreRule",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IgnoreRuleAddRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgnoreRuleListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Add a merchant auto-ignore rule",
        "tags": [
          "ignore-rules"
        ]
      }
    },
    "/api/v1/ignore-rules/apply": {
      "post": {
        "description": "Set Ignored on existing non-deleted transactions matching the rule(s).\n\nOnly rows currently un-ignored are changed \u2014 there is no stored \"user\nun-ignored this\" signal, so the safe move is to never re-ignore a row the\nuser may have deliberately restored. ``matched`` counts every non-deleted\nrow the rule fires on; ``updated`` counts only the rows this call flipped.",
        "operationId": "applyIgnoreRules",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IgnoreRuleApplyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgnoreRuleApplyResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Backfill Ignored on existing transactions matching a rule (or all rules)",
        "tags": [
          "ignore-rules"
        ]
      }
    },
    "/api/v1/ignore-rules/suggestions": {
      "get": {
        "description": "Surface merchants with \u22653 recent transactions where \u226560% are ignored.\n\nMerchants already covered by a rule (via the tiered resolver) are excluded,\nso the list only offers rules that would actually change behavior. Merchants\nthe user has dismissed (case-insensitive) are excluded too \u2014 a dismissal\npersists until reversed via the un-dismiss endpoint.",
        "operationId": "getIgnoreRuleSuggestions",
        "parameters": [
          {
            "in": "query",
            "name": "months",
            "required": false,
            "schema": {
              "default": 12,
              "title": "Months",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgnoreRuleSuggestionsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Suggest ignore rules from merchants you habitually ignore by hand",
        "tags": [
          "ignore-rules"
        ]
      }
    },
    "/api/v1/ignore-rules/suggestions/dismissed": {
      "get": {
        "description": "Return every dismissed suggestion so the user can review or restore them.\n\nThe service normalizes the stored value shape (including legacy bare-string\ndismissals) and sorts newest-first; this handler just wraps the rows.",
        "operationId": "listDismissedIgnoreRuleSuggestions",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DismissedIgnoreRuleSuggestionsResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List dismissed ignore-rule suggestions, newest first",
        "tags": [
          "ignore-rules"
        ]
      },
      "post": {
        "operationId": "dismissIgnoreRuleSuggestion",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IgnoreRuleSuggestionDismissRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgnoreRuleSuggestionDismissResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Dismiss a suggested merchant so it stops being surfaced",
        "tags": [
          "ignore-rules"
        ]
      }
    },
    "/api/v1/ignore-rules/suggestions/dismissed/{merchant}": {
      "delete": {
        "operationId": "undismissIgnoreRuleSuggestion",
        "parameters": [
          {
            "in": "path",
            "name": "merchant",
            "required": true,
            "schema": {
              "title": "Merchant",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgnoreRuleSuggestionUndismissResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Reverse a suggestion dismissal so the merchant may resurface",
        "tags": [
          "ignore-rules"
        ]
      }
    },
    "/api/v1/ignore-rules/{pattern}": {
      "delete": {
        "operationId": "deleteIgnoreRule",
        "parameters": [
          {
            "in": "path",
            "name": "pattern",
            "required": true,
            "schema": {
              "title": "Pattern",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgnoreRuleDeleteResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Remove a merchant auto-ignore rule",
        "tags": [
          "ignore-rules"
        ]
      }
    },
    "/api/v1/income-statement": {
      "get": {
        "operationId": "getIncomeStatement",
        "parameters": [
          {
            "in": "query",
            "name": "year",
            "required": true,
            "schema": {
              "maximum": 2099,
              "minimum": 2020,
              "title": "Year",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomeStatementResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Annual income vs. expenses view with projection and savings rate",
        "tags": [
          "income-statement"
        ]
      }
    },
    "/api/v1/insights/context": {
      "get": {
        "description": "Return the raw context the AI briefing uses.\n\nSame dict that ``gather_context_to_file`` persists for the generation\nworker \u2014 exposed over HTTP so external consumers (Claude agents, the\nfrontend transparency panel, future automation) can inspect the inputs\nwithout re-running the aggregation.",
        "operationId": "getInsightsContext",
        "parameters": [
          {
            "description": "Month in YYYY-MM format.",
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "description": "Month in YYYY-MM format.",
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InsightsContextResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Raw context dict the AI briefing uses (for inspection / external automation)",
        "tags": [
          "insights"
        ]
      }
    },
    "/api/v1/insights/generate": {
      "post": {
        "operationId": "generateInsights",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InsightsGenerateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Kick off background AI generation of a monthly spending briefing",
        "tags": [
          "insights"
        ]
      }
    },
    "/api/v1/insights/saved": {
      "get": {
        "operationId": "listSavedInsights",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavedInsightListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List saved insight briefings for a month",
        "tags": [
          "insights"
        ]
      }
    },
    "/api/v1/insights/saved/{insight_id}": {
      "get": {
        "operationId": "getSavedInsight",
        "parameters": [
          {
            "in": "path",
            "name": "insight_id",
            "required": true,
            "schema": {
              "title": "Insight Id",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavedInsightDetail"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get the markdown content of a saved insight briefing",
        "tags": [
          "insights"
        ]
      }
    },
    "/api/v1/insights/status": {
      "get": {
        "operationId": "getInsightsGenerationStatus",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InsightsStatusResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Background generation status (idle/running/error)",
        "tags": [
          "insights"
        ]
      }
    },
    "/api/v1/journal": {
      "get": {
        "operationId": "getJournal",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JournalResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Day-grouped transaction timeline for a month",
        "tags": [
          "journal"
        ]
      }
    },
    "/api/v1/journal/summaries": {
      "get": {
        "operationId": "listDaySummaries",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DaySummariesResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List saved AI day summaries for a month",
        "tags": [
          "daily-summaries"
        ]
      }
    },
    "/api/v1/journal/summaries/generate": {
      "post": {
        "operationId": "generateDaySummaries",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DaySummaryGenerateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DaySummaryGenerateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Kick off background AI generation of day summaries for a month",
        "tags": [
          "daily-summaries"
        ]
      }
    },
    "/api/v1/journal/summaries/status": {
      "get": {
        "operationId": "getDaySummaryGenerationStatus",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DaySummaryStatusResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Background generation status (idle/running/error)",
        "tags": [
          "daily-summaries"
        ]
      }
    },
    "/api/v1/merchant-aliases": {
      "get": {
        "operationId": "listMerchantAliases",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantAliasListResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List all merchant aliases (raw \u2192 canonical mappings)",
        "tags": [
          "merchant-aliases"
        ]
      }
    },
    "/api/v1/merchant-aliases/{raw_name}": {
      "delete": {
        "operationId": "deleteMerchantAlias",
        "parameters": [
          {
            "in": "path",
            "name": "raw_name",
            "required": true,
            "schema": {
              "title": "Raw Name",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantAliasMutationResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete a merchant alias by raw name",
        "tags": [
          "merchant-aliases"
        ]
      },
      "put": {
        "operationId": "putMerchantAlias",
        "parameters": [
          {
            "in": "path",
            "name": "raw_name",
            "required": true,
            "schema": {
              "title": "Raw Name",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MerchantAliasPutRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantAliasMutationResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Create or update a merchant alias (raw \u2192 canonical)",
        "tags": [
          "merchant-aliases"
        ]
      }
    },
    "/api/v1/merchants/intelligence": {
      "get": {
        "description": "Return recurring-charge detection, price-change alerts, and committed\nburn rate for ``month`` using the prior ``months`` of summary data.",
        "operationId": "getMerchantIntelligence",
        "parameters": [
          {
            "description": "Month in YYYY-MM format.",
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "description": "Month in YYYY-MM format.",
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          },
          {
            "description": "Lookback window in months (default 6).",
            "in": "query",
            "name": "months",
            "required": false,
            "schema": {
              "default": 6,
              "description": "Lookback window in months (default 6).",
              "maximum": 24,
              "minimum": 1,
              "title": "Months",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantIntelligenceResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Recurring-charge detection, price-change alerts, committed burn rate",
        "tags": [
          "merchants"
        ]
      }
    },
    "/api/v1/overrides": {
      "get": {
        "operationId": "listOverrides",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverrideListResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List all category override rules",
        "tags": [
          "overrides"
        ]
      }
    },
    "/api/v1/overrides/consolidate": {
      "post": {
        "description": "Atomically replace `body.members` with a single `body.canonical_company` override.\n\nCreate + deletes run as a single unit: if the canonical key already\nexists (someone else added it) or any member was concurrently deleted,\nthe whole operation fails and no partial state lands. DynamoDB uses\n`TransactWriteItems`; SQLite wraps the equivalent writes in a single\ntransaction in the override service.",
        "operationId": "consolidateOverrides",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OverrideConsolidateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverrideConsolidateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Atomically replace member overrides with a single canonical override",
        "tags": [
          "overrides"
        ]
      }
    },
    "/api/v1/overrides/duplicates": {
      "get": {
        "description": "List override groups that share a normalized merchant key.\n\nUnanimous groups (every member agrees on category) are candidates for\none-click consolidation. Ambiguous groups (two or more distinct\ncategories under the same normalized key) surface as review-only \u2014 they\nfall through to OpenAI in the resolver today. Aliases are NOT applied\nhere; the grouping is built purely from `merchant_normalizer`'s regex\ncleanup so consolidation semantics stay predictable even as the alias\nmap evolves.",
        "operationId": "listOverrideDuplicates",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverrideDuplicatesResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List override groups sharing a normalized merchant key",
        "tags": [
          "overrides"
        ]
      }
    },
    "/api/v1/overrides/match": {
      "get": {
        "description": "Preview the tiered resolver against the current overrides + aliases.\n\nRead-only. Called by the add-rule form on the settings page to surface\n\"closest existing rule\" hints. Returns the primary resolver hit plus up\nto 5 additional fuzzy candidates with confidence \u2265 0.70 (disclosure\nthreshold \u2014 below the 0.90 auto-apply gate, used for user context).\nEmpty `candidates` + null fields when no tier matches.",
        "operationId": "matchOverride",
        "parameters": [
          {
            "description": "Raw company name to resolve",
            "in": "query",
            "name": "company",
            "required": true,
            "schema": {
              "description": "Raw company name to resolve",
              "minLength": 1,
              "title": "Company",
              "type": "string"
            }
          },
          {
            "description": "When true, build the embedding corpus from the user's full transaction history in addition to overrides. Use from the CategoryPicker so prior categorizations of a merchant in months not currently cached can still surface. The settings add-rule form leaves this off \u2014 DB items add noise for consolidation UX.",
            "in": "query",
            "name": "include_history",
            "required": false,
            "schema": {
              "default": false,
              "description": "When true, build the embedding corpus from the user's full transaction history in addition to overrides. Use from the CategoryPicker so prior categorizations of a merchant in months not currently cached can still surface. The settings add-rule form leaves this off \u2014 DB items add noise for consolidation UX.",
              "title": "Include History",
              "type": "boolean"
            }
          },
          {
            "description": "Minimum cosine similarity for fuzzy candidates. The 0.70 default is the disclosure floor used by the settings add-rule form. The CategoryPicker passes a lower value (e.g. 0.55) \u2014 short merchant strings cluster loosely under text-embedding-3-small and a weak but-non-zero suggestion still beats scrolling the full list.",
            "in": "query",
            "name": "min_score",
            "required": false,
            "schema": {
              "default": 0.7,
              "description": "Minimum cosine similarity for fuzzy candidates. The 0.70 default is the disclosure floor used by the settings add-rule form. The CategoryPicker passes a lower value (e.g. 0.55) \u2014 short merchant strings cluster loosely under text-embedding-3-small and a weak but-non-zero suggestion still beats scrolling the full list.",
              "maximum": 1.0,
              "minimum": 0.0,
              "title": "Min Score",
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverrideMatchResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Preview the tiered category resolver against current overrides + aliases",
        "tags": [
          "overrides"
        ]
      }
    },
    "/api/v1/overrides/suggestions": {
      "get": {
        "description": "Analyze manual category corrections to suggest new override rules.\n\nAlgorithm: find companies with 1+ manual corrections to the same target\ncategory that don't already have an override.",
        "operationId": "getOverrideSuggestions",
        "parameters": [
          {
            "in": "query",
            "name": "months",
            "required": false,
            "schema": {
              "default": 12,
              "maximum": 24,
              "minimum": 1,
              "title": "Months",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverrideSuggestionsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Suggest new override rules from manual category corrections",
        "tags": [
          "overrides"
        ]
      }
    },
    "/api/v1/overrides/suggestions/dismissed": {
      "post": {
        "operationId": "dismissOverrideSuggestion",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DismissSuggestionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuggestionDismissResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Dismiss an override suggestion (it stops surfacing until a newer correction)",
        "tags": [
          "overrides"
        ]
      }
    },
    "/api/v1/overrides/suggestions/dismissed/{key}": {
      "delete": {
        "operationId": "undismissOverrideSuggestion",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "required": true,
            "schema": {
              "title": "Key",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuggestionUndismissResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Reverse a previous dismissal (suggestion may resurface)",
        "tags": [
          "overrides"
        ]
      }
    },
    "/api/v1/overrides/{company}": {
      "delete": {
        "operationId": "deleteOverride",
        "parameters": [
          {
            "in": "path",
            "name": "company",
            "required": true,
            "schema": {
              "title": "Company",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverrideDeleteResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Remove a company \u2192 category override",
        "tags": [
          "overrides"
        ]
      },
      "put": {
        "operationId": "putOverride",
        "parameters": [
          {
            "in": "path",
            "name": "company",
            "required": true,
            "schema": {
              "title": "Company",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OverridePutRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverrideListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Pin a company \u2192 category override",
        "tags": [
          "overrides"
        ]
      }
    },
    "/api/v1/parse-failures": {
      "get": {
        "description": "Return parse-failure summaries, newest first, optionally filtered by status.",
        "operationId": "listParseFailures",
        "parameters": [
          {
            "description": "Optional status filter (quarantined, recovered, retried, dismissed).",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Optional status filter (quarantined, recovered, retried, dismissed).",
              "title": "Status"
            }
          },
          {
            "description": "Max rows to return.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "description": "Max rows to return.",
              "maximum": 10000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ParseFailureListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List quarantined parse failures (summaries only, no email body)",
        "tags": [
          "parse-failures"
        ]
      }
    },
    "/api/v1/parse-failures/retry-all": {
      "post": {
        "description": "Retry every quarantined row matching the filter through the same\ndeterministic (no-AI) path as single retry. Useful after a parser lands to\nrecover a backlog. At least one of ``institution`` / ``from_domain`` is\nrequired; the sweep is capped at 1,000 rows and runs synchronously.",
        "operationId": "retryAllParseFailures",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RetryAllRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RetryAllResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Re-run the deterministic parsers across a whole institution's quarantined backlog",
        "tags": [
          "parse-failures"
        ]
      }
    },
    "/api/v1/parse-failures/{failure_id}": {
      "delete": {
        "description": "Mark a quarantined parse failure as dismissed. 404 if it doesn't exist.",
        "operationId": "dismissParseFailure",
        "parameters": [
          {
            "in": "path",
            "name": "failure_id",
            "required": true,
            "schema": {
              "title": "Failure Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DismissResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Dismiss a quarantined parse failure",
        "tags": [
          "parse-failures"
        ]
      },
      "get": {
        "description": "Return a full parse-failure row, including the original email body.",
        "operationId": "getParseFailure",
        "parameters": [
          {
            "in": "path",
            "name": "failure_id",
            "required": true,
            "schema": {
              "title": "Failure Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ParseFailureDetail"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get a single quarantined parse failure, including the email body",
        "tags": [
          "parse-failures"
        ]
      }
    },
    "/api/v1/parse-failures/{failure_id}/resolve": {
      "post": {
        "description": "Record the values the user typed by hand for a quarantined email and mark\nthe failure resolved in one call.\n\nUnlike retry (which re-runs the deterministic parsers and can only ever\nconfirm a parser fix), this accepts manual field overrides \u2014 the recovery\npath for the long tail no parser will ever read. The created transaction\ncarries a ``manual`` CategoryAudit and no ExtractionAudit, so its provenance\nstays distinguishable from an AI-recovered row even though both reuse the\nstore's ``recovered`` status.",
        "operationId": "resolveParseFailure",
        "parameters": [
          {
            "in": "path",
            "name": "failure_id",
            "required": true,
            "schema": {
              "title": "Failure Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ManualResolveRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ManualResolveResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Record a hand-entered transaction for a quarantined email and mark it resolved",
        "tags": [
          "parse-failures"
        ]
      }
    },
    "/api/v1/parse-failures/{failure_id}/retry": {
      "post": {
        "description": "Re-run the deterministic parsers (no AI extraction) against the stored\nemail. A successful parse adds the transaction and marks the row retried;\na duplicate marks it retried; anything else leaves it quarantined.",
        "operationId": "retryParseFailure",
        "parameters": [
          {
            "in": "path",
            "name": "failure_id",
            "required": true,
            "schema": {
              "title": "Failure Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RetryResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Re-run the deterministic parsers against a quarantined email",
        "tags": [
          "parse-failures"
        ]
      }
    },
    "/api/v1/parse-failures/{failure_id}/to-fixture": {
      "post": {
        "description": "Write a scrubbed ``.txt`` + ``.json`` fixture pair from a captured email\nso a new parser can be authored from real evidence.\n\nGated to a dev checkout: this writes into the repo's ``tests/test_data/``\ntree, so it refuses (403) unless the server runs from a git checkout and is\nnot in demo mode. Never overwrites an existing pair (409).",
        "operationId": "parseFailureToFixture",
        "parameters": [
          {
            "in": "path",
            "name": "failure_id",
            "required": true,
            "schema": {
              "title": "Failure Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/FixtureFromFailureRequest"
                  },
                  {
                    "type": "null"
                  }
                ],
                "title": "Body"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FixtureFromFailureResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Write a scrubbed test-fixture pair from a quarantined email (dev checkout only)",
        "tags": [
          "parse-failures"
        ]
      }
    },
    "/api/v1/statements": {
      "get": {
        "description": "List all uploaded statements.",
        "operationId": "listStatements",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatementListResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List all uploaded statements",
        "tags": [
          "statements-crud"
        ]
      }
    },
    "/api/v1/statements/import": {
      "post": {
        "description": "Execute import actions for statement transactions.",
        "operationId": "importStatementTransactions",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Execute import/enrich/update actions for statement transactions",
        "tags": [
          "statements"
        ]
      }
    },
    "/api/v1/statements/upload": {
      "post": {
        "description": "Upload a PDF statement, parse it, and reconcile against DynamoDB.",
        "operationId": "uploadStatement",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_uploadStatement"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatementUploadResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Upload a PDF statement, parse, and reconcile against transactions",
        "tags": [
          "statements"
        ]
      }
    },
    "/api/v1/statements/{statement_id}": {
      "delete": {
        "description": "Delete a statement and its transactions.",
        "operationId": "deleteStatement",
        "parameters": [
          {
            "in": "path",
            "name": "statement_id",
            "required": true,
            "schema": {
              "title": "Statement Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatementDeleteResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete a statement and its parsed transactions",
        "tags": [
          "statements-crud"
        ]
      },
      "get": {
        "description": "Get a statement with all its transactions.",
        "operationId": "getStatement",
        "parameters": [
          {
            "in": "path",
            "name": "statement_id",
            "required": true,
            "schema": {
              "title": "Statement Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatementDetailResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get a statement with all its parsed transactions",
        "tags": [
          "statements-crud"
        ]
      }
    },
    "/api/v1/statements/{statement_id}/download": {
      "get": {
        "description": "Download the original PDF for a statement.",
        "operationId": "downloadStatementPdf",
        "parameters": [
          {
            "in": "path",
            "name": "statement_id",
            "required": true,
            "schema": {
              "title": "Statement Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/pdf": {}
            },
            "description": "Original statement PDF"
          },
          "404": {
            "description": "Statement or PDF not found"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Download the original PDF for a statement",
        "tags": [
          "statements-crud"
        ]
      }
    },
    "/api/v1/statements/{statement_id}/reparse": {
      "post": {
        "description": "Re-parse a statement PDF and reconcile, preserving user edits.",
        "operationId": "reparseStatement",
        "parameters": [
          {
            "in": "path",
            "name": "statement_id",
            "required": true,
            "schema": {
              "title": "Statement Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatementDetailResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Re-parse a statement PDF, preserving prior user edits",
        "tags": [
          "statements"
        ]
      }
    },
    "/api/v1/statements/{statement_id}/transactions": {
      "patch": {
        "description": "Bulk update transaction actions.",
        "operationId": "bulkUpdateStatementTransactions",
        "parameters": [
          {
            "in": "path",
            "name": "statement_id",
            "required": true,
            "schema": {
              "title": "Statement Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkTransactionUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkTransactionUpdateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Bulk-update statement transaction actions",
        "tags": [
          "statements-crud"
        ]
      }
    },
    "/api/v1/statements/{statement_id}/transactions/{row_id}": {
      "patch": {
        "description": "Update a single transaction's action (auto-save), keyed by stable row_id.\n\nPure-int paths are the legacy positional-index shape; they 410 with a\nmachine-readable code so consumers can migrate. There's no safe\nredirect (no mapping from \"row 5\" to a stable id without the full\nstatement payload).",
        "operationId": "updateStatementTransactionAction",
        "parameters": [
          {
            "in": "path",
            "name": "statement_id",
            "required": true,
            "schema": {
              "title": "Statement Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "row_id",
            "required": true,
            "schema": {
              "title": "Row Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransactionActionUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionActionUpdateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update a single statement transaction's action (auto-save), keyed by row_id",
        "tags": [
          "statements-crud"
        ]
      }
    },
    "/api/v1/summary": {
      "get": {
        "operationId": "getMonthlySummary",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryComparisonResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Monthly spending summary with previous-month comparison",
        "tags": [
          "summary"
        ]
      }
    },
    "/api/v1/summary/trend": {
      "get": {
        "operationId": "getSpendingTrend",
        "parameters": [
          {
            "in": "query",
            "name": "months",
            "required": false,
            "schema": {
              "default": 6,
              "maximum": 12,
              "minimum": 2,
              "title": "Months",
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "end_month",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "End Month"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrendResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Multi-month spending trend (2-12 months)",
        "tags": [
          "summary"
        ]
      }
    },
    "/api/v1/tax-pack": {
      "get": {
        "operationId": "getTaxPack",
        "parameters": [
          {
            "in": "query",
            "name": "year",
            "required": true,
            "schema": {
              "maximum": 2099,
              "minimum": 2020,
              "title": "Year",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxPackResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Calendar-year tax pack: claim-line totals with per-transaction evidence",
        "tags": [
          "tax"
        ]
      }
    },
    "/api/v1/tax-pack/export": {
      "get": {
        "description": "Stream the tax pack zip. Disabled in demo mode (reads host disk).",
        "operationId": "exportTaxPack",
        "parameters": [
          {
            "in": "query",
            "name": "year",
            "required": true,
            "schema": {
              "maximum": 2099,
              "minimum": 2020,
              "title": "Year",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Download the year's tax pack as a zip of CSVs plus evidence files",
        "tags": [
          "tax"
        ]
      }
    },
    "/api/v1/tax-pack/items": {
      "post": {
        "description": "Upsert a per-transaction tax override. Disabled in demo mode.",
        "operationId": "setTaxOverride",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaxOverrideRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Force a transaction into a claim line, or exclude it from one",
        "tags": [
          "tax"
        ]
      }
    },
    "/api/v1/tax-pack/items/{tx_id}": {
      "delete": {
        "description": "Delete a per-transaction tax override. Disabled in demo mode.",
        "operationId": "clearTaxOverride",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Clear a per-transaction tax override",
        "tags": [
          "tax"
        ]
      }
    },
    "/api/v1/tax-pack/lines": {
      "get": {
        "description": "Return the seed lines plus the synthetic other line as ``{key, label}``.",
        "operationId": "getTaxLines",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxLinesResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Selectable claim lines for the include-override picker",
        "tags": [
          "tax"
        ]
      }
    },
    "/api/v1/transactions": {
      "get": {
        "operationId": "listTransactions",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List active (non-deleted) transactions for a month",
        "tags": [
          "transactions"
        ]
      },
      "post": {
        "description": "Add a transaction manually (no email pipeline).",
        "operationId": "addManualTransaction",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ManualTransactionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ManualTransactionResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Add a transaction manually (bypasses email pipeline)",
        "tags": [
          "ingestion"
        ]
      }
    },
    "/api/v1/transactions/all": {
      "get": {
        "operationId": "listAllTransactions",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CombinedTransactionsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Combined active + attention + trash buckets for a month",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/attention": {
      "get": {
        "operationId": "getAttentionQueue",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttentionListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Transactions needing review (uncategorized, not ignored, not deleted)",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/bulk": {
      "get": {
        "operationId": "listBulkTransactions",
        "parameters": [
          {
            "description": "Comma-separated YYYY-MM list, max 12",
            "in": "query",
            "name": "months",
            "required": true,
            "schema": {
              "description": "Comma-separated YYYY-MM list, max 12",
              "title": "Months",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "$ref": "#/components/schemas/CombinedTransactionsResponse"
                  },
                  "title": "Response Listbulktransactions",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Combined transactions across multiple months (max 12)",
        "tags": [
          "transactions"
        ]
      },
      "patch": {
        "description": "Update the category on many transactions in a single request.\n\nEach ``updates`` item carries its own target category (unlike the internal\n``batch_update_category`` which applies one category to many rows). Rows\nthat fail to update are reported in ``results`` with ``ok=False`` and an\n``error`` message; the rest still apply \u2014 this endpoint does **not** roll\nback on partial failure.",
        "operationId": "bulkUpdateTransactionCategory",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkCategoryUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkCategoryUpdateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update categories on many transactions in a single request",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/export": {
      "get": {
        "operationId": "exportTransactionsCsv",
        "parameters": [
          {
            "description": "Inclusive start month, YYYY-MM.",
            "in": "query",
            "name": "from",
            "required": true,
            "schema": {
              "description": "Inclusive start month, YYYY-MM.",
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "From",
              "type": "string"
            }
          },
          {
            "description": "Inclusive end month, YYYY-MM. Must be >= `from`; range capped at 24 months.",
            "in": "query",
            "name": "to",
            "required": true,
            "schema": {
              "description": "Inclusive end month, YYYY-MM. Must be >= `from`; range capped at 24 months.",
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "To",
              "type": "string"
            }
          },
          {
            "description": "Free-text search across merchant, note, and category (case-insensitive substring; any-of).",
            "in": "query",
            "name": "q",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Free-text search across merchant, note, and category (case-insensitive substring; any-of).",
              "title": "Q"
            }
          },
          {
            "description": "Case-insensitive **exact** match (clean enum value).",
            "in": "query",
            "name": "category",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Case-insensitive **exact** match (clean enum value).",
              "title": "Category"
            }
          },
          {
            "description": "Case-insensitive **exact** match (clean enum value).",
            "in": "query",
            "name": "institution",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Case-insensitive **exact** match (clean enum value).",
              "title": "Institution"
            }
          },
          {
            "description": "Case-insensitive **substring** match. Merchant strings are noisy (payment-processor codes, store numbers), so partial match is the correct default.",
            "in": "query",
            "name": "company",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Case-insensitive **substring** match. Merchant strings are noisy (payment-processor codes, store numbers), so partial match is the correct default.",
              "title": "Company"
            }
          },
          {
            "description": "Case-insensitive **exact** match (clean enum value: purchase, withdrawal, preauth, e-transfer).",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Case-insensitive **exact** match (clean enum value: purchase, withdrawal, preauth, e-transfer).",
              "title": "Type"
            }
          },
          {
            "description": "Inclusive lower bound on Amount.",
            "in": "query",
            "name": "min_amount",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Inclusive lower bound on Amount.",
              "title": "Min Amount"
            }
          },
          {
            "description": "Inclusive upper bound on Amount.",
            "in": "query",
            "name": "max_amount",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Inclusive upper bound on Amount.",
              "title": "Max Amount"
            }
          },
          {
            "description": "If true, include rows flagged as Ignored.",
            "in": "query",
            "name": "include_ignored",
            "required": false,
            "schema": {
              "default": false,
              "description": "If true, include rows flagged as Ignored.",
              "title": "Include Ignored",
              "type": "boolean"
            }
          },
          {
            "description": "If true, include soft-deleted rows.",
            "in": "query",
            "name": "include_deleted",
            "required": false,
            "schema": {
              "default": false,
              "description": "If true, include soft-deleted rows.",
              "title": "Include Deleted",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/csv": {}
            },
            "description": "CSV stream of filtered transactions"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Stream filtered transactions as CSV",
        "tags": [
          "search"
        ]
      }
    },
    "/api/v1/transactions/latest": {
      "get": {
        "description": "Freshness probe \u2014 returns the max DateFileName, optionally scoped to a month.\n\nUsed by the frontend to detect new transactions without refetching full\nmonth data. `latest` is a lex-sortable DateFileName string (e.g.\n\"2026.04.20_14.32_...\") or null if no matching rows.",
        "operationId": "getLatestTransactionTimestamp",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Month"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LatestTimestampResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Freshness probe \u2014 max DateFileName, optionally scoped to a month",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/search": {
      "get": {
        "operationId": "searchTransactions",
        "parameters": [
          {
            "description": "Inclusive start month, YYYY-MM.",
            "in": "query",
            "name": "from",
            "required": true,
            "schema": {
              "description": "Inclusive start month, YYYY-MM.",
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "From",
              "type": "string"
            }
          },
          {
            "description": "Inclusive end month, YYYY-MM. Must be >= `from`; range capped at 24 months.",
            "in": "query",
            "name": "to",
            "required": true,
            "schema": {
              "description": "Inclusive end month, YYYY-MM. Must be >= `from`; range capped at 24 months.",
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "To",
              "type": "string"
            }
          },
          {
            "description": "Free-text search across merchant, note, and category (case-insensitive substring; any-of).",
            "in": "query",
            "name": "q",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Free-text search across merchant, note, and category (case-insensitive substring; any-of).",
              "title": "Q"
            }
          },
          {
            "description": "Case-insensitive **exact** match (clean enum value).",
            "in": "query",
            "name": "category",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Case-insensitive **exact** match (clean enum value).",
              "title": "Category"
            }
          },
          {
            "description": "Case-insensitive **exact** match (clean enum value).",
            "in": "query",
            "name": "institution",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Case-insensitive **exact** match (clean enum value).",
              "title": "Institution"
            }
          },
          {
            "description": "Case-insensitive **substring** match. Merchant strings are noisy (payment-processor codes, store numbers), so partial match is the correct default.",
            "in": "query",
            "name": "company",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Case-insensitive **substring** match. Merchant strings are noisy (payment-processor codes, store numbers), so partial match is the correct default.",
              "title": "Company"
            }
          },
          {
            "description": "Case-insensitive **exact** match (clean enum value: purchase, withdrawal, preauth, e-transfer).",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Case-insensitive **exact** match (clean enum value: purchase, withdrawal, preauth, e-transfer).",
              "title": "Type"
            }
          },
          {
            "description": "Inclusive lower bound on Amount.",
            "in": "query",
            "name": "min_amount",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Inclusive lower bound on Amount.",
              "title": "Min Amount"
            }
          },
          {
            "description": "Inclusive upper bound on Amount.",
            "in": "query",
            "name": "max_amount",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Inclusive upper bound on Amount.",
              "title": "Max Amount"
            }
          },
          {
            "description": "If true, include rows flagged as Ignored.",
            "in": "query",
            "name": "include_ignored",
            "required": false,
            "schema": {
              "default": false,
              "description": "If true, include rows flagged as Ignored.",
              "title": "Include Ignored",
              "type": "boolean"
            }
          },
          {
            "description": "If true, include soft-deleted rows.",
            "in": "query",
            "name": "include_deleted",
            "required": false,
            "schema": {
              "default": false,
              "description": "If true, include soft-deleted rows.",
              "title": "Include Deleted",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Cross-month transaction search with filters",
        "tags": [
          "search"
        ]
      }
    },
    "/api/v1/transactions/search-by-filter": {
      "post": {
        "description": "POST sibling to ``GET /transactions/search``.\n\nAccepts ``merchant_in``, ``category_in``, ``institution_in``, ``type_in``\narrays (any-of) plus the same amount range and visibility flags. Use\nwhen URL-encoding many filter values is awkward (e.g. an agent passing\na list of 20 merchants).",
        "operationId": "searchTransactionsByFilter",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchByFilterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Cross-month transaction search with array-shaped filters (POST body)",
        "tags": [
          "search"
        ]
      }
    },
    "/api/v1/transactions/trash": {
      "get": {
        "operationId": "listTrashedTransactions",
        "parameters": [
          {
            "in": "query",
            "name": "month",
            "required": true,
            "schema": {
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "title": "Month",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Soft-deleted transactions for a month",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/upload-eml": {
      "post": {
        "description": "Upload a raw .eml file and parse it through the email pipeline.",
        "operationId": "uploadEml",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_uploadEml"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadEmlResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Upload a raw .eml file and parse via the email pipeline",
        "tags": [
          "ingestion"
        ]
      }
    },
    "/api/v1/transactions/{tx_id}": {
      "delete": {
        "operationId": "permanentlyDeleteTransaction",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermanentDeleteResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Hard-delete a transaction (irreversible)",
        "tags": [
          "transactions"
        ]
      },
      "patch": {
        "description": "Apply any subset of {category, state, reviewed} to a transaction.\n\nState semantics \u2014 the value provided IS the state after the call:\n\n- ``active``  \u2192 clears both Ignored and DeletedAt.\n- ``ignored`` \u2192 sets Ignored. Preserves DeletedAt; if the row is currently\n  trashed, the derived state stays trashed (trashed wins). PATCH\n  ``state=\"active\"`` first to bring a trashed row back, then\n  ``state=\"ignored\"`` to ignore it.\n- ``trashed`` \u2192 sets DeletedAt to now.\n\nEmpty body is a no-op (returns all-None response). Missing fields are\nnot modified.",
        "operationId": "patchTransaction",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryUpdateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Partial update \u2014 category, state (active/ignored/trashed), and/or reviewed",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/{tx_id}/attachments": {
      "get": {
        "operationId": "listTransactionAttachments",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List attachments linked to a transaction",
        "tags": [
          "attachments"
        ]
      }
    },
    "/api/v1/transactions/{tx_id}/comment": {
      "put": {
        "operationId": "setTransactionComment",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommentResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Set or update the user comment on a transaction",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/{tx_id}/delete": {
      "post": {
        "deprecated": true,
        "description": "Deprecated \u2014 use `PATCH /transactions/{tx_id}` with `{state: 'trashed'}` or `{state: 'active'}` instead.",
        "operationId": "softDeleteTransaction",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Toggle the soft-delete flag (move to trash / restore)",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/{tx_id}/detail": {
      "get": {
        "operationId": "getTransactionDetail",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDetailResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Full transaction record including email body and headers",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/{tx_id}/fields": {
      "put": {
        "operationId": "updateTransactionFields",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransactionFieldsUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionFieldsUpdateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update editable transaction fields (company, amount, transaction_type)",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/{tx_id}/ignore": {
      "post": {
        "deprecated": true,
        "description": "Deprecated \u2014 use `PATCH /transactions/{tx_id}` with `{state: 'ignored'}` or `{state: 'active'}` instead.",
        "operationId": "setTransactionIgnored",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IgnoreRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgnoreResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Toggle the ignored flag on a transaction",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/transactions/{tx_id}/review": {
      "post": {
        "deprecated": true,
        "description": "Deprecated \u2014 use `PATCH /transactions/{tx_id}` with `{reviewed: true}` instead.",
        "operationId": "markTransactionReviewed",
        "parameters": [
          {
            "in": "path",
            "name": "tx_id",
            "required": true,
            "schema": {
              "title": "Tx Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReviewResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Mark a transaction's category as manually reviewed",
        "tags": [
          "transactions"
        ]
      }
    },
    "/api/v1/whoami": {
      "get": {
        "description": "Return the resolved identity of the current caller.\n\nFor token principals the response carries the token id, label, scope, and the\nstored ``last_used_at`` (which may lag the current request by up to the\n``mark_used`` throttle window). Non-token principals report ``null`` for all\ntoken fields.",
        "operationId": "getWhoami",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhoamiResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Resolve the current caller's identity (token, session, TOFU, or dev-bypass)",
        "tags": [
          "activity"
        ]
      }
    }
  },
  "tags": [
    {
      "description": "App configuration (storage backend, demo mode, user_id).",
      "name": "config"
    },
    {
      "description": "Transaction ingestion \u2014 upload and reparse raw email bodies.",
      "name": "ingestion"
    },
    {
      "description": "Transaction CRUD, soft delete, attention queue, bulk updates.",
      "name": "transactions"
    },
    {
      "description": "Predefined category list used by OpenAI function calling.",
      "name": "categories"
    },
    {
      "description": "Monthly spending summaries, trends, and comparisons.",
      "name": "summary"
    },
    {
      "description": "Budget targets, pace status, and historical averages.",
      "name": "budget"
    },
    {
      "description": "AI spending intelligence \u2014 streamed briefings and the raw context they use.",
      "name": "insights"
    },
    {
      "description": "Company\u2192category override rules + suggestion engine.",
      "name": "overrides"
    },
    {
      "description": "Merchant auto-ignore rules \u2014 pin a merchant to ignored, backfill history, suggestions.",
      "name": "ignore-rules"
    },
    {
      "description": "Add, rename, delete categories in the master list.",
      "name": "category-management"
    },
    {
      "description": "Budget display-only category groupings.",
      "name": "groups"
    },
    {
      "description": "Cross-month transaction search with CSV export.",
      "name": "search"
    },
    {
      "description": "Statement PDF upload, parsing, and four-tier reconciliation.",
      "name": "statements"
    },
    {
      "description": "Persisted statement state \u2014 list, detail, delete, edit transactions.",
      "name": "statements-crud"
    },
    {
      "description": "Per-transaction receipts and documents \u2014 upload, list, download, link, delete.",
      "name": "attachments"
    },
    {
      "description": "Yearly personal income statement aggregation.",
      "name": "income-statement"
    },
    {
      "description": "Calendar-year tax pack: claim-line totals with per-transaction evidence, plus a zip export.",
      "name": "tax"
    },
    {
      "description": "Merchant intelligence \u2014 recurring-charge detection, price changes, burn rate.",
      "name": "merchants"
    },
    {
      "description": "Day-grouped transaction timeline with MTD running totals.",
      "name": "journal"
    },
    {
      "description": "AI-generated per-day narrative summaries for the journal.",
      "name": "daily-summaries"
    },
    {
      "description": "Merchant alias CRUD for the Tier 2 override resolver.",
      "name": "merchant-aliases"
    },
    {
      "description": "Full-data backup: export as zip, import with dedup preview.",
      "name": "data"
    },
    {
      "description": "Quarantined bank emails the parsers couldn't read \u2014 review, retry, dismiss.",
      "name": "parse-failures"
    },
    {
      "description": "Third-party OAuth flows (ChatGPT).",
      "name": "auth"
    },
    {
      "description": "Webapp cookie-session auth \u2014 set/change password, login, logout, sign out all.",
      "name": "webapp-auth"
    },
    {
      "description": "Liveness + last-activity probe (unauthenticated).",
      "name": "health"
    },
    {
      "description": "Per-institution bank-alert cadence \u2014 quiet/dormant detection and passive capture rate.",
      "name": "coverage"
    },
    {
      "description": "Agent activity ledger \u2014 caller introspection (whoami), the write journal, and stale-guarded revert.",
      "name": "activity"
    }
  ]
}
