{
  "openapi": "3.1.1",
  "info": {
    "title": "AffordX API",
    "description": "API documentation for the AffordX Backend Service, covering Authentication and Subscriber endpoints.\n\n**Base URLs:**\n- Demo: `https://api.broker.v1.demo.affordx.nz`\n- Production: `https://api.broker.affordx.nz`\n\n**Download the OpenAPI document:** [JSON](https://api-docs.affordx.nz/openapi.json) · [YAML](https://api-docs.affordx.nz/openapi.yaml)\n",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.broker.v1.demo.affordx.nz",
      "description": "Demo"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Bearer token for authentication. Example: 'Bearer {token}'"
      }
    },
    "schemas": {
      "LoginRequest": {
        "type": "object",
        "required": [
          "username",
          "password"
        ],
        "properties": {
          "username": {
            "type": "string",
            "format": "email",
            "description": "User's email address.",
            "examples": [
              "user@example.com"
            ]
          },
          "password": {
            "type": "string",
            "format": "password",
            "description": "User's password.",
            "examples": [
              "string-password"
            ]
          }
        }
      },
      "JwtResponse": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "JWT access token."
          },
          "refreshToken": {
            "type": "string",
            "description": "Token to refresh the access token."
          },
          "id": {
            "type": "string",
            "description": "User's unique identifier."
          },
          "username": {
            "type": "string",
            "description": "User's username (email)."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "User's email address."
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of roles assigned to the user.",
            "examples": [
              [
                "ROLE_BROKER"
              ]
            ]
          }
        }
      },
      "ApplicationResponse": {
        "type": "object",
        "properties": {
          "applicationId": {
            "type": "string",
            "description": "Unique identifier for the application."
          },
          "clients": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "clientId": {
                  "type": "string"
                },
                "clientName": {
                  "type": "string"
                }
              }
            }
          },
          "status": {
            "type": "string",
            "description": "Current status of the application."
          },
          "applicationDate": {
            "type": "string",
            "format": "date-time",
            "description": "The date the application was created."
          },
          "accounts": {
            "type": "integer",
            "description": "Total number of accounts associated with the application."
          }
        }
      },
      "ClientResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the client."
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          }
        }
      },
      "ApplicationFilesResponse": {
        "type": "object",
        "properties": {
          "documents": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "documentType": {
                  "type": "string"
                },
                "documentUrl": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "pdfStatements": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "period": {
                  "type": "string"
                },
                "uri": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          }
        }
      },
      "DocumentUploadResponse": {
        "type": "object",
        "properties": {
          "applicationId": {
            "type": "string",
            "description": "The application the documents were uploaded to."
          },
          "documentCategory": {
            "type": "string",
            "description": "The category the documents were filed under.",
            "enum": [
              "BANK_STATEMENTS",
              "PAYSLIPS",
              "TAX_SUMMARY",
              "OTHER_DOCUMENTS"
            ]
          },
          "totalUploaded": {
            "type": "integer",
            "description": "Number of files successfully uploaded."
          },
          "statementProcessingTriggered": {
            "type": "boolean",
            "description": "True when uploaded bank statements were submitted for automated statement processing. Processing is asynchronous; poll GET /v1/subscriber/applications/{applicationId} until its transaction summary populates."
          },
          "documents": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "fileId": {
                  "type": "string"
                },
                "fileName": {
                  "type": "string"
                },
                "fileType": {
                  "type": "string"
                },
                "fileSize": {
                  "type": "integer",
                  "format": "int64"
                },
                "status": {
                  "type": "string"
                },
                "documentCategory": {
                  "type": "string"
                },
                "downloadUrl": {
                  "type": "string",
                  "format": "uri"
                },
                "processingReference": {
                  "type": "string",
                  "description": "Reference assigned when a bank statement is submitted for automated processing."
                }
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ClientApplicationRequest": {
        "type": "object",
        "required": [
          "clients"
        ],
        "properties": {
          "clients": {
            "type": "array",
            "description": "List of clients associated with the application.",
            "items": {
              "type": "object",
              "properties": {
                "clientId": {
                  "type": "string"
                },
                "isMainApplicant": {
                  "type": "boolean"
                }
              }
            }
          },
          "isJointApplication": {
            "type": "boolean"
          },
          "sentByEmail": {
            "type": "boolean"
          }
        }
      },
      "PlatformClientCreateRequest": {
        "type": "object",
        "required": [
          "firstName",
          "lastName",
          "email",
          "dateOfBirth",
          "externalReferenceId"
        ],
        "properties": {
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "dateOfBirth": {
            "type": "string",
            "format": "date"
          },
          "externalReferenceId": {
            "type": "string"
          }
        }
      },
      "BankCalculatorV2Request": {
        "type": "object",
        "description": "Inputs for the bank serviceability calculation. All monetary values are strings.",
        "properties": {
          "applicants": {
            "type": "string",
            "description": "Number of applicants. Must be 1 or 2.",
            "enum": [
              "1",
              "2"
            ],
            "examples": [
              "2"
            ]
          },
          "dependants": {
            "type": "string",
            "description": "Number of dependants. Must be 0-5.",
            "enum": [
              "0",
              "1",
              "2",
              "3",
              "4",
              "5"
            ],
            "examples": [
              "1"
            ]
          },
          "app1AnnualGrossIncome": {
            "type": "string",
            "description": "Applicant 1 annual gross income."
          },
          "app2AnnualGrossIncome": {
            "type": "string",
            "description": "Applicant 2 annual gross income."
          },
          "rentalIncomeGrossWeekly": {
            "type": "string",
            "description": "Gross weekly rental income."
          },
          "boarderIncomeGrossWeekly": {
            "type": "string",
            "description": "Gross weekly boarder income."
          },
          "otherIncomeNetWeekly": {
            "type": "string",
            "description": "Net weekly other income."
          },
          "app1StudentLoan": {
            "type": "string",
            "description": "Whether applicant 1 has a student loan. Must be Y or N.",
            "enum": [
              "Y",
              "N"
            ]
          },
          "app2StudentLoan": {
            "type": "string",
            "description": "Whether applicant 2 has a student loan. Must be Y or N.",
            "enum": [
              "Y",
              "N"
            ]
          },
          "existingMortgages": {
            "type": "string",
            "description": "Existing mortgage balances/repayments."
          },
          "overdraftLimit": {
            "type": "string",
            "description": "Overdraft limit."
          },
          "creditCardLimit": {
            "type": "string",
            "description": "Total credit card limit."
          },
          "personalLoanMonthlyRepayment": {
            "type": "string",
            "description": "Monthly personal loan repayment."
          },
          "hirePurchaseOtherLoanMonthlyPayment": {
            "type": "string",
            "description": "Monthly hire purchase / other loan payment."
          },
          "useBankBenchmark": {
            "type": "string",
            "description": "Whether to use the bank benchmark for expenses. Must be Y or N.",
            "enum": [
              "Y",
              "N"
            ],
            "default": "Y"
          },
          "rentBoard": {
            "type": "string",
            "description": "Rent or board expense."
          },
          "bodyCorpGroundRent": {
            "type": "string",
            "description": "Body corporate / ground rent expense."
          },
          "childcareSchooling": {
            "type": "string",
            "description": "Childcare and schooling expense."
          },
          "childSupport": {
            "type": "string",
            "description": "Child support expense."
          },
          "homeContentCarInsurance": {
            "type": "string",
            "description": "Home, contents and car insurance expense."
          },
          "lifeHealthInsurance": {
            "type": "string",
            "description": "Life and health insurance expense."
          },
          "ratesWater": {
            "type": "string",
            "description": "Rates and water expense."
          },
          "kiwisaverContribution": {
            "type": "string",
            "description": "KiwiSaver contribution."
          },
          "otherExpenses": {
            "type": "string",
            "description": "Other expenses."
          },
          "groceriesFood": {
            "type": "string",
            "description": "Groceries and food expense."
          },
          "utilities": {
            "type": "string",
            "description": "Utilities expense."
          },
          "phoneInternet": {
            "type": "string",
            "description": "Phone and internet expense."
          },
          "transportVehicle": {
            "type": "string",
            "description": "Transport and vehicle expense."
          },
          "clothingPersonalCare": {
            "type": "string",
            "description": "Clothing and personal care expense."
          },
          "proposedLoanTerm": {
            "type": "string",
            "description": "Proposed loan term in years.",
            "default": "30"
          }
        }
      },
      "BankResult": {
        "type": "object",
        "description": "Serviceability result for a single bank.",
        "properties": {
          "maxLending": {
            "description": "Maximum lending amount calculated for the bank."
          },
          "maxMonthlySurplus": {
            "description": "Maximum monthly surplus calculated for the bank."
          }
        }
      },
      "BankCalculationResults": {
        "type": "object",
        "description": "Serviceability results across supported banks.",
        "properties": {
          "anz": {
            "$ref": "#/components/schemas/BankResult"
          },
          "asb": {
            "$ref": "#/components/schemas/BankResult"
          },
          "bnz": {
            "$ref": "#/components/schemas/BankResult"
          },
          "kiwibank": {
            "$ref": "#/components/schemas/BankResult"
          },
          "westpac": {
            "$ref": "#/components/schemas/BankResult"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "integer"
          },
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "path": {
            "type": "string"
          }
        }
      }
    }
  },
  "paths": {
    "/v1/auth/login": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Authenticate user",
        "description": "Logs in a user and returns a JWT token upon successful authentication. By default login is validated via a reCAPTCHA token supplied in the request body. Server-to-server callers can instead send the `X-App-Name` header (matching the configured application name) to bypass the reCAPTCHA check.",
        "security": [],
        "parameters": [
          {
            "name": "X-App-Name",
            "in": "header",
            "required": true,
            "description": "Application name for server-to-server callers. When it matches the configured application name, the reCAPTCHA check is skipped and no reCAPTCHA token is required. (Optional on the server, but pre-filled here so API clients send it automatically.)",
            "schema": {
              "type": "string",
              "default": "AffordX",
              "examples": [
                "AffordX"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authentication successful",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JwtResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Invalid input or failed reCAPTCHA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriber/applications": {
      "get": {
        "tags": [
          "Subscription API"
        ],
        "summary": "Get all applications",
        "description": "Retrieves all applications for the current authenticated broker. Requires a JWT bearer token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ApplicationResponse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Subscription API"
        ],
        "summary": "Add new application",
        "description": "Creates a new application for the authenticated broker. Requires a JWT bearer token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "x-crm-platform",
            "in": "header",
            "required": true,
            "description": "The external CRM platform name.",
            "schema": {
              "type": "string",
              "examples": [
                "Monday"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientApplicationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplicationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        }
      }
    },
    "/v1/subscriber/applications/{applicationId}": {
      "get": {
        "tags": [
          "Subscription API"
        ],
        "summary": "Get application by ID",
        "description": "Retrieves a specific application by its ID for the authenticated broker. Requires a JWT bearer token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "applicationId",
            "in": "path",
            "required": true,
            "description": "ID of the application to retrieve.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplicationResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/subscriber/applications/{applicationId}/loan-accounts": {
      "get": {
        "tags": [
          "Subscription API"
        ],
        "summary": "Get all loan accounts for an application",
        "description": "Retrieves all loan accounts for a specific application. Requires a JWT bearer token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "applicationId",
            "in": "path",
            "required": true,
            "description": "ID of the application.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/subscriber/applications/{applicationId}/documents": {
      "get": {
        "tags": [
          "Subscription API"
        ],
        "summary": "Get application documents and PDF statements",
        "description": "Retrieves all submitted documents and PDF statements for a specific application. Requires a JWT bearer token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "applicationId",
            "in": "path",
            "required": true,
            "description": "ID of the application.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplicationFilesResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      },
      "post": {
        "tags": [
          "Subscription API"
        ],
        "summary": "Upload documents to an application",
        "description": "Manually uploads one or more documents (e.g. bank statements) to an application. Uploading with documentCategory=BANK_STATEMENTS submits the files for automated statement processing and triggers asynchronous report generation. There is no webhook — poll GET /v1/subscriber/applications/{applicationId} until its transaction summary populates. Other categories are stored only. Requires a JWT bearer token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "x-crm-platform",
            "in": "header",
            "required": true,
            "description": "The external CRM platform name.",
            "schema": {
              "type": "string",
              "examples": [
                "Monday"
              ]
            }
          },
          {
            "name": "applicationId",
            "in": "path",
            "required": true,
            "description": "ID of the application to upload documents to.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "files",
                  "documentCategory"
                ],
                "properties": {
                  "files": {
                    "type": "array",
                    "description": "One or more files to upload.",
                    "items": {
                      "type": "string",
                      "contentMediaType": "application/octet-stream"
                    }
                  },
                  "documentCategory": {
                    "type": "string",
                    "description": "Document category. BANK_STATEMENTS triggers automated statement processing.",
                    "enum": [
                      "BANK_STATEMENTS",
                      "PAYSLIPS",
                      "TAX_SUMMARY",
                      "OTHER_DOCUMENTS"
                    ]
                  },
                  "description": {
                    "type": "string",
                    "description": "Optional free-text description."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Documents uploaded successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentUploadResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - no files, or none uploaded successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v1/subscriber/clients": {
      "get": {
        "tags": [
          "Subscription API"
        ],
        "summary": "Get a list of clients for the login user",
        "description": "Retrieves a list of client for the login user. Requires a JWT bearer token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ClientResponse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Subscription API"
        ],
        "summary": "Create a new client",
        "description": "Creates a new client in the external CRM platform for the authenticated broker. Requires a JWT bearer token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "x-crm-platform",
            "in": "header",
            "required": true,
            "description": "The external CRM platform name.",
            "schema": {
              "type": "string",
              "examples": [
                "Monday"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlatformClientCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        }
      }
    },
    "/v1/subscriber/clients/{clientId}": {
      "get": {
        "tags": [
          "Subscription API"
        ],
        "summary": "Get Client Details",
        "description": "Retrieves detailed information about a specific client by their ID. Requires a JWT bearer token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "description": "ID of the client to retrieve.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/v2/bank-calculator/calculate": {
      "post": {
        "tags": [
          "Bank Calculator"
        ],
        "summary": "Calculate bank serviceability",
        "description": "Runs the bank serviceability calculation for the supplied applicant income and expense details, returning the maximum lending and monthly surplus for each supported bank. Requires a JWT bearer token with at least the BROKER role.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BankCalculatorV2Request"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankCalculationResults"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Invalid input or Master Sheet ID not configured"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal Server Error - Calculation failed"
          }
        }
      }
    }
  },
  "x-ext-urls": {}
}