{
  "openapi": "3.0.4",
  "info": {
    "title": "Oricalo API",
    "description": "Read and update a personal budget in Oricalo: daily entries, categories, money accounts, monthly budgets, recurring bills and income, savings pots and debts. Authenticate with a personal API key (Settings → AI & API in the app; read-only or read & write) or an OAuth access token from a connected app. Deleting an entry moves it to a 30-day trash. Guide: https://oricalo.com/developers · MCP server for AI assistants: https://oricalo.com/server/mcp",
    "termsOfService": "https://oricalo.com/terms",
    "contact": {
      "name": "Oricalo",
      "url": "https://oricalo.com/developers",
      "email": "info@oricalo.com"
    },
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://oricalo.com/server",
      "description": "Oricalo API"
    }
  ],
  "paths": {
    "/api/account": {
      "get": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AccountDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/account/usage": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Current usage against the user's plan limits — daily entries for the given\r\nmonth, recurring lines for the given year, and total categories. Defaults to\r\nthe current month/year. Drives the \"X / Y\" usage meters and upgrade nudges.",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PlanUsage"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanUsage"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanUsage"
                }
              }
            }
          }
        }
      }
    },
    "/api/account/export": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Export ALL of the signed-in user's own data as a downloadable JSON file\r\n(GDPR data portability / right to access). Scoped to the caller's own\r\nrecords — the same set that account deletion removes — so a household\r\nmember exports their own data, never the owner's shared dataset.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/accounts": {
      "get": {
        "tags": [
          "Accounts"
        ],
        "summary": "All accounts for the owner, each with its live balance (few per owner).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MoneyAccountDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MoneyAccountDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MoneyAccountDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Accounts"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoneyAccountInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/MoneyAccountInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/MoneyAccountInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/{id}": {
      "get": {
        "tags": [
          "Accounts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Accounts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoneyAccountInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/MoneyAccountInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/MoneyAccountInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Accounts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/accounts/{id}/default": {
      "put": {
        "tags": [
          "Accounts"
        ],
        "summary": "Make this account the owner's default (preselected when adding a new daily entry).\r\nExactly one default per owner — this clears the flag on all the owner's other accounts.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneyAccountDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/reorder": {
      "post": {
        "tags": [
          "Accounts"
        ],
        "summary": "New display order for the accounts grid — sortOrder = list index.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "text/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "application/*+json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/account-transfers": {
      "get": {
        "tags": [
          "AccountTransfers"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AccountTransferDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AccountTransferDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AccountTransferDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "AccountTransfers"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountTransferInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountTransferInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/AccountTransferInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AccountTransferDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountTransferDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountTransferDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/account-transfers/{id}": {
      "delete": {
        "tags": [
          "AccountTransfers"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/budgets": {
      "get": {
        "tags": [
          "Budgets"
        ],
        "summary": "Budgeted amounts per category for a year (only categories with a budget set).",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BudgetRowDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BudgetRowDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BudgetRowDto"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/budgets/{categoryId}/{year}": {
      "put": {
        "tags": [
          "Budgets"
        ],
        "summary": "Upsert a single month's budget for a category.",
        "parameters": [
          {
            "name": "categoryId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "year",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetBudgetDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/SetBudgetDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/SetBudgetDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/budgets/{categoryId}/fill/{year}": {
      "put": {
        "tags": [
          "Budgets"
        ],
        "summary": "Fill the same budget across many months of one category (horizontal fill).",
        "parameters": [
          {
            "name": "categoryId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "year",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FillBudgetDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/FillBudgetDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/FillBudgetDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/cashflowlines": {
      "get": {
        "tags": [
          "CashflowLines"
        ],
        "summary": "Income + fixed-expense lines for a given year (each year has its own set).",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CashflowLineDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CashflowLineDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CashflowLineDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "CashflowLines"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CashflowLineInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CashflowLineInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CashflowLineInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CashflowLineDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CashflowLineDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CashflowLineDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/cashflowlines/{id}": {
      "put": {
        "tags": [
          "CashflowLines"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CashflowLineInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CashflowLineInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CashflowLineInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "CashflowLines"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/cashflowlines/reorder": {
      "post": {
        "tags": [
          "CashflowLines"
        ],
        "summary": "Persist a new ordering: sortOrder = position in the supplied id list.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReorderDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/ReorderDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/ReorderDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/cashflowlines/copy": {
      "post": {
        "tags": [
          "CashflowLines"
        ],
        "summary": "Clone line definitions (name/kind/order) from one year to another.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CopyLinesDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CopyLinesDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CopyLinesDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/cashflowlines/{id}/amount/{year}": {
      "put": {
        "tags": [
          "CashflowLines"
        ],
        "summary": "Upsert a single month's amount for a line.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "year",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetAmountDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/SetAmountDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/SetAmountDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/cashflowlines/{id}/fill/{year}": {
      "put": {
        "tags": [
          "CashflowLines"
        ],
        "summary": "Fill the same amount across many months of one line in a single call —\r\nthe \"apply across\" (and per-line slice of \"apply down\") grid action.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "year",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FillAmountDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/FillAmountDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/FillAmountDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/categories": {
      "get": {
        "tags": [
          "Categories"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CategoryDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CategoryDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CategoryDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Categories"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/categories/{id}": {
      "put": {
        "tags": [
          "Categories"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryDto"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Categories"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/debts": {
      "get": {
        "tags": [
          "Debts"
        ],
        "summary": "All debts for the owner, each with its full merged ledger (few per owner).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DebtDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DebtDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DebtDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Debts"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DebtInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/DebtInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/DebtInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/debts/{id}": {
      "get": {
        "tags": [
          "Debts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Debts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DebtInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/DebtInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/DebtInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Debts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/debts/{id}/payments": {
      "post": {
        "tags": [
          "Debts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DebtPaymentInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/DebtPaymentInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/DebtPaymentInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/debts/{id}/payments/{paymentId}": {
      "delete": {
        "tags": [
          "Debts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "paymentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/recurring": {
      "get": {
        "tags": [
          "Recurring"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RecurringRuleDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RecurringRuleDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RecurringRuleDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Recurring"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecurringRuleInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/RecurringRuleInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/RecurringRuleInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/recurring/{id}": {
      "get": {
        "tags": [
          "Recurring"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Recurring"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecurringRuleInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/RecurringRuleInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/RecurringRuleInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Recurring"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/recurring/cashflow": {
      "get": {
        "tags": [
          "Recurring"
        ],
        "summary": "Project each active rule's <em>future</em> (not-yet-generated) occurrences across the given\r\nyear into 12 monthly sums, for the Cashflow view's \"virtual\" lines. Projection starts at the\r\nrule's ZenCashFlow.Api.Models.RecurringRule.NextRunDate, so occurrences that already posted as real\r\ndaily entries are excluded — the Cashflow folds these projected amounts into its totals, and\r\ndouble-counting them against the daily roll-ups would inflate elapsed months. (A past year\r\ntherefore projects nothing: its NextRunDate is beyond the year's end.)",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/VirtualCashflowLineDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/VirtualCashflowLineDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/VirtualCashflowLineDto"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/recurring/occurrences": {
      "get": {
        "tags": [
          "Recurring"
        ],
        "summary": "Future (not-yet-generated) recurring occurrences that fall within one month, one row per\r\noccurrence with its day-of-month — drives the Daily spending calendar's \"recurring due\"\r\nmarkers. Mirrors M:ZenCashFlow.Api.Controllers.RecurringController.Cashflow(System.Int32)'s \"project from NextRunDate forward\" rule, so\r\nalready-posted occurrences (now real daily entries) are excluded.",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RecurringOccurrenceDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RecurringOccurrenceDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RecurringOccurrenceDto"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/recurring/{id}/post-occurrence": {
      "post": {
        "tags": [
          "Recurring"
        ],
        "summary": "\"Mark as paid\": post the rule's next upcoming occurrence now — create its (Completed) transaction\r\nand advance the rule past it, exactly as the daily sweep would when it came due. This stops the\r\nengine regenerating that occurrence later (no duplicate). Only the rule's *next* occurrence may be\r\nposted, so occurrences between now and a later one are never silently skipped.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostOccurrenceDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PostOccurrenceDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PostOccurrenceDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/recurring/past-occurrences": {
      "post": {
        "tags": [
          "Recurring"
        ],
        "summary": "Preview for the create form: the occurrences a rule with this schedule and a past start date\r\nwould skip (start date up to yesterday). Nothing is saved. An incomplete form previews as none.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecurringRuleInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/RecurringRuleInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/RecurringRuleInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PastOccurrencesDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PastOccurrencesDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PastOccurrencesDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/recurring/reorder": {
      "post": {
        "tags": [
          "Recurring"
        ],
        "summary": "Persist a new display order for recurring rows (Cashflow drag-to-reorder). The client sends\r\nthe rule ids of one section in their new order; each is assigned ZenCashFlow.Api.Models.RecurringRule.SortOrder\r\nby position. Ids not owned by the caller are ignored.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "text/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "application/*+json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/recurring/{id}/pause": {
      "post": {
        "tags": [
          "Recurring"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/recurring/{id}/resume": {
      "post": {
        "tags": [
          "Recurring"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecurringRuleDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/savingspots": {
      "get": {
        "tags": [
          "SavingsPots"
        ],
        "summary": "All pots for the owner, each with its full contribution list (few per owner).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SavingsPotDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SavingsPotDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SavingsPotDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "SavingsPots"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SavingsPotInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/SavingsPotInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/SavingsPotInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/savingspots/{id}": {
      "get": {
        "tags": [
          "SavingsPots"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "SavingsPots"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SavingsPotInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/SavingsPotInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/SavingsPotInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "SavingsPots"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/savingspots/{id}/contributions": {
      "post": {
        "tags": [
          "SavingsPots"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContributionInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/ContributionInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/ContributionInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/savingspots/{id}/contributions/{contributionId}": {
      "delete": {
        "tags": [
          "SavingsPots"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "contributionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavingsPotDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/summary/month": {
      "get": {
        "tags": [
          "Summary"
        ],
        "summary": "Totals + per-category breakdown for one month. Expenses combine daily\r\nentries (transactions) with fixed/recurring lines; income comes from\r\nincome lines plus any income transactions.",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MonthSummaryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonthSummaryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonthSummaryDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/summary/year": {
      "get": {
        "tags": [
          "Summary"
        ],
        "summary": "The CashFlow dashboard: per-month income, daily + fixed expenses, savings,\r\nand the cumulative running balance (\"Overall\") across the year.\r\nThe running balance is real cash: it starts from the accounts' opening balances plus the net\r\nof everything before this year, so it lines up with the account balances instead of restarting\r\nat zero each January. Note it still counts Pending entries and fixed cash-flow lines, which\r\nthe account balances do not — see the field notes on ZenCashFlow.Api.Dtos.MonthTotalDto.",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/YearSummaryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/YearSummaryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/YearSummaryDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/summary/categories": {
      "get": {
        "tags": [
          "Summary"
        ],
        "summary": "Per-category spend by month + yearly total (mirrors the Categories sheet).",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryYearSummaryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryYearSummaryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryYearSummaryDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/summary/categories-range": {
      "get": {
        "tags": [
          "Summary"
        ],
        "summary": "Spending per category between two dates (inclusive): the same sum as M:ZenCashFlow.Api.Controllers.SummaryController.Categories(System.Int32)\r\n(every expense entry, by its date), for any period - a quarter, the last 30 days, two weeks.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryRangeSummaryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryRangeSummaryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryRangeSummaryDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/summary/years": {
      "get": {
        "tags": [
          "Summary"
        ],
        "summary": "Distinct years that have data, so the client can populate a year picker.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "format": "int32"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "format": "int32"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "format": "int32"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/transactions": {
      "get": {
        "tags": [
          "Transactions"
        ],
        "summary": "Lists transactions, optionally filtered to a single month tab via\r\n?year=2026&month=6. Without filters, returns everything for the user.",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TransactionDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TransactionDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TransactionDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Transactions"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransactionInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/TransactionInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/TransactionInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/transactions/search": {
      "get": {
        "tags": [
          "Transactions"
        ],
        "summary": "Search entries over any dates: text in the note or the category name, plus optional category,\r\ntype, account and amount range. Returns the newest matches (up to limit) and\r\ntotals over ALL matches, so \"how much did I spend at Lidl this year?\" is one call.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "categoryId",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Whether a transaction is money going out (Expense) or coming in (Income).\r\nA cashflow is the running balance of incomes minus expenses.",
            "schema": {
              "$ref": "#/components/schemas/TransactionType"
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "minAmount",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "maxAmount",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/EntrySearchResultDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntrySearchResultDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntrySearchResultDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/transactions/{id}": {
      "get": {
        "tags": [
          "Transactions"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Transactions"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransactionInputDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/TransactionInputDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/TransactionInputDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Transactions"
        ],
        "summary": "Move an entry to the trash. It disappears everywhere at once (totals, balances, budgets) but\r\ncan be restored for ZenCashFlow.Api.Controllers.TransactionsController.TrashDays days - an agent's mistake, or a mis-tap, is never\r\nfinal. The background sweep purges it after that.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/transactions/trash": {
      "get": {
        "tags": [
          "Transactions"
        ],
        "summary": "Recently deleted entries, newest deletion first.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TrashedTransactionDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TrashedTransactionDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TrashedTransactionDto"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/transactions/{id}/restore": {
      "post": {
        "tags": [
          "Transactions"
        ],
        "summary": "Bring an entry back from the trash, exactly as it was.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/transactions/trash/{id}": {
      "delete": {
        "tags": [
          "Transactions"
        ],
        "summary": "Delete a trashed entry for good, without waiting for the purge.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AccountDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "nullable": true
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "currencyPosition": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string",
            "nullable": true
          },
          "numberFormat": {
            "type": "string",
            "nullable": true
          },
          "navOrder": {
            "type": "string",
            "nullable": true
          },
          "emailNotifications": {
            "type": "boolean"
          },
          "pushNotifications": {
            "type": "boolean"
          },
          "subscriptionPlan": {
            "type": "string",
            "nullable": true
          },
          "subscriptionStart": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "subscriptionEnd": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "hasPaidSubscription": {
            "type": "boolean"
          },
          "emailConfirmed": {
            "type": "boolean"
          },
          "hasPassword": {
            "type": "boolean"
          },
          "foreverFree": {
            "type": "boolean"
          },
          "planLapsed": {
            "type": "boolean"
          },
          "billingCadenceMonths": {
            "type": "integer",
            "format": "int32"
          },
          "previousPlan": {
            "type": "string",
            "nullable": true
          },
          "selectedPlan": {
            "type": "string",
            "nullable": true
          },
          "dailyLogReminder": {
            "type": "boolean"
          },
          "dailyLogReminderTime": {
            "type": "string",
            "nullable": true
          },
          "timeZoneId": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "AccountTransferDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "fromAccountId": {
            "type": "integer",
            "format": "int32"
          },
          "toAccountId": {
            "type": "integer",
            "format": "int32"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A transfer of money between two accounts."
      },
      "AccountTransferInputDto": {
        "type": "object",
        "properties": {
          "fromAccountId": {
            "type": "integer",
            "format": "int32"
          },
          "toAccountId": {
            "type": "integer",
            "format": "int32"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "note": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Create payload for a transfer."
      },
      "BudgetRowDto": {
        "type": "object",
        "properties": {
          "categoryId": {
            "type": "integer",
            "format": "int32"
          },
          "amounts": {
            "type": "array",
            "items": {
              "type": "number",
              "format": "double"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One category's budgeted amounts for a year (12 entries, index 0 = January)."
      },
      "CashflowLineDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "kind": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "amounts": {
            "type": "array",
            "items": {
              "type": "number",
              "format": "double"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CashflowLineInputDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "kind": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "CategoryBreakdownDto": {
        "type": "object",
        "properties": {
          "categoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "categoryName": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "total": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "CategoryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "color": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CategoryInputDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "color": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CategoryRangeRowDto": {
        "type": "object",
        "properties": {
          "categoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "entries": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "CategoryRangeSummaryDto": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CategoryRangeRowDto"
            },
            "nullable": true
          },
          "total": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "Spending per category over a date range (see SummaryController.CategoriesRange)."
      },
      "CategoryYearRowDto": {
        "type": "object",
        "properties": {
          "categoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "categoryName": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "months": {
            "type": "array",
            "items": {
              "type": "number",
              "format": "double"
            },
            "nullable": true
          },
          "total": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "CategoryYearSummaryDto": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CategoryYearRowDto"
            },
            "nullable": true
          },
          "monthTotals": {
            "type": "array",
            "items": {
              "type": "number",
              "format": "double"
            },
            "nullable": true
          },
          "grandTotal": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "ContributionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "note": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A single deposit (positive) or withdrawal (negative) within a pot."
      },
      "ContributionInputDto": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "note": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Create payload for a contribution."
      },
      "CopyLinesDto": {
        "type": "object",
        "properties": {
          "fromYear": {
            "type": "integer",
            "format": "int32"
          },
          "toYear": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "Clone line definitions (names/kinds/order) from one year to another."
      },
      "DayOfWeek": {
        "enum": [
          0,
          1,
          2,
          3,
          4,
          5,
          6
        ],
        "type": "integer",
        "format": "int32"
      },
      "DebtDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "initialBalance": {
            "type": "number",
            "format": "double"
          },
          "targetDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "paid": {
            "type": "number",
            "format": "double"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DebtPaymentDto"
            },
            "nullable": true
          },
          "showInOverview": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "A debt as returned to the client. ZenCashFlow.Api.Dtos.DebtDto.Paid is the summed payments\r\n(server-computed); the client derives Remaining = InitialBalance − Paid.\r\nZenCashFlow.Api.Dtos.DebtDto.Payments is populated on the single-debt endpoint and left empty on\r\nthe list endpoint."
      },
      "DebtInputDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "initialBalance": {
            "type": "number",
            "format": "double"
          },
          "targetDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "showInOverview": {
            "type": "boolean",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Create/update payload for a debt. Name + a positive initial balance required."
      },
      "DebtPaymentDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "origin": {
            "type": "string",
            "nullable": true
          },
          "pending": {
            "type": "boolean"
          },
          "transactionId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One row in a debt's merged ledger. ZenCashFlow.Api.Dtos.DebtPaymentDto.Origin is \"manual\" (tracking-only payment),\r\n\"entry\" (a linked daily expense), or \"recurring\" (a linked entry from a recurring rule).\r\nFor linked rows ZenCashFlow.Api.Dtos.DebtPaymentDto.TransactionId is set and ZenCashFlow.Api.Dtos.DebtPaymentDto.Pending reflects whether\r\nit's posted yet (pending rows don't reduce the remaining); manual rows are always posted and\r\ncarry ZenCashFlow.Api.Dtos.DebtPaymentDto.Id (the DebtPayment id) so they can be deleted here."
      },
      "DebtPaymentInputDto": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "asExpense": {
            "type": "boolean"
          },
          "categoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Create payload for a payment. When ZenCashFlow.Api.Dtos.DebtPaymentInputDto.AsExpense is true the payment is recorded as a\r\nreal daily expense (linked to the debt, hits the balance), optionally in ZenCashFlow.Api.Dtos.DebtPaymentInputDto.CategoryId\r\n(defaults to the owner's \"Debt\" category). When false it's a tracking-only entry."
      },
      "EntrySearchResultDto": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "format": "int32"
          },
          "expenseTotal": {
            "type": "number",
            "format": "double"
          },
          "incomeTotal": {
            "type": "number",
            "format": "double"
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TransactionDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Entry search: the newest matches plus totals over every match."
      },
      "FillAmountDto": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "format": "double"
          },
          "months": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Set the same amount on one line for the given months (1-12); null/empty = all 12."
      },
      "FillBudgetDto": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "format": "double"
          },
          "months": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Set the same budget on a category for the given months (1-12); null/empty = all 12."
      },
      "MoneyAccountDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "openingBalance": {
            "type": "number",
            "format": "double"
          },
          "balance": {
            "type": "number",
            "format": "double"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "isDefault": {
            "type": "boolean"
          },
          "showInOverview": {
            "type": "boolean"
          },
          "expenseTracking": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "A money account as returned to the client. ZenCashFlow.Api.Dtos.MoneyAccountDto.Balance is the live balance\r\n(opening + completed income − completed expense + transfers-in − transfers-out),\r\ncomputed on the server. Named \"MoneyAccountDto\" to avoid a clash with the profile\r\n`AccountDto` in AuthDtos.cs."
      },
      "MoneyAccountInputDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "openingBalance": {
            "type": "number",
            "format": "double"
          },
          "showInOverview": {
            "type": "boolean",
            "nullable": true
          },
          "expenseTracking": {
            "type": "boolean",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Create/update payload for a money account. Name required; the rest optional."
      },
      "MonthSummaryDto": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "totalIncome": {
            "type": "number",
            "format": "double"
          },
          "dailyIncome": {
            "type": "number",
            "format": "double"
          },
          "fixedIncome": {
            "type": "number",
            "format": "double"
          },
          "dailyExpense": {
            "type": "number",
            "format": "double"
          },
          "fixedExpense": {
            "type": "number",
            "format": "double"
          },
          "totalExpense": {
            "type": "number",
            "format": "double"
          },
          "net": {
            "type": "number",
            "format": "double"
          },
          "expenseByCategory": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CategoryBreakdownDto"
            },
            "nullable": true
          },
          "incomeByCategory": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CategoryBreakdownDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "MonthTotalDto": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "totalIncome": {
            "type": "number",
            "format": "double"
          },
          "dailyIncome": {
            "type": "number",
            "format": "double"
          },
          "dailyExpense": {
            "type": "number",
            "format": "double"
          },
          "fixedExpense": {
            "type": "number",
            "format": "double"
          },
          "totalExpense": {
            "type": "number",
            "format": "double"
          },
          "savings": {
            "type": "number",
            "format": "double"
          },
          "runningBalance": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "One month column of the CashFlow dashboard. Expenses are split into\r\ndaily (from the month tabs / transactions) and fixed (from cashflow lines)."
      },
      "MonthlyMode": {
        "enum": [
          0,
          1,
          2
        ],
        "type": "integer",
        "description": "For monthly rules: whether it lands on a fixed day-of-month or an ordinal weekday.",
        "format": "int32"
      },
      "PastOccurrencesDto": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "format": "int32"
          },
          "dates": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date-time"
            },
            "nullable": true
          },
          "capped": {
            "type": "boolean"
          },
          "total": {
            "type": "integer",
            "format": "int32"
          },
          "bookFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The occurrences a rule with a past start date would skip (between its start date and yesterday),\r\nso the form can name them before the user opts to add them. ZenCashFlow.Api.Dtos.PastOccurrencesDto.Total is how many there\r\nare; ZenCashFlow.Api.Dtos.PastOccurrencesDto.Dates holds the first few of them. ZenCashFlow.Api.Dtos.PastOccurrencesDto.Count is how many backfill would\r\nbook: all of them, or - when ZenCashFlow.Api.Dtos.PastOccurrencesDto.Capped - only the most recent ones, starting at\r\nZenCashFlow.Api.Dtos.PastOccurrencesDto.BookFrom."
      },
      "PlanUsage": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "nullable": true
          },
          "dailyEntries": {
            "$ref": "#/components/schemas/UsageItem"
          },
          "recurringIncome": {
            "$ref": "#/components/schemas/UsageItem"
          },
          "recurringExpenses": {
            "$ref": "#/components/schemas/UsageItem"
          },
          "categories": {
            "$ref": "#/components/schemas/UsageItem"
          },
          "savingsPots": {
            "$ref": "#/components/schemas/UsageItem"
          },
          "debts": {
            "$ref": "#/components/schemas/UsageItem"
          },
          "accounts": {
            "$ref": "#/components/schemas/UsageItem"
          }
        },
        "additionalProperties": false,
        "description": "A user's current usage against every plan limit."
      },
      "PostOccurrenceDto": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "Request to post (\"mark as paid\") a rule's next occurrence, dated ZenCashFlow.Api.Dtos.PostOccurrenceDto.Date."
      },
      "RecurrenceEnd": {
        "enum": [
          0,
          1,
          2
        ],
        "type": "integer",
        "description": "When a recurring rule stops producing entries.",
        "format": "int32"
      },
      "RecurrenceFrequency": {
        "enum": [
          0,
          1,
          2,
          3
        ],
        "type": "integer",
        "description": "How often a ZenCashFlow.Api.Models.RecurringRule repeats.",
        "format": "int32"
      },
      "RecurringOccurrenceDto": {
        "type": "object",
        "properties": {
          "day": {
            "type": "integer",
            "format": "int32"
          },
          "ruleId": {
            "type": "integer",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "kind": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "isNext": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "A single future (not-yet-posted) recurring occurrence within a month, for the Daily\r\nspending calendar's \"recurring due\" markers. ZenCashFlow.Api.Dtos.RecurringOccurrenceDto.Day is the day-of-month."
      },
      "RecurringRuleDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "type": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "categoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "frequency": {
            "$ref": "#/components/schemas/RecurrenceFrequency"
          },
          "interval": {
            "type": "integer",
            "format": "int32"
          },
          "weekDays": {
            "type": "integer",
            "format": "int32"
          },
          "timeOfDay": {
            "type": "string",
            "nullable": true
          },
          "monthlyMode": {
            "$ref": "#/components/schemas/MonthlyMode"
          },
          "dayOfMonth": {
            "type": "integer",
            "format": "int32"
          },
          "ordinal": {
            "$ref": "#/components/schemas/WeekOrdinal"
          },
          "ordinalWeekday": {
            "$ref": "#/components/schemas/DayOfWeek"
          },
          "monthOfYear": {
            "type": "integer",
            "format": "int32"
          },
          "startDate": {
            "type": "string",
            "format": "date-time"
          },
          "endMode": {
            "$ref": "#/components/schemas/RecurrenceEnd"
          },
          "endDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "maxOccurrences": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "autoPostCompleted": {
            "type": "boolean"
          },
          "isPaused": {
            "type": "boolean"
          },
          "reminderDaysBefore": {
            "type": "integer",
            "format": "int32"
          },
          "nextRunDate": {
            "type": "string",
            "format": "date-time"
          },
          "lastGeneratedDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "occurrencesGenerated": {
            "type": "integer",
            "format": "int32"
          },
          "debtId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "timeZoneId": {
            "type": "string",
            "nullable": true
          },
          "accountId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A recurring rule as returned to the client (enums are sent as numbers)."
      },
      "RecurringRuleInputDto": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "type": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "categoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "frequency": {
            "$ref": "#/components/schemas/RecurrenceFrequency"
          },
          "interval": {
            "type": "integer",
            "format": "int32"
          },
          "weekDays": {
            "type": "integer",
            "format": "int32"
          },
          "timeOfDay": {
            "type": "string",
            "nullable": true
          },
          "monthlyMode": {
            "$ref": "#/components/schemas/MonthlyMode"
          },
          "dayOfMonth": {
            "type": "integer",
            "format": "int32"
          },
          "ordinal": {
            "$ref": "#/components/schemas/WeekOrdinal"
          },
          "ordinalWeekday": {
            "$ref": "#/components/schemas/DayOfWeek"
          },
          "monthOfYear": {
            "type": "integer",
            "format": "int32"
          },
          "startDate": {
            "type": "string",
            "format": "date-time"
          },
          "endMode": {
            "$ref": "#/components/schemas/RecurrenceEnd"
          },
          "endDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "maxOccurrences": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "autoPostCompleted": {
            "type": "boolean"
          },
          "reminderDaysBefore": {
            "type": "integer",
            "format": "int32"
          },
          "debtId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "timeZoneId": {
            "type": "string",
            "nullable": true
          },
          "accountId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "backfillPast": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "Create/update payload for a recurring rule."
      },
      "ReorderDto": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "ids": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "New ordering for a set of lines within one year — sortOrder = list index."
      },
      "SavingsPotDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "targetAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "targetDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "balance": {
            "type": "number",
            "format": "double"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "contributions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContributionDto"
            },
            "nullable": true
          },
          "showInOverview": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "A savings pot as returned to the client. ZenCashFlow.Api.Dtos.SavingsPotDto.Balance is the summed\r\ncontributions (server-computed). ZenCashFlow.Api.Dtos.SavingsPotDto.Contributions is populated on the\r\nsingle-pot endpoint and left empty on the list endpoint."
      },
      "SavingsPotInputDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "targetAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "targetDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "showInOverview": {
            "type": "boolean",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Create/update payload for a pot. Name required; the rest optional."
      },
      "SetAmountDto": {
        "type": "object",
        "properties": {
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "amount": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "SetBudgetDto": {
        "type": "object",
        "properties": {
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "amount": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "TransactionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "type": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "categoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "categoryName": {
            "type": "string",
            "nullable": true
          },
          "categoryColor": {
            "type": "string",
            "nullable": true
          },
          "createdByUserId": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/TransactionStatus"
          },
          "debtId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "recurringRuleId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "accountId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "timeOfDay": {
            "type": "string",
            "nullable": true
          },
          "reminderMinutes": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "nullable": true
          },
          "remindersSent": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "nullable": true
          },
          "createdVia": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "TransactionInputDto": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "type": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "categoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/TransactionStatus"
          },
          "debtId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "accountId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "timeOfDay": {
            "type": "string",
            "nullable": true
          },
          "reminderMinutes": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "nullable": true
          },
          "timeZoneId": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "TransactionStatus": {
        "enum": [
          0,
          1
        ],
        "type": "integer",
        "description": "Whether a daily entry is settled (Completed) or still awaiting (Pending) — e.g.\r\na payment that's been scheduled but hasn't cleared yet. New entries default to\r\nCompleted; the user can flag one Pending from the ledger's status column.",
        "format": "int32"
      },
      "TransactionType": {
        "enum": [
          0,
          1
        ],
        "type": "integer",
        "description": "Whether a transaction is money going out (Expense) or coming in (Income).\r\nA cashflow is the running balance of incomes minus expenses.",
        "format": "int32"
      },
      "TrashedTransactionDto": {
        "type": "object",
        "properties": {
          "entry": {
            "$ref": "#/components/schemas/TransactionDto"
          },
          "deletedAt": {
            "type": "string",
            "format": "date-time"
          },
          "purgeAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "An entry in the trash: restorable until ZenCashFlow.Api.Dtos.TrashedTransactionDto.PurgeAt, then deleted for good."
      },
      "UsageItem": {
        "type": "object",
        "properties": {
          "used": {
            "type": "integer",
            "format": "int32"
          },
          "limit": {
            "type": "integer",
            "format": "int32"
          },
          "unlimited": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "One usage figure: how many of an allowance are in use. When ZenCashFlow.Api.Services.UsageItem.Unlimited\r\n            is true the ZenCashFlow.Api.Services.UsageItem.Limit is not enforced and the meter shows \"unlimited\"."
      },
      "VirtualCashflowLineDto": {
        "type": "object",
        "properties": {
          "ruleId": {
            "type": "integer",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "kind": {
            "$ref": "#/components/schemas/TransactionType"
          },
          "amounts": {
            "type": "array",
            "items": {
              "type": "number",
              "format": "double"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A recurring rule projected across a year for the Cashflow view — 12 monthly sums. Only the\r\nrule's future (not-yet-generated) occurrences are projected; these are folded into the Cashflow\r\ntotals. ZenCashFlow.Api.Dtos.VirtualCashflowLineDto.RuleId identifies the source rule so the row can be drag-reordered."
      },
      "WeekOrdinal": {
        "enum": [
          1,
          2,
          3,
          4,
          5
        ],
        "type": "integer",
        "description": "Which occurrence of a weekday within a month (for ZenCashFlow.Api.Models.MonthlyMode.OrdinalWeekday).",
        "format": "int32"
      },
      "YearSummaryDto": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "totalIncome": {
            "type": "number",
            "format": "double"
          },
          "totalExpense": {
            "type": "number",
            "format": "double"
          },
          "net": {
            "type": "number",
            "format": "double"
          },
          "months": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MonthTotalDto"
            },
            "nullable": true
          },
          "lockedFromMonth": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A year of the CashFlow dashboard.\r\n\r\nZenCashFlow.Api.Dtos.YearSummaryDto.LockedFromMonth is the first month the current plan may not see (null when\r\nthe whole year is visible). Those months arrive with every figure zeroed — the server does\r\nnot send data behind the paywall — so the client must render them as locked rather than as\r\nmonths where nothing happened. It is only ever a FUTURE month: nothing recorded is hidden."
      }
    },
    "securitySchemes": {
      "Bearer": {
        "type": "http",
        "description": "A personal API key (ocl_…) from Settings → AI & API, or an OAuth access token (oca_…). Send it as: Authorization: Bearer <key>. Read-only keys can call GET endpoints only.",
        "scheme": "bearer",
        "bearerFormat": "Oricalo API key"
      }
    }
  },
  "security": [
    {
      "Bearer": [ ]
    }
  ]
}