{
  "openapi": "3.1.0",
  "info": {
    "title": "Right Money Agent API",
    "description": "Integrate financial wellness into any AI agent or app. The Right Money Agent API lets you onboard users into the three-bucket system (Bills, Lifestyle, Freedom) programmatically.",
    "version": "1.0.0",
    "contact": {
      "name": "Right Money",
      "url": "https://rightmoney.com/developers",
      "email": "steve@larsenfin.com"
    }
  },
  "servers": [
    {
      "url": "https://app.rightmoney.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "agentKey": []
    }
  ],
  "paths": {
    "/api/onboarding/agent/status": {
      "get": {
        "operationId": "getOnboardingStatus",
        "summary": "Check onboarding status",
        "description": "Returns the current onboarding step for a user. Use this to determine what action to take next.",
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The user's unique identifier"
          }
        ],
        "responses": {
          "200": {
            "description": "Current onboarding status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                },
                "examples": {
                  "needsConnect": {
                    "summary": "User needs to connect bank",
                    "value": {
                      "onboarded": false,
                      "step": "connect"
                    }
                  },
                  "analyzing": {
                    "summary": "Transactions being analyzed",
                    "value": {
                      "onboarded": false,
                      "step": "analyzing"
                    }
                  },
                  "needsConfirm": {
                    "summary": "User needs to confirm classifications",
                    "value": {
                      "onboarded": false,
                      "step": "confirm",
                      "unconfirmed_count": 5
                    }
                  },
                  "done": {
                    "summary": "Onboarding complete",
                    "value": {
                      "onboarded": true,
                      "step": "done"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/api/onboarding/classify": {
      "post": {
        "operationId": "classifyTransactions",
        "summary": "Classify transactions",
        "description": "Triggers AI classification of the user's bank transactions into buckets (bills, lifestyle, income, ignore). This is idempotent — calling it again after classification returns immediately.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserIdBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Classification result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClassifyResponse"
                },
                "examples": {
                  "alreadyClassified": {
                    "summary": "Already classified (idempotent)",
                    "value": {
                      "success": true,
                      "message": "Already classified",
                      "classified": 0,
                      "skipped": true
                    }
                  },
                  "freshClassification": {
                    "summary": "Fresh classification",
                    "value": {
                      "success": true,
                      "classified": 47,
                      "rulesCreated": 12,
                      "totalTransactions": 210,
                      "merchantPatterns": 47
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/api/onboarding/agent/start": {
      "post": {
        "operationId": "startOnboarding",
        "summary": "Get financial summary",
        "description": "Returns the user's financial summary with transactions classified into the three-bucket system. If the user hasn't connected their bank or classified transactions yet, returns the appropriate next step.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserIdBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Onboarding state or financial summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StartResponse"
                },
                "examples": {
                  "needsPlaid": {
                    "summary": "User needs to connect bank",
                    "value": {
                      "status": "needs_plaid",
                      "message": "User has no bank connections. Direct user to connect their bank.",
                      "link_url": "https://app.rightmoney.com/onboarding"
                    }
                  },
                  "needsClassification": {
                    "summary": "Transactions need classification",
                    "value": {
                      "status": "needs_classification",
                      "message": "Bank connected but transactions not yet classified. Call POST /api/onboarding/classify with { \"userId\": \"...\" } first."
                    }
                  },
                  "ready": {
                    "summary": "Summary ready for review",
                    "value": {
                      "status": "ready",
                      "summary": {
                        "income": {
                          "monthly_total": 5000,
                          "sources": [
                            {
                              "pattern": "ACME CORP PAYROLL",
                              "monthly_amount": 5000,
                              "frequency": "biweekly",
                              "is_recurring": true
                            }
                          ]
                        },
                        "bills": {
                          "monthly_total": 2100,
                          "percentage": 42,
                          "items": [
                            {
                              "pattern": "ZILLOW MORTGAGE",
                              "monthly_amount": 1500,
                              "frequency": "monthly",
                              "is_recurring": true,
                              "bill_group": "housing"
                            },
                            {
                              "pattern": "GEICO AUTO",
                              "monthly_amount": 150,
                              "frequency": "monthly",
                              "is_recurring": true,
                              "bill_group": "insurance"
                            }
                          ]
                        },
                        "lifestyle": {
                          "monthly_total": 1400,
                          "percentage": 28
                        },
                        "freedom": {
                          "monthly_total": 1500,
                          "percentage": 30
                        },
                        "months_of_freedom": 3.2,
                        "total_balance": 9600,
                        "top_recurring": [
                          {
                            "pattern": "ZILLOW MORTGAGE",
                            "bucket": "bills",
                            "monthly_amount": 1500,
                            "frequency": "monthly",
                            "bill_group": "housing"
                          },
                          {
                            "pattern": "SPOTIFY",
                            "bucket": "lifestyle",
                            "monthly_amount": 10.99,
                            "frequency": "monthly",
                            "bill_group": null
                          }
                        ],
                        "unconfirmed_count": 47,
                        "classification_confidence": 0.87
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/api/onboarding/agent/transactions": {
      "get": {
        "operationId": "getAgentTransactions",
        "summary": "Get raw merchant patterns for agent-side classification",
        "description": "Returns unclassified transactions grouped by merchant pattern. The calling AI agent classifies each merchant into a bucket (bills, lifestyle, income, ignore) and submits results via POST /api/onboarding/agent/confirm with a classifications array. Falls back to backend classification recommended when >100 merchant patterns.",
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The user's unique identifier"
          }
        ],
        "responses": {
          "200": {
            "description": "Merchant patterns for classification",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionsResponse"
                },
                "example": {
                  "userId": "abc-123",
                  "transactionCount": 47,
                  "merchantCount": 12,
                  "dateRange": { "start": "2025-12-01", "end": "2026-03-01" },
                  "merchants": [
                    {
                      "pattern": "AMAZON",
                      "displayName": "Amazon",
                      "totalAmount": 234.50,
                      "transactionCount": 8,
                      "avgAmount": 29.31,
                      "direction": "outflow",
                      "sampleDates": ["2026-01-15", "2026-02-01", "2026-02-28"],
                      "suggestedBucket": null
                    }
                  ],
                  "buckets": ["bills", "lifestyle", "income", "ignore"],
                  "fallbackRecommended": false,
                  "instructions": "Classify each merchant into exactly one bucket..."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/api/onboarding/agent/review": {
      "get": {
        "operationId": "reviewClassifications",
        "summary": "Review data quality and get improvement suggestions",
        "description": "Returns a prioritized list of data quality issues: low-confidence classifications, likely misclassifications (streaming in bills), ambiguous transfers (Venmo/Zelle), missing income, and bill frequency issues. Walk through issues one at a time with the user.",
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The user's unique identifier"
          }
        ],
        "responses": {
          "200": {
            "description": "Review results with prioritized issues",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReviewResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/api/onboarding/agent/confirm": {
      "post": {
        "operationId": "confirmClassifications",
        "summary": "Confirm or adjust classifications",
        "description": "Confirms the AI-generated transaction classifications. Optionally pass changes to reclassify specific merchants before confirming. Omit changes to confirm all as-is.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfirmBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Confirmation result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfirmResponse"
                },
                "example": {
                  "success": true,
                  "confirmed": 47,
                  "rules_created": 35
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/api/user/state": {
      "get": {
        "operationId": "getUserState",
        "summary": "Get complete user state",
        "description": "Returns the canonical, complete picture of a user's Right Money system — financial breakdown, health score, classification status, account info, and next action. Replaces the need for multiple API calls.",
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The user's unique identifier"
          }
        ],
        "responses": {
          "200": {
            "description": "Complete user state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserStateResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/api/user/snapshot-token": {
      "get": {
        "operationId": "getSnapshotToken",
        "summary": "Generate a snapshot/report token",
        "description": "Returns a short-lived HMAC token for embedding snapshot, trends, and report URLs in emails, chat, or shareable links. No login required to view tokenized URLs.",
        "parameters": [
          { "name": "userId", "in": "query", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "ttl", "in": "query", "required": false, "schema": { "type": "string", "enum": ["email", "share"] }, "description": "Token lifetime: 'email' (24h, default) or 'share' (7 days)" }
        ],
        "responses": {
          "200": {
            "description": "Token and pre-built URLs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": { "type": "string" },
                    "expiresInHours": { "type": "integer" },
                    "urls": {
                      "type": "object",
                      "properties": {
                        "snapshot": { "type": "string", "format": "uri" },
                        "trends": { "type": "string", "format": "uri" },
                        "report": { "type": "string", "format": "uri" }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/user/snapshot.png": {
      "get": {
        "operationId": "getSnapshotPng",
        "summary": "Get branded snapshot card image",
        "description": "Returns a branded PNG snapshot card showing the three-bucket breakdown, Months of Freedom, and health score. Auth via token query param (no login required).",
        "parameters": [
          { "name": "token", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Snapshot token from /api/user/snapshot-token" },
          { "name": "userId", "in": "query", "required": false, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "PNG image", "content": { "image/png": { "schema": { "type": "string", "format": "binary" } } } },
          "401": { "description": "Invalid or expired token" }
        }
      }
    },
    "/api/user/trends.png": {
      "get": {
        "operationId": "getTrendsPng",
        "summary": "Get trends chart image",
        "description": "Returns a branded PNG trends chart showing Months of Freedom over time. Paid users get full data; free users see upgrade teaser.",
        "parameters": [
          { "name": "token", "in": "query", "required": false, "schema": { "type": "string" } },
          { "name": "userId", "in": "query", "required": false, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "PNG image", "content": { "image/png": { "schema": { "type": "string", "format": "binary" } } } },
          "401": { "description": "Invalid or expired token" }
        }
      }
    },
    "/api/user/report.html": {
      "get": {
        "operationId": "getReportHtml",
        "summary": "Get full HTML financial report",
        "description": "Returns a styled, mobile-responsive HTML report with complete financial breakdown — the Family Meeting report. Includes all merchants, bill groups, MoF trend, and health score.",
        "parameters": [
          { "name": "token", "in": "query", "required": false, "schema": { "type": "string" } },
          { "name": "userId", "in": "query", "required": false, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "HTML report", "content": { "text/html": { "schema": { "type": "string" } } } },
          "401": { "description": "Invalid or expired token" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "agentKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-RM-Agent-Key",
        "description": "API key for agent access. Issued on request during beta."
      }
    },
    "schemas": {
      "UserIdBody": {
        "type": "object",
        "required": ["userId"],
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid",
            "description": "The user's unique identifier"
          }
        }
      },
      "StatusResponse": {
        "type": "object",
        "required": ["onboarded", "step"],
        "properties": {
          "onboarded": {
            "type": "boolean",
            "description": "Whether the user has completed onboarding"
          },
          "step": {
            "type": "string",
            "enum": ["connect", "analyzing", "confirm", "done"],
            "description": "Current onboarding step"
          },
          "unconfirmed_count": {
            "type": "integer",
            "description": "Number of unconfirmed classifications. Only present when step is \"confirm\"."
          }
        }
      },
      "ClassifyResponse": {
        "type": "object",
        "required": ["success"],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "message": {
            "type": "string"
          },
          "classified": {
            "type": "integer",
            "description": "Number of merchant patterns classified"
          },
          "skipped": {
            "type": "boolean",
            "description": "True if classifications already existed"
          },
          "rulesCreated": {
            "type": "integer",
            "description": "Number of high-confidence merchant rules created"
          },
          "totalTransactions": {
            "type": "integer",
            "description": "Total transactions analyzed"
          },
          "merchantPatterns": {
            "type": "integer",
            "description": "Unique merchant patterns found"
          }
        }
      },
      "StartResponse": {
        "type": "object",
        "required": ["status"],
        "properties": {
          "status": {
            "type": "string",
            "enum": ["needs_plaid", "needs_classification", "ready", "confirmed"],
            "description": "Current state of the onboarding flow"
          },
          "message": {
            "type": "string"
          },
          "link_url": {
            "type": "string",
            "format": "uri",
            "description": "URL to direct the user to (when status is needs_plaid)"
          },
          "summary": {
            "$ref": "#/components/schemas/FinancialSummary"
          }
        }
      },
      "FinancialSummary": {
        "type": "object",
        "description": "Complete financial breakdown in the three-bucket system",
        "properties": {
          "income": {
            "type": "object",
            "properties": {
              "monthly_total": {
                "type": "number"
              },
              "sources": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "pattern": { "type": "string" },
                    "monthly_amount": { "type": "number" },
                    "frequency": { "type": "string", "enum": ["monthly", "biweekly", "weekly", "quarterly", "annual", "one-time"] },
                    "is_recurring": { "type": "boolean" }
                  }
                }
              }
            }
          },
          "bills": {
            "type": "object",
            "properties": {
              "monthly_total": { "type": "number" },
              "percentage": { "type": "number" },
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "pattern": { "type": "string" },
                    "monthly_amount": { "type": "number" },
                    "frequency": { "type": "string" },
                    "is_recurring": { "type": "boolean" },
                    "bill_group": {
                      "type": ["string", "null"],
                      "enum": ["housing", "utilities", "insurance", "subscriptions", "transportation", "debt", "medical", "childcare", null]
                    }
                  }
                }
              }
            }
          },
          "lifestyle": {
            "type": "object",
            "properties": {
              "monthly_total": { "type": "number" },
              "percentage": { "type": "number" }
            }
          },
          "freedom": {
            "type": "object",
            "properties": {
              "monthly_total": { "type": "number" },
              "percentage": { "type": "number" }
            }
          },
          "months_of_freedom": {
            "type": "number",
            "description": "Total balance divided by monthly freedom amount"
          },
          "total_balance": {
            "type": "number"
          },
          "top_recurring": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "pattern": { "type": "string" },
                "bucket": { "type": "string", "enum": ["bills", "lifestyle", "income", "ignore"] },
                "monthly_amount": { "type": "number" },
                "frequency": { "type": "string" },
                "bill_group": { "type": ["string", "null"] }
              }
            }
          },
          "unconfirmed_count": {
            "type": "integer"
          },
          "classification_confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        }
      },
      "TransactionsResponse": {
        "type": "object",
        "required": ["userId", "transactionCount", "merchantCount", "merchants", "buckets"],
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "transactionCount": {
            "type": "integer",
            "description": "Total number of transactions in the date range"
          },
          "merchantCount": {
            "type": "integer",
            "description": "Number of unique merchant patterns"
          },
          "dateRange": {
            "type": ["object", "null"],
            "properties": {
              "start": { "type": "string", "format": "date" },
              "end": { "type": "string", "format": "date" }
            }
          },
          "merchants": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "pattern": { "type": "string", "description": "Normalized merchant pattern" },
                "displayName": { "type": "string" },
                "totalAmount": { "type": "number" },
                "transactionCount": { "type": "integer" },
                "avgAmount": { "type": "number" },
                "direction": { "type": "string", "enum": ["inflow", "outflow"] },
                "sampleDates": { "type": "array", "items": { "type": "string", "format": "date" } },
                "suggestedBucket": { "type": ["string", "null"] }
              }
            }
          },
          "buckets": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Valid bucket values"
          },
          "fallbackRecommended": {
            "type": "boolean",
            "description": "True if >100 merchant patterns — recommend backend classification instead"
          },
          "instructions": {
            "type": "string",
            "description": "Classification guidance for the AI agent"
          }
        }
      },
      "ReviewResponse": {
        "type": "object",
        "required": ["userId", "score", "issueCount", "issues", "nextAction"],
        "properties": {
          "userId": { "type": "string", "format": "uuid" },
          "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Data quality score" },
          "issueCount": { "type": "integer" },
          "issues": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": { "type": "string", "enum": ["low_confidence", "likely_wrong", "missing_income", "ambiguous_transfer", "bill_frequency", "income_validation", "ignore_detection"] },
                "merchant": { "type": ["string", "null"] },
                "currentBucket": { "type": ["string", "null"] },
                "confidence": { "type": ["number", "null"] },
                "suggestedBucket": { "type": ["string", "null"] },
                "question": { "type": "string" },
                "options": { "type": "array", "items": { "type": "string" } },
                "priority": { "type": "integer" }
              }
            }
          },
          "nextAction": {
            "type": "object",
            "properties": {
              "type": { "type": "string", "enum": ["improve", "upgrade", "view"] },
              "label": { "type": "string" },
              "url": { "type": "string", "format": "uri" }
            }
          }
        }
      },
      "UserStateResponse": {
        "type": "object",
        "required": ["userId", "generatedAt", "financial", "health", "classification", "account", "next_action"],
        "properties": {
          "userId": { "type": "string", "format": "uuid" },
          "generatedAt": { "type": "string", "format": "date-time" },
          "financial": {
            "type": "object",
            "properties": {
              "income": {
                "type": "object",
                "properties": {
                  "monthly_total": { "type": "number" },
                  "sources": { "type": "array", "items": { "type": "object" } },
                  "confidence": { "type": "number" }
                }
              },
              "bills": {
                "type": "object",
                "properties": {
                  "monthly_total": { "type": "number" },
                  "percent_of_income": { "type": "integer" },
                  "groups": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "monthly_amount": { "type": "number" }, "items": { "type": "array", "items": { "type": "string" } } } } },
                  "confidence": { "type": "number" }
                }
              },
              "lifestyle": {
                "type": "object",
                "properties": {
                  "monthly_total": { "type": "number" },
                  "percent_of_income": { "type": "integer" },
                  "top_merchants": { "type": "array", "items": { "type": "string" } },
                  "confidence": { "type": "number" }
                }
              },
              "freedom": {
                "type": "object",
                "properties": {
                  "monthly_total": { "type": "number" },
                  "percent_of_income": { "type": "integer" },
                  "total_savings": { "type": "number" }
                }
              },
              "months_of_freedom": {
                "type": "object",
                "properties": {
                  "current": { "type": "number" },
                  "trend": { "type": "array", "items": { "type": "object", "properties": { "month": { "type": "string" }, "value": { "type": "number" } } } },
                  "target": { "type": ["number", "null"] }
                }
              }
            }
          },
          "health": {
            "type": "object",
            "properties": {
              "score": { "type": "integer", "minimum": 0, "maximum": 100 },
              "label": { "type": "string" },
              "breakdown": { "type": "object" },
              "what_would_improve": { "type": "array", "items": { "type": "object", "properties": { "action": { "type": "string" }, "score_gain": { "type": "integer" } } } }
            }
          },
          "classification": {
            "type": "object",
            "properties": {
              "overall_confidence": { "type": "number" },
              "unconfirmed_count": { "type": "integer" },
              "db_coverage": { "type": "number" },
              "flagged_count": { "type": "integer" },
              "last_run": { "type": ["string", "null"], "format": "date-time" }
            }
          },
          "account": {
            "type": "object",
            "properties": {
              "status": { "type": "string", "enum": ["free", "trialing", "paid", "expired"] },
              "source": { "type": "string" },
              "last_sync": { "type": ["string", "null"], "format": "date-time" },
              "data_age_days": { "type": ["integer", "null"] },
              "sync_enabled": { "type": "boolean" },
              "plaid_connections": { "type": "integer" }
            }
          },
          "next_action": {
            "type": "object",
            "properties": {
              "type": { "type": "string" },
              "label": { "type": "string" },
              "subtitle": { "type": "string" },
              "url": { "type": "string", "format": "uri" },
              "priority": { "type": "integer" }
            }
          },
          "snapshot_url": { "type": "string", "format": "uri" }
        }
      },
      "ConfirmBody": {
        "type": "object",
        "required": ["userId"],
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid",
            "description": "The user's unique identifier"
          },
          "classifications": {
            "type": "array",
            "description": "Full agent-side classifications. Use this when the agent classified merchants from GET /api/onboarding/agent/transactions.",
            "items": {
              "type": "object",
              "required": ["pattern", "bucket"],
              "properties": {
                "pattern": { "type": "string", "description": "Merchant pattern (exact match)" },
                "bucket": { "type": "string", "enum": ["bills", "lifestyle", "income", "ignore"] },
                "is_recurring": { "type": "boolean" },
                "estimated_frequency": { "type": "string", "enum": ["monthly", "biweekly", "weekly", "quarterly", "annual", "one-time"] },
                "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
                "bill_group": { "type": ["string", "null"], "enum": ["housing", "utilities", "insurance", "subscriptions", "transportation", "debt", "medical", "childcare", null] }
              }
            }
          },
          "changes": {
            "type": "array",
            "description": "Optional adjustments to existing backend classifications. Omit to confirm all as-is.",
            "items": {
              "type": "object",
              "required": ["merchant_pattern", "new_bucket"],
              "properties": {
                "merchant_pattern": {
                  "type": "string",
                  "description": "The merchant pattern to reclassify (e.g. \"SPOTIFY\")"
                },
                "new_bucket": {
                  "type": "string",
                  "enum": ["bills", "lifestyle", "income", "ignore"],
                  "description": "The new bucket to assign"
                }
              }
            }
          }
        }
      },
      "ConfirmResponse": {
        "type": "object",
        "required": ["success"],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "confirmed": {
            "type": "integer",
            "description": "Number of classifications confirmed"
          },
          "rules_created": {
            "type": "integer",
            "description": "Number of persistent merchant rules created"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "missingUserId": {
                "value": { "error": "userId is required" }
              },
              "invalidJson": {
                "value": { "error": "Invalid JSON body" }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Invalid or missing API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Invalid or missing X-RM-Agent-Key header"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Database not configured",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Database not configured"
            }
          }
        }
      }
    }
  }
}
