{
  "openapi": "3.1.0",
  "info": {
    "title": "Drawserv Lender API",
    "version": "0.2.0",
    "summary": "A documented, per-lender door into a construction-loan book: projects, draws, inspections, budgets, documents.",
    "description": "The Drawserv Lender API lets a lender reach its own construction-loan book from its own systems: list projects, read draws and their evidence, read reviewed inspections and budgets, open a project, upload documents.\n\n**Status.** Every operation in this document answers on production and on the sandbox. A key is minted by Drawserv staff on request. Operations are added here as they are served. Nothing here is sensitive — it describes shapes, not data.\n\n## Authentication\n\nEvery request carries a key as a bearer token: `Authorization: Bearer dsk_live_…` on production, `dsk_test_…` on the sandbox. A key acts for a **lender**, optionally narrowed to one **department**, never for a person: it is minted by Drawserv staff, shown once, stored hashed, and revocable at once. A key carries **scopes** (below) as its ceiling; the lender and department it is bound to are its floor — a record outside them reads as `not_found`. A portal session token is refused: the API is not a second way to spend a login. A key may carry an IP allowlist and an expiry. Every request is written to a ledger (key, method, path, status, duration, address, request id) that staff can show the lender.\n\n| Scope | Grants |\n|---|---|\n| `projects:read` | Read projects, parties, budgets and released risk reports |\n| `draws:read` | Read draws, their lines, approvals and lien waivers |\n| `inspections:read` | Read reviewed inspections and their progress lines |\n| `documents:read` | Read document metadata and fetch short-lived download links |\n| `projects:write` | Create projects and update a pre-closing project's intake fields |\n| `documents:write` | Upload documents to a project |\n\nA new key starts with the read scopes (`projects:read`, `draws:read`, `inspections:read`, `documents:read`); a scope GET /me reports that no operation here needs is held in reserve.\n\n## Reading\n\nList answers are paged: `page` (from 1) and `per_page` (at most 200), answered as `{ data: [...], page: { number, size, total } }`. Draw lists are read as one **slice** (`scope`: working, funded, declined, open-waivers) or one **window** (`since`, with `until` for history); the whole ledger is never answered at once. Projects take `updated_since`. Every answer carries `X-Request-Id`.\n\nField names are the Drawserv data model's own, enum values are the application's proven vocabularies, and money is **dollars to the cent** as JSON numbers — never cents. Staff-only columns are not in any schema: the API shows a lender exactly what its portal shows, by the same rules.\n\n## Errors\n\nEvery non-2xx answer is `{ \"error\": { \"code\", \"message\", \"requestId\" } }`. The code is the contract; the message may change.\n\n| Code | HTTP | Meaning |\n|---|---|---|\n| `unauthorized` | 401 | The key is missing, malformed, revoked, expired, for the other environment, or a session token was sent |\n| `forbidden_scope` | 403 | The key lacks the scope the operation needs |\n| `not_found` | 404 | No such record in the caller's view — a record outside the key's lender or department reads as absent |\n| `validation_failed` | 400 | A parameter or body field is missing or malformed |\n| `rate_limited` | 429 | The key's request budget is spent; Retry-After says when to try again |\n| `conflict` | 409 | The write collides with the record's current state (a project no longer pre-closing) |\n| `internal` | 500 | Something failed on our side; the requestId names the ledger row to quote |\n\n## Versioning\n\n`/v1` in the path. Changes are additive: a field is never renamed or re-typed, an enum value never changes meaning, and a retired enum value stays declared while a row may still carry it. A breaking change is `/v2` beside `/v1`, with twelve months' notice in the changelog.\n\n## Sandbox\n\n`dsk_test_` keys work against `staging.app.drawserv.com`, a scrubbed copy of the book with a demo portfolio per lender. Nothing done there reaches production.\n\n## Delivery\n\nEach operation carries the phase it shipped in (`x-drawserv-phase`). This document carries served operations only, so `x-drawserv-served` is true on every one; an operation joins it the day its door opens.\n\n| Operation | Phase |\n|---|---|\n| `GET /me` | P1 — the read door |\n| `GET /projects` | P1 — the read door |\n| `POST /projects` | P2 — writes and documents |\n| `GET /projects/{projectId}` | P1 — the read door |\n| `PATCH /projects/{projectId}/intake` | P2 — writes and documents |\n| `GET /projects/{projectId}/parties` | P1 — the read door |\n| `GET /projects/{projectId}/budget` | P1 — the read door |\n| `GET /projects/{projectId}/draws` | P1 — the read door |\n| `GET /projects/{projectId}/documents` | P2 — writes and documents |\n| `POST /projects/{projectId}/documents` | P2 — writes and documents |\n| `GET /risk-reports/{projectId}` | P1 — the read door |\n| `GET /draws` | P1 — the read door |\n| `GET /draws/{drawId}` | P1 — the read door |\n| `GET /inspections` | P1 — the read door |\n| `GET /inspections/{inspectionId}` | P1 — the read door |\n| `GET /documents/{documentId}` | P2 — writes and documents |\n\n_Drawserv is a product of Findlan Inc d/b/a Drawserv._",
    "contact": {
      "name": "Drawserv",
      "url": "https://findlanassociates.com"
    },
    "x-drawserv-status": "read-door-writes-and-documents-live"
  },
  "servers": [
    {
      "url": "https://app.drawserv.com/api/v1",
      "description": "Production — dsk_live_ keys"
    },
    {
      "url": "https://staging.app.drawserv.com/api/v1",
      "description": "Sandbox — dsk_test_ keys, against a scrubbed copy of the book"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "What a key is."
    },
    {
      "name": "Projects",
      "description": "Projects on the lender's book, their parties, budgets and risk reports."
    },
    {
      "name": "Draws",
      "description": "Draw requests and their evidence."
    },
    {
      "name": "Inspections",
      "description": "Reviewed site visits."
    },
    {
      "name": "Documents",
      "description": "Files the lender may read, and uploads."
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Account"
        ],
        "summary": "Who am I",
        "description": "The lender, the department and the key behind the bearer. Needs no scope; the first call to make with a new key.",
        "x-scope": null,
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "responses": {
          "200": {
            "description": "The record",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Me"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/projects": {
      "get": {
        "operationId": "listProjects",
        "tags": [
          "Projects"
        ],
        "summary": "List projects",
        "description": "Every project in the key's view — the lender's book, or the department's — newest first.",
        "x-scope": "projects:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/lifecycle"
          },
          {
            "$ref": "#/components/parameters/department"
          },
          {
            "$ref": "#/components/parameters/updated_since"
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the set",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      },
      "post": {
        "operationId": "createProject",
        "tags": [
          "Projects"
        ],
        "summary": "Create a project",
        "description": "Opens a project on the lender's book in the Draft stage of intake, as the portal's New project form would. The answer is the project as the API reads it back.",
        "x-scope": "projects:write",
        "x-drawserv-phase": "P2",
        "x-drawserv-served": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Project"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/projects/{projectId}": {
      "get": {
        "operationId": "getProject",
        "tags": [
          "Projects"
        ],
        "summary": "Get a project",
        "x-scope": "projects:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "responses": {
          "200": {
            "description": "The record",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Project"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/projects/{projectId}/intake": {
      "patch": {
        "operationId": "updateProjectIntake",
        "tags": [
          "Projects"
        ],
        "summary": "Update a project's intake fields",
        "description": "Changes the intake fields of a project that has not closed. Refused with conflict once the project is active.",
        "x-scope": "projects:write",
        "x-drawserv-phase": "P2",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntakePatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The record",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Project"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/projects/{projectId}/parties": {
      "get": {
        "operationId": "listProjectParties",
        "tags": [
          "Projects"
        ],
        "summary": "List a project's parties",
        "description": "The borrower, the builder and any other party on the project, with their contact people. Party changes stay in the portal in v1.",
        "x-scope": "projects:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the set",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Party"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/projects/{projectId}/budget": {
      "get": {
        "operationId": "getProjectBudget",
        "tags": [
          "Projects"
        ],
        "summary": "Get a project's budget",
        "description": "The budget lines, the funding sources and the totals, computed on the server.",
        "x-scope": "projects:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "responses": {
          "200": {
            "description": "The record",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Budget"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/projects/{projectId}/draws": {
      "get": {
        "operationId": "listProjectDraws",
        "tags": [
          "Draws"
        ],
        "summary": "List a project's draws",
        "description": "The project's draws, newest first, with the same slices and windows as the whole-book list.",
        "x-scope": "draws:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/scope"
          },
          {
            "$ref": "#/components/parameters/since"
          },
          {
            "$ref": "#/components/parameters/until"
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the set",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Draw"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/projects/{projectId}/documents": {
      "get": {
        "operationId": "listProjectDocuments",
        "tags": [
          "Documents"
        ],
        "summary": "List a project's documents",
        "description": "The documents the lender may read on the project, newest first, each with a short-lived download link.",
        "x-scope": "documents:read",
        "x-drawserv-phase": "P2",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the set",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Document"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      },
      "post": {
        "operationId": "createProjectDocument",
        "tags": [
          "Documents"
        ],
        "summary": "Ask for an upload slot",
        "description": "Step one of two: declare the file and get a signed URL to PUT its bytes to. The document row lands when the bytes do.",
        "x-scope": "documents:write",
        "x-drawserv-phase": "P2",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DocumentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UploadSlot"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/risk-reports/{projectId}": {
      "get": {
        "operationId": "getRiskReport",
        "tags": [
          "Projects"
        ],
        "summary": "Get a project's released risk report",
        "description": "The stored report PDF, released while the project's approval stands; not_found before approval or after a later decision.",
        "x-scope": "projects:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "responses": {
          "200": {
            "description": "The record",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/RiskReport"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/draws": {
      "get": {
        "operationId": "listDraws",
        "tags": [
          "Draws"
        ],
        "summary": "List draws",
        "description": "Draws across the key's view, newest first. Ask for one slice (scope) or one window (since, until); the whole ledger is never answered at once.",
        "x-scope": "draws:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/scope"
          },
          {
            "$ref": "#/components/parameters/since"
          },
          {
            "$ref": "#/components/parameters/until"
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the set",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Draw"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/draws/{drawId}": {
      "get": {
        "operationId": "getDraw",
        "tags": [
          "Draws"
        ],
        "summary": "Get a draw",
        "x-scope": "draws:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/drawId"
          }
        ],
        "responses": {
          "200": {
            "description": "The record",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Draw"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/inspections": {
      "get": {
        "operationId": "listInspections",
        "tags": [
          "Inspections"
        ],
        "summary": "List inspections",
        "description": "Reviewed inspections across the key's view, newest first.",
        "x-scope": "inspections:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/project"
          },
          {
            "$ref": "#/components/parameters/inspections_since"
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the set",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Inspection"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/Page"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/inspections/{inspectionId}": {
      "get": {
        "operationId": "getInspection",
        "tags": [
          "Inspections"
        ],
        "summary": "Get an inspection",
        "x-scope": "inspections:read",
        "x-drawserv-phase": "P1",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/inspectionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The record",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Inspection"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/documents/{documentId}": {
      "get": {
        "operationId": "getDocument",
        "tags": [
          "Documents"
        ],
        "summary": "Get a document",
        "description": "The document's metadata and a fresh short-lived download link.",
        "x-scope": "documents:read",
        "x-drawserv-phase": "P2",
        "x-drawserv-served": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/documentId"
          }
        ],
        "responses": {
          "200": {
            "description": "The record",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Document"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "dsk_live_… (production) or dsk_test_… (sandbox)",
        "description": "A per-lender key, minted by Drawserv staff, shown once, revocable at once. The scope an operation needs is its `x-scope`; a key's scopes are listed in the Authentication section."
      }
    },
    "headers": {
      "RequestId": {
        "description": "The request's id — quote it to support; it names the ledger row",
        "schema": {
          "type": "string"
        }
      }
    },
    "parameters": {
      "projectId": {
        "name": "projectId",
        "in": "path",
        "required": true,
        "description": "A project's id",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "drawId": {
        "name": "drawId",
        "in": "path",
        "required": true,
        "description": "A draw's id",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "inspectionId": {
        "name": "inspectionId",
        "in": "path",
        "required": true,
        "description": "An inspection's id",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "documentId": {
        "name": "documentId",
        "in": "path",
        "required": true,
        "description": "A document's id",
        "schema": {
          "type": "string"
        }
      },
      "page": {
        "name": "page",
        "in": "query",
        "required": false,
        "description": "The page to answer, from 1",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        }
      },
      "per_page": {
        "name": "per_page",
        "in": "query",
        "required": false,
        "description": "Rows per page, at most 200",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "lifecycle": {
        "name": "lifecycle",
        "in": "query",
        "required": false,
        "description": "Only projects in this lifecycle",
        "schema": {
          "type": "string",
          "enum": [
            "intake",
            "active",
            "completed"
          ]
        }
      },
      "department": {
        "name": "department",
        "in": "query",
        "required": false,
        "description": "Only projects in this department (LenderDepartment.id)",
        "schema": {
          "type": "string"
        }
      },
      "updated_since": {
        "name": "updated_since",
        "in": "query",
        "required": false,
        "description": "Only projects changed at or after this moment",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "scope": {
        "name": "scope",
        "in": "query",
        "required": false,
        "description": "One slice of the ledger: working is every draw not yet funded or declined; funded and declined one outcome each; open-waivers the draws still carrying a lien waiver to sign",
        "schema": {
          "type": "string",
          "enum": [
            "working",
            "funded",
            "declined",
            "open-waivers"
          ]
        }
      },
      "since": {
        "name": "since",
        "in": "query",
        "required": false,
        "description": "A window of the ledger: every open draw whatever its age, plus every draw with a stage stamp at or after this moment",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "until": {
        "name": "until",
        "in": "query",
        "required": false,
        "description": "With since: only stage stamps in [since, until) — an older window of history",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "inspections_since": {
        "name": "since",
        "in": "query",
        "required": false,
        "description": "Only inspections reviewed at or after this moment",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "project": {
        "name": "project",
        "in": "query",
        "required": false,
        "description": "Only inspections on this project",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "The key is missing, malformed, revoked, expired, for the other environment, or a session token was sent",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ForbiddenScope": {
        "description": "The key lacks the scope the operation needs",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such record in the caller's view — a record outside the key's lender or department reads as absent",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "A parameter or body field is missing or malformed",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "The key's request budget is spent; Retry-After says when to try again",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "description": "Seconds to wait before trying again",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "The write collides with the record's current state (a project no longer pre-closing)",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Internal": {
        "description": "Something failed on our side; the requestId names the ledger row to quote",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Page": {
        "type": "object",
        "description": "Where a list answer sits in its set.",
        "properties": {
          "number": {
            "type": "integer",
            "description": "The page answered, from 1"
          },
          "size": {
            "type": "integer",
            "description": "Rows per page as applied (per_page, capped at 200)"
          },
          "total": {
            "type": "integer",
            "description": "Rows in the whole set"
          }
        },
        "required": [
          "number",
          "size",
          "total"
        ]
      },
      "Error": {
        "type": "object",
        "description": "Every non-2xx answer. The code is the contract; the message is for a person; the requestId names the request's ledger row (also the X-Request-Id header) to quote to support.",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorized",
                  "forbidden_scope",
                  "not_found",
                  "validation_failed",
                  "rate_limited",
                  "conflict",
                  "internal"
                ],
                "description": "One of the closed set of codes"
              },
              "message": {
                "type": "string",
                "description": "A sentence for a person; wording may change"
              },
              "requestId": {
                "type": "string",
                "description": "The request's id, the same as the X-Request-Id header"
              }
            },
            "required": [
              "code",
              "message",
              "requestId"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Lender": {
        "type": "object",
        "description": "The lending organisation a key acts for.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The lender's id",
            "x-column": "id"
          },
          "name": {
            "type": "string",
            "description": "The lender's name",
            "x-column": "name"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the lender was added to Drawserv",
            "x-column": "created_at"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "x-graph-label": "Lender",
        "x-table": "lenders"
      },
      "LenderDepartment": {
        "type": "object",
        "description": "A department of the lender. A key bound to a department sees that department's projects; a key bound to the lender alone sees the whole book.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The department's id",
            "x-column": "id"
          },
          "name": {
            "type": "string",
            "description": "The department's name",
            "x-column": "name"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the department was added",
            "x-column": "created_at"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "x-graph-label": "LenderDepartment",
        "x-table": "lender_departments"
      },
      "ApiKey": {
        "type": "object",
        "description": "The calling key, as the ledger knows it. The secret itself is never returned.",
        "properties": {
          "name": {
            "type": "string",
            "description": "The display name given at creation (\"Acme Bank API — Treasury\")"
          },
          "prefix": {
            "type": "string",
            "description": "The key's first six characters, to tell keys apart"
          },
          "environment": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "description": "live on production, test on the sandbox"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "projects:read",
                "draws:read",
                "inspections:read",
                "documents:read",
                "events:read",
                "projects:write",
                "documents:write",
                "webhooks:manage"
              ]
            },
            "description": "The key's scopes — the ceiling of what it may do"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the key was minted"
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the key stops working; null for no expiry"
          },
          "lastUsedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The newest request before this one"
          }
        },
        "required": [
          "name",
          "prefix",
          "environment",
          "scopes",
          "createdAt"
        ]
      },
      "Me": {
        "type": "object",
        "description": "What a caller is: the lender, the department (if the key is narrowed to one) and the key.",
        "properties": {
          "lender": {
            "$ref": "#/components/schemas/Lender",
            "description": "The lender the key acts for"
          },
          "department": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/LenderDepartment"
              },
              {
                "type": "null"
              }
            ],
            "description": "The department the key is narrowed to, or null for the whole book"
          },
          "key": {
            "$ref": "#/components/schemas/ApiKey",
            "description": "The calling key"
          }
        },
        "required": [
          "lender",
          "department",
          "key"
        ]
      },
      "ProjectRef": {
        "type": "object",
        "description": "Enough of a project to name and route it from a draw or inspection row.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The project's id"
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "The one-line property address"
          },
          "borrower": {
            "type": [
              "string",
              "null"
            ],
            "description": "The borrower as named at intake"
          },
          "builder": {
            "type": [
              "string",
              "null"
            ],
            "description": "The builder (general contractor) as named at intake"
          },
          "lifecycleStatus": {
            "type": "string",
            "enum": [
              "intake",
              "active",
              "completed"
            ],
            "description": "The overall lifecycle"
          }
        },
        "required": [
          "id",
          "address",
          "lifecycleStatus"
        ]
      },
      "Person": {
        "type": "object",
        "description": "A contact person on a party.",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Full name"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Email address"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Phone number, as entered"
          }
        }
      },
      "Project": {
        "type": "object",
        "description": "One construction-loan project on the lender's book. lifecycleStatus is the overall lifecycle (intake → active → completed); intakeStage is the stage within intake and only means something while lifecycleStatus is intake. The four money figures (budgetTotal, fundedToDate, remainingToFund, constructionProgress) are computed on the server from the budget, the funded draws and the reviewed inspections — read them, never re-sum lines.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The project's id",
            "x-column": "id"
          },
          "lenderId": {
            "type": "string",
            "description": "The lender the project is on the book of — always the key's lender",
            "x-column": "lender_id"
          },
          "departmentId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The department the project sits in; null for the whole institution",
            "x-column": "lender_department_id"
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "The one-line property address",
            "x-column": "property_address"
          },
          "streetAddress": {
            "type": [
              "string",
              "null"
            ],
            "description": "Street address",
            "x-column": "street_address"
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "City",
            "x-column": "city"
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "description": "US state, full name (Maine)",
            "x-column": "state"
          },
          "zipCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "ZIP code",
            "x-column": "zip_code"
          },
          "lifecycleStatus": {
            "type": "string",
            "enum": [
              "intake",
              "active",
              "completed"
            ],
            "description": "The overall lifecycle: intake → active → completed",
            "x-column": "lifecycle_status"
          },
          "intakeStage": {
            "type": "string",
            "enum": [
              "Draft",
              "In Review",
              "Approved",
              "Declined"
            ],
            "description": "The stage within intake; only meaningful while lifecycleStatus is intake",
            "x-column": "status"
          },
          "paymentStatus": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "unpaid",
              "processing",
              "paid",
              "waived"
            ],
            "description": "The project fee's billing state",
            "x-column": "payment_status"
          },
          "constructionType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "New Construction",
              "Renovation",
              "Modular",
              "Commercial",
              "Manufactured",
              "Modular / Manufactured"
            ],
            "description": "Modular = factory-built sections set on a permanent foundation to the state/IRC residential code, financed like site-built; Manufactured = built to the federal HUD code on a permanent chassis, the mobile home; the retired Modular / Manufactured is one of the two, not yet reclassified — older rows may still carry Modular / Manufactured",
            "x-column": "construction_type"
          },
          "sqft": {
            "type": [
              "number",
              "null"
            ],
            "description": "Total square footage",
            "x-column": "total_square_footage"
          },
          "contractAmount": {
            "type": [
              "number",
              "null"
            ],
            "description": "The builder's contract or cost-plus estimate, dollars — the intake basis",
            "x-column": "contract_amount"
          },
          "referenceCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "An external reference or loan number, when one was recorded",
            "x-column": "reference_code"
          },
          "closingDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The loan's closing date, recorded when the project was activated; null for projects activated before it was tracked",
            "x-column": "closing_date"
          },
          "expectedMaturityDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The loan's expected maturity date",
            "x-column": "expected_maturity_date"
          },
          "submittedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the project was submitted for review (intake)",
            "x-column": "submitted_at"
          },
          "approvedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the project was approved (intake); the risk report is released from this moment",
            "x-column": "approved_at"
          },
          "declinedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the project was declined (intake)",
            "x-column": "declined_at"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the project was completed",
            "x-column": "completed_at"
          },
          "budgetConfirmedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When staff confirmed the budget",
            "x-column": "budget_confirmed_at"
          },
          "budgetConfirmedTotal": {
            "type": [
              "number",
              "null"
            ],
            "description": "The working-budget total stamped when the budget was confirmed, dollars",
            "x-column": "budget_confirmed_total"
          },
          "projectFeeBilled": {
            "type": [
              "number",
              "null"
            ],
            "description": "The project fee as billed, dollars; null until billed",
            "x-column": "assessment_fee_billed"
          },
          "latitude": {
            "type": [
              "number",
              "null"
            ],
            "description": "WGS-84 latitude of the property",
            "x-column": "latitude"
          },
          "longitude": {
            "type": [
              "number",
              "null"
            ],
            "description": "WGS-84 longitude of the property",
            "x-column": "longitude"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the project was created",
            "x-column": "created_at"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the project last changed",
            "x-column": "updated_at"
          },
          "budgetTotal": {
            "type": [
              "number",
              "null"
            ],
            "x-unit": "USD",
            "description": "Σ the budget lines, dollars; computed on the server",
            "x-derived": "rollup"
          },
          "fundedToDate": {
            "type": "number",
            "x-unit": "USD",
            "description": "The pre-ledger funding epoch plus every funded draw's lines, dollars; computed on the server",
            "x-derived": "rollup"
          },
          "remainingToFund": {
            "type": [
              "number",
              "null"
            ],
            "x-unit": "USD",
            "description": "budgetTotal − fundedToDate, dollars; computed on the server",
            "x-derived": "rollup"
          },
          "constructionProgress": {
            "type": [
              "number",
              "null"
            ],
            "x-unit": "percent",
            "description": "Percent complete, 0–100, budget-weighted over each line's latest reviewed observation; null before a reviewed inspection",
            "x-derived": "rollup"
          },
          "borrower": {
            "type": [
              "string",
              "null"
            ],
            "description": "The borrower as named at intake; the party records carry the people",
            "x-column": "borrower"
          },
          "builder": {
            "type": [
              "string",
              "null"
            ],
            "description": "The builder (general contractor) as named at intake",
            "x-column": "contractor"
          },
          "lender": {
            "$ref": "#/components/schemas/Lender",
            "description": "The lender, by name"
          },
          "department": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/LenderDepartment"
              },
              {
                "type": "null"
              }
            ],
            "description": "The department, by name, or null"
          }
        },
        "required": [
          "id",
          "lenderId",
          "lender",
          "lifecycleStatus",
          "intakeStage",
          "createdAt",
          "updatedAt",
          "fundedToDate"
        ],
        "x-graph-label": "Project",
        "x-table": "projects",
        "x-redaction": "PROJECT_STAFF_ONLY"
      },
      "Party": {
        "type": "object",
        "description": "A party on the project — the borrower, the builder, or another role — with its contact people.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The party record's id",
            "x-column": "id"
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "The project",
            "x-column": "project_id"
          },
          "stakeholderId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The cross-project identity (the same builder on several projects shares one); null while unlinked",
            "x-column": "stakeholder_id"
          },
          "role": {
            "type": "string",
            "x-known-values": [
              "borrower",
              "builder"
            ],
            "description": "The party's role on the project; borrower and builder are the two the app writes, other words are possible",
            "x-column": "role"
          },
          "partyKind": {
            "type": "string",
            "enum": [
              "company",
              "household"
            ],
            "description": "company = a business; household = an individual or family",
            "x-column": "party_kind"
          },
          "companyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The company (empty for a household)",
            "x-column": "company_name"
          },
          "contactName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The primary contact's name",
            "x-column": "contact_name"
          },
          "contactEmail": {
            "type": [
              "string",
              "null"
            ],
            "description": "The primary contact's email",
            "x-column": "contact_email"
          },
          "contactPhone": {
            "type": [
              "string",
              "null"
            ],
            "description": "The primary contact's phone",
            "x-column": "contact_phone"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the party was added",
            "x-column": "created_at"
          },
          "people": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Person"
            },
            "description": "The contact people, first = primary",
            "x-column": "people"
          },
          "stakeholderName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The cross-project identity's canonical name; null while unlinked"
          }
        },
        "required": [
          "id",
          "projectId",
          "role",
          "partyKind",
          "people",
          "createdAt"
        ],
        "x-table": "project_contacts",
        "x-redaction": "CONTACT_STAFF_ONLY"
      },
      "BudgetLineItem": {
        "type": "object",
        "description": "A line of the project's budget (the uses side). fundedToDate is computed on the server: the pre-ledger epoch plus every funded draw's amount on the line.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The line's id",
            "x-column": "id"
          },
          "name": {
            "type": "string",
            "description": "The line's name",
            "x-column": "name"
          },
          "amount": {
            "type": "number",
            "description": "The budgeted amount, dollars",
            "x-column": "amount"
          },
          "epochFundedToDate": {
            "type": [
              "number",
              "null"
            ],
            "description": "Funded on the line before draws were recorded on the platform, dollars; a starting balance, not a running total",
            "x-column": "funded_to_date"
          },
          "epochClosingDraw": {
            "type": [
              "number",
              "null"
            ],
            "description": "The line's dollars settled in the closing draw at intake; a starting balance, not a running total",
            "x-column": "closing_draw"
          },
          "sortOrder": {
            "type": "integer",
            "description": "Display order",
            "x-column": "sort_order"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the line was added",
            "x-column": "created_at"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the line last changed",
            "x-column": "updated_at"
          },
          "currentProgress": {
            "type": [
              "number",
              "null"
            ],
            "x-unit": "percent",
            "description": "The line's latest reviewed observation, 0–100; null when no reviewed inspection observed it",
            "x-derived": "rollup"
          },
          "fundedToDate": {
            "type": "number",
            "description": "Funded on the line to date, dollars, to the cent: epochFundedToDate plus epochClosingDraw plus funded draws' lines against it"
          }
        },
        "required": [
          "id",
          "name",
          "amount",
          "fundedToDate",
          "sortOrder"
        ],
        "x-graph-label": "BudgetLineItem",
        "x-table": "project_budget_lines"
      },
      "FundingSource": {
        "type": "object",
        "description": "Where the project's money comes from (the sources side): the loan, or equity. fundedToDate is computed on the server like a budget line's.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The source's id",
            "x-column": "id"
          },
          "name": {
            "type": "string",
            "description": "The source's name",
            "x-column": "name"
          },
          "sourceType": {
            "type": "string",
            "enum": [
              "loan",
              "equity"
            ],
            "description": "loan or equity",
            "x-column": "source_type"
          },
          "amount": {
            "type": "number",
            "description": "The committed amount, dollars",
            "x-column": "amount"
          },
          "epochFundedToDate": {
            "type": [
              "number",
              "null"
            ],
            "description": "Drawn from the source before draws were recorded on the platform, dollars; a starting balance, not a running total",
            "x-column": "funded_to_date"
          },
          "epochClosingDraw": {
            "type": [
              "number",
              "null"
            ],
            "description": "The source's dollars settled in the closing draw at intake; a starting balance, not a running total",
            "x-column": "closing_draw"
          },
          "sortOrder": {
            "type": "integer",
            "description": "Display order",
            "x-column": "sort_order"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the source was added",
            "x-column": "created_at"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the source last changed",
            "x-column": "updated_at"
          },
          "fundedToDate": {
            "type": "number",
            "description": "Drawn from the source to date, dollars, to the cent: epochFundedToDate plus epochClosingDraw plus funded draws' funding lines against it"
          }
        },
        "required": [
          "id",
          "name",
          "sourceType",
          "amount",
          "fundedToDate",
          "sortOrder"
        ],
        "x-graph-label": "FundingSource",
        "x-table": "project_funding_sources"
      },
      "Budget": {
        "type": "object",
        "description": "The project's budget: its lines, its funding sources and the four totals the app computes from them.",
        "properties": {
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "The project"
          },
          "confirmedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When staff confirmed the budget; null while it is still being assembled"
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BudgetLineItem"
            },
            "description": "The budget lines, in sort order"
          },
          "fundingSources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FundingSource"
            },
            "description": "The funding sources, in sort order"
          },
          "totals": {
            "type": "object",
            "properties": {
              "budgetTotal": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Σ the lines, dollars, to the cent; null until the budget has lines"
              },
              "fundedToDate": {
                "type": "number",
                "description": "Funded to date across the lines, dollars, to the cent"
              },
              "remainingToFund": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "budgetTotal − fundedToDate, dollars, to the cent; null until the budget has lines"
              },
              "constructionProgress": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Percent complete, 0–100, from the latest reviewed inspection; null before one"
              }
            },
            "required": [
              "fundedToDate",
              "constructionProgress"
            ]
          }
        },
        "required": [
          "projectId",
          "lines",
          "fundingSources",
          "totals"
        ]
      },
      "Draw": {
        "type": "object",
        "description": "A draw request on a project. status is the current stage only (draft → pending → approved → funded, declined as the side exit); the stage stamps say what was ever reached. number is \"Draw 4\": the 1-based rank by creation within the project, computed on the server. requestedAmount is Σ the lines, computed on the server. Approvals that were revoked and lien waivers that were voided are not returned.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The draw's id",
            "x-column": "id"
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "The project",
            "x-column": "project_id"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "pending",
              "approved",
              "funded",
              "declined",
              "inspection",
              "review"
            ],
            "description": "The current stage: draft → pending → approved → funded, declined as the side exit — older rows may still carry inspection, review",
            "x-column": "status"
          },
          "drawType": {
            "type": "string",
            "enum": [
              "standard",
              "prepaid",
              "closing"
            ],
            "description": "prepaid = the builder's spend before the loan; closing = disbursed when the loan closes; standard = a regular post-closing request",
            "x-column": "draw_type"
          },
          "requestedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who requested the draw",
            "x-column": "requested_by_name"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the draw was created (a draft)",
            "x-column": "created_at"
          },
          "submittedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it left draft — the request date",
            "x-column": "submitted_at"
          },
          "approvedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When staff approved it",
            "x-column": "approved_at"
          },
          "approvedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who approved it",
            "x-column": "approved_by_name"
          },
          "declinedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was declined",
            "x-column": "declined_at"
          },
          "declinedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who declined it",
            "x-column": "declined_by_name"
          },
          "fundedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was marked funded — the draw's completion",
            "x-column": "funded_at"
          },
          "fundedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who marked it funded",
            "x-column": "funded_by_name"
          },
          "fundedVia": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "lender",
              "staff"
            ],
            "description": "lender = the lender signed the funding; staff = recorded by staff; null until funded",
            "x-column": "funded_via"
          },
          "inspectionWaivedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When staff waived the inspection requirement for this draw",
            "x-column": "inspection_waived_at"
          },
          "inspectionWaivedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who waived it",
            "x-column": "inspection_waived_by_name"
          },
          "lienWaiversWaivedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When staff waived the lien-waiver requirement for this draw (not a waiver's own state)",
            "x-column": "lien_waivers_waived_at"
          },
          "lienWaiversWaivedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who waived it",
            "x-column": "lien_waivers_waived_by_name"
          },
          "approvalsWaivedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When staff waived the party-approval requirement for this draw (not one approval's own state)",
            "x-column": "approvals_waived_at"
          },
          "approvalsWaivedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who waived it",
            "x-column": "approvals_waived_by_name"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the draw last changed",
            "x-column": "updated_at"
          },
          "number": {
            "type": "integer",
            "description": "\"Draw 4\": the 1-based rank by creation within the project; computed on the server",
            "x-derived": "ordinal"
          },
          "requestedAmount": {
            "type": [
              "number",
              "null"
            ],
            "x-unit": "USD",
            "description": "Σ the lines, dollars; computed on the server (null while a draft has no lines)",
            "x-derived": "rollup"
          },
          "declineReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it was declined",
            "x-column": "decline_reason"
          },
          "fundedAmount": {
            "type": [
              "number",
              "null"
            ],
            "description": "The total the funding signature was for, dollars, to the cent; held while the draw stays funded",
            "x-column": "funded_amount"
          },
          "fundedNote": {
            "type": [
              "string",
              "null"
            ],
            "description": "How the disbursement was confirmed, as written at funding",
            "x-column": "funded_note"
          },
          "inspectionWaivedReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the inspection requirement was waived for this draw",
            "x-column": "inspection_waived_reason"
          },
          "lienWaiversWaivedReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the lien-waiver requirement was waived for this draw",
            "x-column": "lien_waivers_waived_reason"
          },
          "approvalsWaivedReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the party-approval requirement was waived for this draw",
            "x-column": "approvals_waived_reason"
          },
          "project": {
            "$ref": "#/components/schemas/ProjectRef",
            "description": "The project, enough to name and route it"
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DrawLineItem"
            },
            "description": "The amounts requested per budget line"
          },
          "approvals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DrawApproval"
            },
            "description": "The party approvals on the draw (revoked ones excluded)"
          },
          "lienWaivers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LienWaiver"
            },
            "description": "The lien waivers requested on the draw (voided ones excluded)"
          },
          "inspections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InspectionRef"
            },
            "description": "The reviewed inspections that are evidence for the draw — its own and any cited as shared evidence"
          }
        },
        "required": [
          "id",
          "projectId",
          "number",
          "status",
          "drawType",
          "createdAt",
          "updatedAt",
          "project",
          "lines",
          "approvals",
          "lienWaivers",
          "inspections"
        ],
        "x-graph-label": "Draw",
        "x-table": "project_draws"
      },
      "DrawLineItem": {
        "type": "object",
        "description": "One draw's requested amount against one budget line. A budget line with no row here has nothing drawn on this draw.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The line's id",
            "x-column": "id"
          },
          "budgetLineId": {
            "type": "string",
            "description": "The budget line drawn against (BudgetLineItem.id)",
            "x-column": "budget_line_id"
          },
          "amount": {
            "type": "number",
            "description": "The amount requested on the line, dollars",
            "x-column": "amount"
          }
        },
        "required": [
          "id",
          "budgetLineId",
          "amount"
        ],
        "x-graph-label": "DrawLineItem",
        "x-table": "project_draw_lines"
      },
      "DrawApproval": {
        "type": "object",
        "description": "A party's approval record on a draw: who still has to sign, who has signed, who was waived. state is computed per row. Revoked records are closed history and are not returned. The signature itself is not returned.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The approval record's id",
            "x-column": "id"
          },
          "partyId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The party asked (Party.id); null once that party was removed from the project",
            "x-column": "contact_id"
          },
          "lastSentAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the approval request was last emailed or texted to the party; null if never sent",
            "x-column": "last_sent_at"
          },
          "approvedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the party signed",
            "x-column": "approved_at"
          },
          "approvedAmount": {
            "type": [
              "number",
              "null"
            ],
            "description": "The amount the signer consented to, dollars; null on records signed before it was stamped",
            "x-column": "approved_amount"
          },
          "waivedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When staff waived this party's approval on the draw",
            "x-column": "waived_at"
          },
          "waivedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who waived it",
            "x-column": "waived_by_name"
          },
          "approverName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The signer's name as typed at signing",
            "x-column": "approver_name"
          },
          "signedVia": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "app",
              "link"
            ],
            "description": "app = signed in the portal; link = signed through an emailed link",
            "x-column": "signed_via"
          },
          "state": {
            "type": "string",
            "enum": [
              "revoked",
              "signed",
              "waived",
              "awaiting"
            ],
            "description": "Computed per row: signed when approvedAt is set, else waived when waivedAt is set, else awaiting. Revoked records are not returned.",
            "x-derived": "state"
          },
          "partyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The party's name as recorded when asked",
            "x-column": "stakeholder_name"
          },
          "contactName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The person asked",
            "x-column": "contact_name"
          },
          "approvedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who signed, as recorded",
            "x-column": "approved_by_name"
          },
          "waivedReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why staff waived this party's approval",
            "x-column": "waived_reason"
          }
        },
        "required": [
          "id",
          "state"
        ],
        "x-graph-label": "DrawApproval"
      },
      "LienWaiver": {
        "type": "object",
        "description": "A lien waiver requested from a payee on a draw. state is computed per row: requested (open) or executed. Voided waivers are closed history and are not returned.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The waiver's id",
            "x-column": "id"
          },
          "stakeholderId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The payee's cross-project identity; null once that identity was deleted",
            "x-column": "stakeholder_id"
          },
          "amount": {
            "type": "number",
            "description": "The amount waived, dollars",
            "x-column": "amount"
          },
          "requestedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the waiver was requested",
            "x-column": "created_at"
          },
          "lastSentAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the signature request was last emailed or texted to the payee; null if never sent",
            "x-column": "last_sent_at"
          },
          "requestedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who requested it",
            "x-column": "requested_by_name"
          },
          "signedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the payee signed",
            "x-column": "signed_at"
          },
          "signedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who signed, as typed at signing",
            "x-column": "signed_by_name"
          },
          "signedVia": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "app",
              "link"
            ],
            "description": "app = signed in the portal; link = signed through an emailed link",
            "x-column": "signed_via"
          },
          "state": {
            "type": "string",
            "enum": [
              "voided",
              "executed",
              "requested"
            ],
            "description": "Computed per row: executed when signedAt is set, else requested (open). Voided waivers are not returned.",
            "x-derived": "state"
          },
          "stakeholderName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The payee's name as recorded",
            "x-column": "stakeholder_name"
          }
        },
        "required": [
          "id",
          "amount",
          "state",
          "requestedAt"
        ],
        "x-graph-label": "LienWaiver"
      },
      "InspectionRef": {
        "type": "object",
        "description": "An inspection as a draw's evidence: its id and stage stamps.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The inspection's id"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "completed",
              "reviewed"
            ],
            "description": "Always reviewed on a draw's evidence list"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the visit was submitted"
          },
          "reviewedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When staff reviewed and released it"
          }
        },
        "required": [
          "id",
          "status"
        ]
      },
      "Inspection": {
        "type": "object",
        "description": "A site visit on a project, released to the lender once staff have reviewed it — the API returns reviewed inspections only. origin tells an ordered inspector visit from a member's own site check.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The inspection's id",
            "x-column": "id"
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "The project",
            "x-column": "project_id"
          },
          "drawId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The draw it was ordered on; null for a periodic visit",
            "x-column": "draw_id"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "completed",
              "reviewed"
            ],
            "description": "The current stage: pending → completed → reviewed. The API returns reviewed inspections only",
            "x-column": "status"
          },
          "origin": {
            "type": "string",
            "enum": [
              "ordered",
              "self"
            ],
            "description": "ordered = an inspector visit staff ordered; self = a site check a borrower or builder submitted themselves",
            "x-column": "origin"
          },
          "createdByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who submitted a site check; null on an ordered visit",
            "x-column": "created_by_name"
          },
          "scheduledFor": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The date the visit was scheduled for",
            "x-column": "scheduled_for"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the visit was submitted — the inspection date",
            "x-column": "completed_at"
          },
          "reviewedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When staff reviewed and released it",
            "x-column": "reviewed_at"
          },
          "reviewedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who reviewed it",
            "x-column": "reviewed_by_name"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the visit was ordered or started",
            "x-column": "created_at"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the inspection last changed",
            "x-column": "updated_at"
          },
          "inspectorName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The inspector, by name; null on a site check",
            "x-column": "inspector_name"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "The inspector's field notes",
            "x-column": "summary"
          },
          "requestedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The staff member who asked a member for the visit, when one did",
            "x-column": "requested_by_name"
          },
          "project": {
            "$ref": "#/components/schemas/ProjectRef",
            "description": "The project, enough to name and route it"
          },
          "drawNumber": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The number of the draw it was ordered on (\"Draw 4\"); null for a periodic visit"
          },
          "linkedDrawIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Further draws citing this visit as shared evidence"
          },
          "progress": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProgressLineItem"
            },
            "description": "What the visit observed per budget line, recorded at review"
          }
        },
        "required": [
          "id",
          "projectId",
          "status",
          "origin",
          "createdAt",
          "project",
          "linkedDrawIds",
          "progress"
        ],
        "x-graph-label": "Inspection",
        "x-table": "project_inspections",
        "x-redaction": "INSPECTION_STAFF_ONLY"
      },
      "ProgressLineItem": {
        "type": "object",
        "description": "What one inspection observed on one budget line: the percent complete recorded at review.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The observation's id",
            "x-column": "id"
          },
          "budgetLineId": {
            "type": "string",
            "description": "The budget line observed (BudgetLineItem.id)",
            "x-column": "budget_line_id"
          },
          "progress": {
            "type": "number",
            "description": "Percent complete observed, 0–100",
            "x-column": "progress"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the observation was recorded",
            "x-column": "created_at"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it last changed",
            "x-column": "updated_at"
          }
        },
        "required": [
          "id",
          "budgetLineId",
          "progress"
        ],
        "x-graph-label": "ProgressLineItem",
        "x-table": "project_inspection_progress"
      },
      "Document": {
        "type": "object",
        "description": "A file the lender may read — a released risk report, a draw's documents, a reviewed inspection's photos, the lender's own uploads — with a short-lived download link.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The document's id",
            "x-column": "id"
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "The project",
            "x-column": "project_id"
          },
          "drawId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The draw it is attached to, if any",
            "x-column": "draw_id"
          },
          "inspectionId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The inspection it is attached to, if any",
            "x-column": "inspection_id"
          },
          "name": {
            "type": "string",
            "description": "The file name",
            "x-column": "name"
          },
          "mime": {
            "type": [
              "string",
              "null"
            ],
            "description": "The MIME type",
            "x-column": "mime_type"
          },
          "size": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Bytes",
            "x-column": "size"
          },
          "parseStatus": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending",
              "parsing",
              "completed",
              "failed",
              "skipped"
            ],
            "description": "Where the document is in text extraction; a photo is skipped",
            "x-column": "parse_status"
          },
          "uploadedByKind": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "staff",
              "member",
              "lender",
              "inspector",
              "system",
              "other"
            ],
            "description": "Who uploaded it: staff, a portal member, the lender, an inspector, or the system",
            "x-column": "uploaded_by_kind"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it was uploaded",
            "x-column": "created_at"
          },
          "takenAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "A photo's shutter time, which can be hours before the upload; null for a document",
            "x-column": "taken_at"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the record last changed",
            "x-column": "updated_at"
          },
          "uploadedByName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who uploaded it, by name",
            "x-column": "uploaded_by_name"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "A signed download link, good for a few minutes; fetch the document again for a fresh one"
          },
          "urlExpiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the link stops working"
          }
        },
        "required": [
          "id",
          "projectId",
          "name",
          "createdAt"
        ],
        "x-graph-label": "Document",
        "x-table": "files"
      },
      "UploadSlot": {
        "type": "object",
        "description": "Where to put a document's bytes. PUT the file to uploadUrl with the headers given; the document row lands when the bytes do and is handled as a portal upload is.",
        "properties": {
          "documentId": {
            "type": "string",
            "description": "The id the document will have"
          },
          "uploadUrl": {
            "type": "string",
            "format": "uri",
            "description": "A signed single-use URL to PUT the bytes to"
          },
          "method": {
            "type": "string",
            "enum": [
              "PUT"
            ],
            "description": "Always PUT"
          },
          "headers": {
            "type": "object",
            "description": "Headers to send with the PUT, as given"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the slot closes unused"
          }
        },
        "required": [
          "documentId",
          "uploadUrl",
          "method",
          "headers",
          "expiresAt"
        ]
      },
      "RiskReport": {
        "type": "object",
        "description": "The released risk report for a project: the stored PDF, released while the project's approval stands.",
        "properties": {
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "The project"
          },
          "approvedAt": {
            "type": "string",
            "format": "date-time",
            "description": "The approval the report belongs to"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "A signed download link, good for a few minutes"
          },
          "urlExpiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the link stops working"
          }
        },
        "required": [
          "projectId",
          "approvedAt",
          "url",
          "urlExpiresAt"
        ]
      },
      "ProjectCreate": {
        "type": "object",
        "description": "A new project on the lender's book: the fields the portal's New project form takes, less the contract amount and the square footage, which are set inside the platform (a body carrying contractAmount or sqft is refused; both are read on Project). Everything else — the lender, the stage, the lifecycle — is set on the server. Upload documents to the project after creating it.",
        "properties": {
          "streetAddress": {
            "type": "string",
            "description": "Street address of the property"
          },
          "city": {
            "type": "string",
            "description": "City"
          },
          "state": {
            "type": "string",
            "description": "US state, full name (Maine)"
          },
          "zipCode": {
            "type": "string",
            "description": "ZIP code"
          },
          "borrower": {
            "type": [
              "string",
              "null"
            ],
            "description": "The borrower's name"
          },
          "builder": {
            "type": [
              "string",
              "null"
            ],
            "description": "The builder (general contractor)'s name"
          },
          "constructionType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "New Construction",
              "Renovation",
              "Modular",
              "Commercial",
              "Manufactured",
              "Modular / Manufactured"
            ],
            "description": "The construction type"
          },
          "departmentId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The department the project is filed under: the key's own, or the lender's only one. Optional — send it to assert the department, or leave it out. A key not bound to a department at a lender with two or more cannot create projects; bind the key first"
          },
          "parties": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/PartyInput"
            },
            "description": "The parties to seed; the borrower and the builder at least"
          }
        },
        "required": [
          "streetAddress",
          "city",
          "state",
          "zipCode"
        ]
      },
      "PartyInput": {
        "type": "object",
        "description": "A party to add at creation.",
        "properties": {
          "role": {
            "type": "string",
            "x-known-values": [
              "borrower",
              "builder"
            ],
            "description": "borrower or builder (another word is accepted and shown as given)"
          },
          "partyKind": {
            "type": "string",
            "enum": [
              "company",
              "household"
            ],
            "description": "company or household"
          },
          "companyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The company (empty for a household)"
          },
          "contactName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The primary contact's name"
          },
          "contactEmail": {
            "type": [
              "string",
              "null"
            ],
            "description": "The primary contact's email"
          },
          "contactPhone": {
            "type": [
              "string",
              "null"
            ],
            "description": "The primary contact's phone"
          },
          "people": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Person"
            },
            "description": "Further contact people"
          }
        },
        "required": [
          "role",
          "partyKind"
        ]
      },
      "IntakePatch": {
        "type": "object",
        "description": "Intake fields to change on a project that has not closed. Send only the fields to change; a field sent as null is cleared. The contract amount and the square footage are set inside the platform and cannot be changed here (a body carrying contractAmount or sqft is refused; both are read on Project). Refused with conflict once the project is active.",
        "properties": {
          "streetAddress": {
            "type": [
              "string",
              "null"
            ],
            "description": "Street address of the property"
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "City"
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "description": "US state, full name"
          },
          "zipCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "ZIP code"
          },
          "borrower": {
            "type": [
              "string",
              "null"
            ],
            "description": "The borrower's name"
          },
          "builder": {
            "type": [
              "string",
              "null"
            ],
            "description": "The builder's name"
          },
          "constructionType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "New Construction",
              "Renovation",
              "Modular",
              "Commercial",
              "Manufactured",
              "Modular / Manufactured"
            ],
            "description": "The construction type"
          }
        }
      },
      "DocumentInput": {
        "type": "object",
        "description": "What an upload slot needs.",
        "properties": {
          "name": {
            "type": "string",
            "description": "The file name, with its extension"
          },
          "mime": {
            "type": "string",
            "description": "The MIME type"
          },
          "size": {
            "type": "integer",
            "description": "Bytes"
          },
          "drawId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Attach to this draw"
          }
        },
        "required": [
          "name",
          "mime",
          "size"
        ]
      }
    }
  },
  "x-drawserv-scopes": {
    "projects:read": "Read projects, parties, budgets and released risk reports",
    "draws:read": "Read draws, their lines, approvals and lien waivers",
    "inspections:read": "Read reviewed inspections and their progress lines",
    "documents:read": "Read document metadata and fetch short-lived download links",
    "projects:write": "Create projects and update a pre-closing project's intake fields",
    "documents:write": "Upload documents to a project"
  },
  "x-drawserv-default-scopes": [
    "projects:read",
    "draws:read",
    "inspections:read",
    "documents:read"
  ],
  "x-drawserv-error-codes": {
    "unauthorized": "The key is missing, malformed, revoked, expired, for the other environment, or a session token was sent",
    "forbidden_scope": "The key lacks the scope the operation needs",
    "not_found": "No such record in the caller's view — a record outside the key's lender or department reads as absent",
    "validation_failed": "A parameter or body field is missing or malformed",
    "rate_limited": "The key's request budget is spent; Retry-After says when to try again",
    "conflict": "The write collides with the record's current state (a project no longer pre-closing)",
    "internal": "Something failed on our side; the requestId names the ledger row to quote"
  },
  "x-drawserv-phases": {
    "P1": "the read door",
    "P2": "writes and documents"
  },
  "x-drawserv-served-phases": [
    "P1",
    "P2"
  ],
  "x-drawserv-schema-revision": 16
}
