{
  "openapi": "3.1.0",
  "info": {
    "title": "BuildWorkPro API",
    "version": "1.0.0",
    "description": "Construction project management, CRM, and workflows — public REST API.",
    "contact": {
      "name": "BuildWorkPro Developers",
      "email": "developers@buildworkpro.com"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://buildworkpro.com/terms"
    }
  },
  "servers": [
    {
      "url": "https://app.buildworkpro.com/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "schemas": {
      "ScopeId": {
        "type": "string",
        "enum": [
          "contacts:read",
          "contacts:write",
          "contacts:delete",
          "leads:read",
          "leads:write",
          "leads:delete",
          "projects:read",
          "projects:write",
          "projects:delete",
          "bids:read",
          "bids:write",
          "bids:delete",
          "pay_apps:read",
          "pay_apps:write",
          "pay_apps:delete",
          "change_orders:read",
          "change_orders:write",
          "change_orders:delete",
          "site_logs:read",
          "site_logs:write",
          "site_logs:delete",
          "time_entries:read",
          "time_entries:write",
          "time_entries:delete",
          "products:read",
          "products:write",
          "products:delete",
          "documents:read",
          "documents:write",
          "documents:delete",
          "invoices:read",
          "invoices:write",
          "invoices:delete",
          "projects:reassign",
          "bids:send",
          "bids:accept_reject",
          "pay_apps:approve",
          "pay_apps:mark_paid",
          "pay_apps:send",
          "change_orders:approve",
          "change_orders:send",
          "invoices:send",
          "invoices:mark_paid",
          "invoices:void",
          "users:read",
          "comments:read",
          "comments:write",
          "comments:delete",
          "webhooks:manage",
          "jobs:read",
          "jobs:write",
          "notifications:read",
          "notifications:write",
          "messaging:read",
          "messaging:send",
          "support:read",
          "support:write",
          "reports:read",
          "signatures:read",
          "signatures:send",
          "audit:read",
          "settings:read",
          "settings:write",
          "dispatch:read",
          "dispatch:write",
          "offline_access",
          "platform_admin:read"
        ],
        "description": "Available API scopes"
      },
      "ErrorCode": {
        "type": "string",
        "enum": [
          "bad_request",
          "unauthorized",
          "token_expired",
          "forbidden",
          "scope_insufficient",
          "subscription_inactive",
          "tenant_suspended",
          "not_found",
          "conflict",
          "idempotency_key_reused",
          "validation_failed",
          "rate_limited",
          "internal_error",
          "service_unavailable",
          "maintenance"
        ],
        "description": "Catalog of error codes returned by the API"
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "URL identifying the problem type"
          },
          "title": {
            "type": "string",
            "description": "Short human-readable summary"
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code"
          },
          "detail": {
            "type": "string",
            "description": "Human-readable explanation"
          },
          "request_id": {
            "type": "string",
            "description": "Server-generated request identifier"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string",
                  "description": "Field path that failed validation"
                },
                "code": {
                  "type": "string",
                  "description": "Validation error code"
                },
                "message": {
                  "type": "string",
                  "description": "Human-readable validation message"
                }
              },
              "required": [
                "field",
                "code",
                "message"
              ]
            },
            "description": "Validation error array (422 only)"
          }
        },
        "required": [
          "type",
          "title",
          "status"
        ]
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page; null when no more results"
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether more pages are available after this one"
          },
          "count": {
            "type": "integer",
            "description": "Items in this page"
          }
        },
        "required": [
          "cursor",
          "has_more",
          "count"
        ]
      },
      "ResponseMeta": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "description": "Server-generated request identifier"
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMeta"
          }
        }
      },
      "Contact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "firstName": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastName": {
            "type": [
              "string",
              "null"
            ]
          },
          "companyName": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string"
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "firstName",
          "lastName",
          "companyName",
          "email",
          "phone",
          "type",
          "createdAt",
          "updatedAt"
        ]
      },
      "CreateContact": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "minLength": 1
          },
          "lastName": {
            "type": "string",
            "minLength": 1
          },
          "companyName": {
            "type": "string",
            "minLength": 1
          },
          "position": {
            "type": [
              "string",
              "null"
            ]
          },
          "parentContactId": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "mobile": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "customer",
              "general_contractor",
              "architect",
              "city",
              "hoa",
              "vendor",
              "subcontractor",
              "supplier",
              "employee",
              "partner",
              "other"
            ]
          }
        },
        "required": [
          "type"
        ]
      },
      "UpdateContact": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "minLength": 1
          },
          "lastName": {
            "type": "string",
            "minLength": 1
          },
          "companyName": {
            "type": "string",
            "minLength": 1
          },
          "position": {
            "type": [
              "string",
              "null"
            ]
          },
          "parentContactId": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "mobile": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "customer",
              "general_contractor",
              "architect",
              "city",
              "hoa",
              "vendor",
              "subcontractor",
              "supplier",
              "employee",
              "partner",
              "other"
            ]
          }
        }
      },
      "Lead": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "contactId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "stageId": {
            "type": "integer"
          },
          "assignedTo": {
            "type": [
              "integer",
              "null"
            ]
          },
          "source": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectType": {
            "type": [
              "string",
              "null"
            ]
          },
          "estimatedValue": {
            "type": [
              "string",
              "null"
            ]
          },
          "probability": {
            "type": [
              "integer",
              "null"
            ]
          },
          "expectedCloseDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteAddress": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteCity": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteState": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteZipCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "name",
          "contactId",
          "stageId",
          "assignedTo",
          "source",
          "projectType",
          "estimatedValue",
          "probability",
          "expectedCloseDate",
          "description",
          "createdAt",
          "updatedAt"
        ]
      },
      "CreateLead": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "stageId": {
            "type": "integer"
          },
          "contactId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "assignedTo": {
            "type": [
              "integer",
              "null"
            ]
          },
          "source": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "residential",
              "commercial",
              "industrial"
            ]
          },
          "estimatedValue": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "probability": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "expectedCloseDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteAddress": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteCity": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255
          },
          "siteState": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100
          },
          "siteZipCode": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          }
        },
        "required": [
          "name",
          "stageId"
        ]
      },
      "UpdateLead": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "stageId": {
            "type": "integer"
          },
          "contactId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "assignedTo": {
            "type": [
              "integer",
              "null"
            ]
          },
          "source": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "residential",
              "commercial",
              "industrial"
            ]
          },
          "estimatedValue": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "probability": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "expectedCloseDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteAddress": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteCity": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255
          },
          "siteState": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100
          },
          "siteZipCode": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          }
        }
      },
      "LeadStage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "color": {
            "type": [
              "string",
              "null"
            ]
          },
          "order": {
            "type": "integer"
          },
          "isDefault": {
            "type": "boolean"
          },
          "isWon": {
            "type": "boolean"
          },
          "isLost": {
            "type": "boolean"
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "name",
          "color",
          "order",
          "isDefault",
          "isWon",
          "isLost",
          "createdAt"
        ]
      },
      "CreateLeadStage": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "color": {
            "type": [
              "string",
              "null"
            ]
          },
          "order": {
            "type": "integer"
          },
          "isDefault": {
            "type": "boolean"
          },
          "isWon": {
            "type": "boolean"
          },
          "isLost": {
            "type": "boolean"
          }
        },
        "required": [
          "name"
        ]
      },
      "UpdateLeadStage": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "color": {
            "type": [
              "string",
              "null"
            ]
          },
          "order": {
            "type": "integer"
          },
          "isDefault": {
            "type": "boolean"
          },
          "isWon": {
            "type": "boolean"
          },
          "isLost": {
            "type": "boolean"
          }
        }
      },
      "MoveResult": {
        "type": "object",
        "properties": {
          "fromProjectId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "toProjectId": {
            "type": "integer"
          },
          "cleared": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "fromProjectId",
          "toProjectId",
          "cleared"
        ]
      },
      "SweepResult": {
        "type": "object",
        "properties": {
          "moved": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "dropped": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          }
        },
        "required": [
          "moved",
          "dropped"
        ]
      },
      "AttachedRecords": {
        "type": "object",
        "properties": {
          "counts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "total": {
            "type": "integer"
          },
          "invoiceTotal": {
            "type": "string"
          }
        },
        "required": [
          "counts",
          "total",
          "invoiceTotal"
        ]
      },
      "Project": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "projectNumber": {
            "type": "string"
          },
          "contactId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "bidId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "projectType": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "priority": {
            "type": [
              "string",
              "null"
            ]
          },
          "startDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "expectedEndDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "actualEndDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "contractValue": {
            "type": [
              "string",
              "null"
            ]
          },
          "originalContractValue": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteAddress": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteCity": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteState": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteZipCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "architectName": {
            "type": [
              "string",
              "null"
            ]
          },
          "architectProjectNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "contractFor": {
            "type": [
              "string",
              "null"
            ]
          },
          "assignedTo": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "isPractice": {
            "type": "boolean",
            "description": "Read-only. Present on GET /projects/{id}. True for the onboarding practice project a user starts from the setup wizard — demo content that \"Clear sample data\" removes; false for every other project."
          }
        },
        "required": [
          "id",
          "name",
          "projectNumber",
          "contactId",
          "bidId",
          "projectType",
          "status",
          "priority",
          "startDate",
          "expectedEndDate",
          "actualEndDate",
          "contractValue",
          "originalContractValue",
          "description",
          "siteAddress",
          "assignedTo",
          "createdAt",
          "updatedAt"
        ]
      },
      "CreateProject": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "projectNumber": {
            "type": "string"
          },
          "contactId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "projectType": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "planning",
              "in_progress",
              "on_hold",
              "closeout",
              "completed",
              "cancelled"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ]
          },
          "startDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "expectedEndDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "actualEndDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "originalContractValue": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "contractValue": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteAddress": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteCity": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255
          },
          "siteState": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100
          },
          "siteZipCode": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "architectName": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Architect or engineer of record; printed in the pay application's \"Via architect / engineer\" cell."
          },
          "architectProjectNumber": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "The architect's own project number, printed on the pay application and its continuation sheet."
          },
          "contractFor": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Scope line printed in the pay application's \"Contract for\" cell (e.g. \"Structural steel — Phase 2\")."
          },
          "assignedTo": {
            "type": [
              "integer",
              "null"
            ]
          },
          "templateId": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Optional project template ID. The project is created with the template's phases, tasks and checklist subtasks already in place. Create only — PATCH ignores it."
          }
        },
        "required": [
          "name"
        ]
      },
      "UpdateProject": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "projectNumber": {
            "type": "string"
          },
          "contactId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "projectType": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "planning",
              "in_progress",
              "on_hold",
              "closeout",
              "completed",
              "cancelled"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ]
          },
          "startDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "expectedEndDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "actualEndDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "originalContractValue": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "contractValue": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteAddress": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteCity": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255
          },
          "siteState": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100
          },
          "siteZipCode": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "architectName": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Architect or engineer of record; printed in the pay application's \"Via architect / engineer\" cell."
          },
          "architectProjectNumber": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "The architect's own project number, printed on the pay application and its continuation sheet."
          },
          "contractFor": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Scope line printed in the pay application's \"Contract for\" cell (e.g. \"Structural steel — Phase 2\")."
          },
          "assignedTo": {
            "type": [
              "integer",
              "null"
            ]
          },
          "bidId": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "description": "Link an existing bid to this project (both sides), or `null` to unlink. Requires the `bids:write` scope too. Refused with 409 when the bid already belongs to another live project or this project already has a different bid — unlink first. Linking an accepted bid fills the original contract (pre-tax) and site address only if the project has none."
          }
        }
      },
      "BidLineItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "bidId": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "item",
              "section",
              "note"
            ],
            "description": "item = priced line; section = header grouping the rows after it; note = customer-visible text."
          },
          "isOptional": {
            "type": "boolean",
            "description": "An optional (alternate) priced line, quoted outside the bid total."
          },
          "markName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-friendly mark name, e.g. W-01. Null on section and note rows."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "type": [
              "string",
              "null"
            ],
            "description": "Quantity. Sections and notes are not priced."
          },
          "unitPrice": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pre-markup direct cost per unit (before margin, overhead, commission, and distributed project costs). Not the customer price — use customerUnitPrice."
          },
          "total": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pre-markup line cost. Summing these does not equal the bid subtotal. Use customerTotal."
          },
          "customerUnitPrice": {
            "type": [
              "string",
              "null"
            ],
            "description": "Customer-facing unit price after markup and this line's share of distributed costs. Null on section and note rows. An optional line is priced on its own and is excluded from the bid subtotal."
          },
          "customerTotal": {
            "type": [
              "string",
              "null"
            ],
            "description": "Customer-facing line total in dollars, reconciled to the cent. Null on section and note rows. The sum over priced, non-optional lines equals the bid's pre-discount subtotal."
          },
          "sellTaxPercent": {
            "type": [
              "string",
              "null"
            ],
            "description": "Per-line sales-tax rate (0–100). Null inherits the bid's tax rate; 0 exempts the line."
          },
          "buyTaxPercent": {
            "type": [
              "string",
              "null"
            ],
            "description": "Per-line purchase-tax rate (0–100) on a flat line's cost. Null inherits the product's rate."
          },
          "order": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Sort position within the bid. A section covers every following row until the next section."
          }
        },
        "required": [
          "id",
          "bidId",
          "kind",
          "isOptional",
          "markName",
          "description",
          "quantity",
          "unitPrice",
          "total",
          "customerUnitPrice",
          "customerTotal",
          "sellTaxPercent",
          "buyTaxPercent",
          "order"
        ]
      },
      "Bid": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "bidNumber": {
            "type": "string"
          },
          "contactId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "leadId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "projectId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "subtotal": {
            "type": [
              "string",
              "null"
            ]
          },
          "taxAmount": {
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "type": [
              "string",
              "null"
            ]
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BidLineItem"
            },
            "description": "Present on GET /bids/{id}. unitPrice and total are pre-markup cost; customerUnitPrice and customerTotal are the customer-facing prices. Omitted from list responses."
          },
          "discountAmount": {
            "type": [
              "string",
              "null"
            ]
          },
          "taxRate": {
            "type": [
              "string",
              "null"
            ]
          },
          "discount": {
            "type": [
              "string",
              "null"
            ]
          },
          "discountType": {
            "type": [
              "string",
              "null"
            ]
          },
          "validUntil": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "terms": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteAddress": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteCity": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteState": {
            "type": [
              "string",
              "null"
            ]
          },
          "siteZipCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "sentAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "acceptedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "acceptedBy": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "isPractice": {
            "type": "boolean",
            "description": "Read-only. Present on GET /bids/{id}. True for the onboarding practice bid a user sends to themselves; it becomes a normal bid (false) once it is sent to anyone outside the organization."
          },
          "proposalDetail": {
            "type": "string",
            "enum": [
              "total",
              "line_items",
              "full"
            ],
            "description": "What the customer sees: total (a scope paragraph and the totals, no line table), line_items (the line table, no sub-lines) or full (the line table with each line's sub-lines)."
          },
          "proposalShowQuantities": {
            "type": "boolean",
            "description": "Whether the proposal prints the Qty column."
          },
          "proposalShowUnitPrices": {
            "type": "boolean",
            "description": "Whether the proposal prints the Unit price column."
          },
          "showSublines": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Deprecated: equals proposalDetail = full. Read proposalDetail instead; writing it alone maps to full or line_items."
          }
        },
        "required": [
          "id",
          "bidNumber",
          "contactId",
          "leadId",
          "projectId",
          "status",
          "subtotal",
          "taxAmount",
          "total",
          "taxRate",
          "discount",
          "discountType",
          "validUntil",
          "notes",
          "terms",
          "siteAddress",
          "sentAt",
          "acceptedAt",
          "acceptedBy",
          "createdAt",
          "updatedAt",
          "proposalDetail",
          "proposalShowQuantities",
          "proposalShowUnitPrices"
        ]
      },
      "PayApp": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "payAppNumber": {
            "type": "string"
          },
          "projectId": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "periodStart": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "periodEnd": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "contractSum": {
            "type": [
              "string",
              "null"
            ]
          },
          "changeOrdersTotal": {
            "type": [
              "string",
              "null"
            ]
          },
          "revisedContractSum": {
            "type": [
              "string",
              "null"
            ]
          },
          "previouslyBilled": {
            "type": [
              "string",
              "null"
            ]
          },
          "currentBilled": {
            "type": [
              "string",
              "null"
            ]
          },
          "materialsStored": {
            "type": [
              "string",
              "null"
            ]
          },
          "totalCompleted": {
            "type": [
              "string",
              "null"
            ]
          },
          "retainagePercent": {
            "type": [
              "string",
              "null"
            ]
          },
          "retainageAmount": {
            "type": [
              "string",
              "null"
            ]
          },
          "currentPaymentDue": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "includeNotary": {
            "type": "boolean",
            "description": "Print the notary acknowledgment block on the pay application PDF. Defaults to true."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "payAppNumber",
          "projectId",
          "status",
          "periodStart",
          "periodEnd",
          "contractSum",
          "changeOrdersTotal",
          "revisedContractSum",
          "previouslyBilled",
          "currentBilled",
          "materialsStored",
          "totalCompleted",
          "retainagePercent",
          "retainageAmount",
          "currentPaymentDue",
          "createdAt",
          "updatedAt"
        ]
      },
      "Invoice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "tenantId": {
            "type": "integer"
          },
          "invoiceNumber": {
            "type": "string"
          },
          "projectId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "contactId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "sent",
              "partial",
              "paid",
              "voided"
            ]
          },
          "issueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "subtotal": {
            "type": "string"
          },
          "taxAmount": {
            "type": "string"
          },
          "total": {
            "type": "string"
          },
          "amountPaid": {
            "type": "string"
          },
          "balanceDue": {
            "type": "string"
          },
          "notesToCustomer": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "tenantId",
          "invoiceNumber",
          "projectId",
          "contactId",
          "status",
          "issueDate",
          "dueDate",
          "subtotal",
          "taxAmount",
          "total",
          "amountPaid",
          "balanceDue",
          "notesToCustomer",
          "createdAt",
          "updatedAt"
        ]
      },
      "InvoiceLineItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "invoiceId": {
            "type": "integer"
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": "string"
          },
          "unitPrice": {
            "type": "string"
          },
          "amount": {
            "type": "string"
          },
          "order": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "invoiceId",
          "description",
          "quantity",
          "unitPrice",
          "amount",
          "order"
        ]
      },
      "InvoicePayment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "invoiceId": {
            "type": "integer"
          },
          "amount": {
            "type": "string"
          },
          "paidAt": {
            "type": "string",
            "format": "date-time"
          },
          "method": {
            "type": [
              "string",
              "null"
            ]
          },
          "reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "voidedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "invoiceId",
          "amount",
          "paidAt",
          "method",
          "reference",
          "notes",
          "voidedAt"
        ]
      },
      "Notification": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "tenantId": {
            "type": "integer"
          },
          "userId": {
            "type": "integer"
          },
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          },
          "entityType": {
            "type": [
              "string",
              "null"
            ]
          },
          "entityId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "readAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "tenantId",
          "userId",
          "type",
          "title",
          "message",
          "entityType",
          "entityId",
          "readAt",
          "createdAt"
        ]
      },
      "ChangeOrder": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "changeOrderNumber": {
            "type": "string"
          },
          "projectId": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "totalAmount": {
            "type": [
              "string",
              "null"
            ]
          },
          "approvedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "approvedBy": {
            "type": [
              "string",
              "null"
            ]
          },
          "rejectedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "rejectionReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "changeOrderNumber",
          "projectId",
          "title",
          "description",
          "status",
          "reason",
          "totalAmount",
          "approvedAt",
          "approvedBy",
          "rejectedAt",
          "rejectionReason",
          "createdAt",
          "updatedAt"
        ]
      },
      "SiteLog": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "projectId": {
            "type": "integer"
          },
          "logDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "personnelCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "workPerformed": {
            "type": [
              "string",
              "null"
            ]
          },
          "materialsReceived": {
            "type": [
              "string",
              "null"
            ]
          },
          "visitorsOnSite": {
            "type": [
              "string",
              "null"
            ]
          },
          "issues": {
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "lat": {
            "type": [
              "number",
              "null"
            ]
          },
          "lng": {
            "type": [
              "number",
              "null"
            ]
          },
          "isReviewed": {
            "type": "boolean"
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "projectId",
          "logDate",
          "title",
          "notes",
          "personnelCount",
          "workPerformed",
          "materialsReceived",
          "visitorsOnSite",
          "issues",
          "tags",
          "lat",
          "lng",
          "isReviewed",
          "createdAt",
          "updatedAt"
        ]
      },
      "TimeEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "projectId": {
            "type": "integer"
          },
          "userId": {
            "type": "integer"
          },
          "taskId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "phaseId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "hours": {
            "type": "string"
          },
          "hourlyRate": {
            "type": [
              "string",
              "null"
            ]
          },
          "totalCost": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "lat": {
            "type": [
              "number",
              "null"
            ]
          },
          "lng": {
            "type": [
              "number",
              "null"
            ]
          },
          "isReviewed": {
            "type": "boolean"
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "projectId",
          "userId",
          "taskId",
          "phaseId",
          "date",
          "hours",
          "hourlyRate",
          "totalCost",
          "description",
          "lat",
          "lng",
          "isReviewed",
          "createdAt",
          "updatedAt"
        ]
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "sku": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string"
          },
          "categoryId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "costPrice": {
            "type": [
              "string",
              "null"
            ],
            "description": "Unit cost in dollars. For a kit (isKit: true) this is calculated from its components and read-only: a value sent on create or update is ignored."
          },
          "sellPrice": {
            "type": [
              "string",
              "null"
            ],
            "description": "Unit sell price in dollars. For a kit (isKit: true) this is calculated from its components and read-only: a value sent on create or update is ignored."
          },
          "unit": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "manufacturerCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "isKit": {
            "type": "boolean"
          },
          "isActive": {
            "type": "boolean"
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "sku",
          "name",
          "description",
          "type",
          "categoryId",
          "costPrice",
          "sellPrice",
          "unit",
          "manufacturerId",
          "manufacturerCode",
          "isKit",
          "isActive",
          "createdAt",
          "updatedAt"
        ]
      },
      "ProductKitItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "kitProductId": {
            "type": "integer"
          },
          "componentProductId": {
            "type": "integer"
          },
          "defaultQuantity": {
            "type": [
              "string",
              "null"
            ]
          },
          "overrideCostPrice": {
            "type": [
              "string",
              "null"
            ]
          },
          "sortOrder": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "kitProductId",
          "componentProductId",
          "defaultQuantity",
          "overrideCostPrice",
          "sortOrder"
        ]
      },
      "Document": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "entityType": {
            "type": "string"
          },
          "entityId": {
            "type": "integer"
          },
          "originalFilename": {
            "type": "string"
          },
          "storagePath": {
            "type": "string"
          },
          "mimeType": {
            "type": "string"
          },
          "fileSize": {
            "type": "integer"
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "entityType",
          "entityId",
          "originalFilename",
          "storagePath",
          "mimeType",
          "fileSize",
          "category",
          "description",
          "createdAt",
          "updatedAt"
        ]
      },
      "CalendarEvent": {
        "type": "object",
        "properties": {
          "id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              }
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "task",
              "phase",
              "project",
              "lead",
              "activity",
              "bid"
            ]
          },
          "title": {
            "type": "string"
          },
          "start": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "end": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "projectId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "assignedTo": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "type",
          "title",
          "start",
          "end"
        ]
      },
      "SearchResult": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "project",
              "contact",
              "site_log",
              "document",
              "bid",
              "lead",
              "invoice",
              "pay_app",
              "change_order"
            ],
            "description": "Entity type of the match."
          },
          "id": {
            "type": "integer",
            "description": "Id of the matched record; fetch it from that type's own endpoint."
          },
          "title": {
            "type": "string",
            "description": "Display title (name, number or filename)."
          },
          "subtitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Short secondary text — email, project number, or the start of the matched body. Null when there is none."
          },
          "projectId": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Parent project for change orders, pay apps, site logs and invoices; null when the record has none. Absent for other types."
          }
        },
        "required": [
          "type",
          "id",
          "title",
          "subtitle"
        ]
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "entityType": {
            "type": "string"
          },
          "entityId": {
            "type": "integer"
          },
          "userId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "content": {
            "type": "string"
          },
          "parentId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "entityType",
          "entityId",
          "userId",
          "content",
          "parentId",
          "createdAt",
          "updatedAt"
        ]
      },
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "tenantUserId": {
            "type": "integer"
          },
          "firstName": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastName": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "username": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "isActive": {
            "type": "boolean"
          },
          "joinedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "tenantUserId",
          "firstName",
          "lastName",
          "email",
          "username",
          "role",
          "isActive",
          "joinedAt"
        ]
      },
      "Me": {
        "type": "object",
        "properties": {
          "keyId": {
            "type": [
              "integer",
              "null"
            ],
            "description": "API key id when the caller is an API key. Null for an OAuth access token."
          },
          "keyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "API key name when the caller is an API key. Null for an OAuth access token."
          },
          "tenantId": {
            "type": "integer",
            "description": "Organization this credential is bound to."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Scopes granted to this credential."
          }
        },
        "required": [
          "keyId",
          "keyName",
          "tenantId",
          "scopes"
        ]
      },
      "ProjectTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "createdAt",
          "updatedAt"
        ]
      },
      "BrandingSettings": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "tenantId": {
            "type": "integer"
          },
          "companyName": {
            "type": [
              "string",
              "null"
            ]
          },
          "primaryColor": {
            "type": [
              "string",
              "null"
            ]
          },
          "accentColor": {
            "type": [
              "string",
              "null"
            ]
          },
          "hasLogo": {
            "type": "boolean"
          }
        }
      },
      "DataRetentionSettings": {
        "type": "object",
        "properties": {
          "auditLogRetentionDays": {
            "type": [
              "integer",
              "null"
            ]
          },
          "activityLogRetentionDays": {
            "type": [
              "integer",
              "null"
            ]
          },
          "notificationRetentionDays": {
            "type": [
              "integer",
              "null"
            ]
          },
          "deletedRecordsRetentionDays": {
            "type": [
              "integer",
              "null"
            ]
          },
          "exportRetentionDays": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "JobProgress": {
        "type": "object",
        "properties": {
          "processed": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "errorsCount": {
            "type": "integer"
          }
        },
        "required": [
          "processed",
          "total",
          "errorsCount"
        ]
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "resourceType": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "progress": {
            "$ref": "#/components/schemas/JobProgress"
          },
          "params": {},
          "result": {},
          "errorMessage": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "startedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "expiresAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "type",
          "resourceType",
          "status",
          "createdAt",
          "expiresAt"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "eventTypes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isActive": {
            "type": "boolean"
          },
          "consecutiveFailures": {
            "type": "integer"
          },
          "lastDeliveredAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "disabledAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "disabledReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "url",
          "description",
          "eventTypes",
          "isActive",
          "consecutiveFailures",
          "lastDeliveredAt",
          "disabledAt",
          "disabledReason",
          "createdAt",
          "updatedAt"
        ]
      },
      "WebhookEndpointWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "properties": {
              "signingSecret": {
                "type": "string"
              }
            },
            "required": [
              "signingSecret"
            ]
          }
        ]
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "eventId": {
            "type": "string"
          },
          "endpointId": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed",
              "exhausted"
            ]
          },
          "attemptCount": {
            "type": "integer"
          },
          "nextAttemptAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "lastAttemptedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "responseStatus": {
            "type": [
              "integer",
              "null"
            ]
          },
          "responseBody": {
            "type": [
              "string",
              "null"
            ]
          },
          "responseHeaders": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            }
          },
          "errorMessage": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "eventId",
          "endpointId",
          "status",
          "attemptCount",
          "nextAttemptAt",
          "lastAttemptedAt",
          "responseStatus",
          "responseBody",
          "responseHeaders",
          "errorMessage",
          "createdAt",
          "completedAt"
        ]
      },
      "WebhookDeliveryFull": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "eventId": {
            "type": "string"
          },
          "endpointId": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed",
              "exhausted"
            ]
          },
          "attemptCount": {
            "type": "integer"
          },
          "nextAttemptAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "lastAttemptedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "responseStatus": {
            "type": [
              "integer",
              "null"
            ]
          },
          "responseBody": {
            "type": [
              "string",
              "null"
            ]
          },
          "responseHeaders": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            }
          },
          "errorMessage": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "eventId",
          "endpointId",
          "status",
          "attemptCount",
          "nextAttemptAt",
          "lastAttemptedAt",
          "responseStatus",
          "responseBody",
          "responseHeaders",
          "errorMessage",
          "createdAt",
          "completedAt"
        ]
      },
      "ApiAuditLog": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "tenantId": {
            "type": "integer"
          },
          "apiKeyId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "oauthTokenId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "userId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "method": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "durationMs": {
            "type": "integer"
          },
          "requestId": {
            "type": "string"
          },
          "ip": {
            "type": [
              "string",
              "null"
            ]
          },
          "userAgent": {
            "type": [
              "string",
              "null"
            ]
          },
          "errorCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          }
        },
        "required": [
          "id",
          "tenantId",
          "apiKeyId",
          "oauthTokenId",
          "userId",
          "method",
          "path",
          "status",
          "durationMs",
          "requestId",
          "ip",
          "userAgent",
          "errorCode",
          "createdAt"
        ]
      },
      "UsageResponse": {
        "type": "object",
        "properties": {
          "tenant": {
            "type": "object",
            "properties": {
              "limit": {
                "type": "integer"
              },
              "used": {
                "type": "integer"
              },
              "remaining": {
                "type": "integer"
              },
              "resetSeconds": {
                "type": "integer"
              },
              "windowSeconds": {
                "type": "integer"
              }
            },
            "required": [
              "limit",
              "used",
              "remaining",
              "resetSeconds",
              "windowSeconds"
            ],
            "description": "Tenant-wide cost-weighted budget (sliding hour window)"
          },
          "apiKey": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "limit": {
                "type": "integer"
              },
              "used": {
                "type": "integer"
              },
              "remaining": {
                "type": "integer"
              },
              "resetSeconds": {
                "type": "integer"
              },
              "windowSeconds": {
                "type": "integer"
              }
            },
            "required": [
              "limit",
              "used",
              "remaining",
              "resetSeconds",
              "windowSeconds"
            ],
            "description": "Per-API-key request limit (sliding minute window). Null when the caller is OAuth/refresh-token authenticated (not an API key)."
          }
        },
        "required": [
          "tenant",
          "apiKey"
        ]
      },
      "SignatureRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "entityType": {
            "type": "string",
            "enum": [
              "bid",
              "change_order"
            ]
          },
          "entityId": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "signerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "signerEmail": {
            "type": [
              "string",
              "null"
            ]
          },
          "sentAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "signedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "signedDocumentId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "signingUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present only for callers holding signatures:send, and only while the request is pending or sent."
          }
        },
        "required": [
          "id",
          "entityType",
          "entityId",
          "status",
          "signerName",
          "signerEmail",
          "sentAt",
          "signedAt",
          "signedDocumentId"
        ]
      },
      "CrewAssignment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "projectId": {
            "type": "integer"
          },
          "userId": {
            "type": "integer"
          },
          "date": {
            "type": "string"
          },
          "startTime": {
            "type": [
              "string",
              "null"
            ]
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdBy": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "projectName": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "userFirstName": {
            "type": [
              "string",
              "null"
            ]
          },
          "userLastName": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "projectId",
          "userId",
          "date",
          "startTime",
          "note",
          "createdBy",
          "createdAt",
          "updatedAt",
          "projectName",
          "projectNumber",
          "userFirstName",
          "userLastName"
        ]
      },
      "BriefingDismissal": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "itemKey": {
            "type": "string",
            "description": "`<category>:<entityType>:<entityId>`, as on the briefing row."
          },
          "dismissedUntil": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Snooze end, or null until the row's state changes."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              }
            ]
          },
          "deletedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "itemKey",
          "dismissedUntil",
          "createdAt",
          "updatedAt",
          "deletedAt"
        ]
      },
      "OauthClientRegistration": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          },
          "client_id_issued_at": {
            "type": "integer"
          },
          "client_name": {
            "type": "string"
          },
          "client_secret_expires_at": {
            "type": "integer"
          },
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "grant_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scope": {
            "type": "string"
          },
          "token_endpoint_auth_method": {
            "type": "string",
            "enum": [
              "none",
              "client_secret_basic",
              "client_secret_post"
            ]
          }
        },
        "required": [
          "client_id",
          "client_id_issued_at",
          "client_name",
          "redirect_uris",
          "grant_types",
          "scope",
          "token_endpoint_auth_method"
        ]
      },
      "OauthTokenResponse": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expires_in": {
            "type": "integer"
          },
          "refresh_token": {
            "type": "string"
          },
          "scope": {
            "type": "string"
          }
        },
        "required": [
          "access_token",
          "token_type",
          "expires_in",
          "scope"
        ]
      },
      "OauthUserinfo": {
        "type": "object",
        "properties": {
          "sub": {
            "type": "string",
            "description": "Stringified user id of the resource owner.",
            "example": "42"
          },
          "tenant_id": {
            "type": "integer",
            "description": "Tenant the access token is scoped to.",
            "example": 7
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address of the resource owner.",
            "example": "jane@example.com"
          },
          "name": {
            "type": "string",
            "description": "`firstName lastName`, trimmed. Empty string when both names are missing — never null.",
            "example": "Jane Doe"
          },
          "role": {
            "type": "string",
            "description": "The user's tenant role at the moment the request was authenticated.",
            "example": "company_admin"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The scopes carried by the presented access token.",
            "example": [
              "contacts:read",
              "leads:read"
            ]
          }
        },
        "required": [
          "sub",
          "tenant_id",
          "email",
          "name",
          "role",
          "scopes"
        ]
      },
      "SentBid": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Bid"
          },
          {
            "type": "object",
            "properties": {
              "signatureRequested": {
                "type": "boolean",
                "description": "True when this send created an e-signature request."
              },
              "signatureRequestId": {
                "type": "integer",
                "description": "Id of the signature request created by this send. Absent when signatureRequested is false. Never a signing URL or recipient token."
              }
            },
            "required": [
              "signatureRequested"
            ]
          }
        ]
      },
      "SendBidBody": {
        "type": "object",
        "properties": {
          "to": {
            "anyOf": [
              {
                "type": "string",
                "format": "email"
              },
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "email"
                },
                "minItems": 1,
                "maxItems": 20
              }
            ]
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            },
            "maxItems": 20
          },
          "templateId": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "subject": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "customMessage": {
            "type": "string",
            "maxLength": 5000
          },
          "requestSignature": {
            "type": "boolean",
            "default": false,
            "description": "When true, this send also starts an e-signature and the email includes the signing link. Defaults to false. Also requires the signatures:send scope and a signer name."
          },
          "signerEmail": {
            "type": "string",
            "maxLength": 320,
            "format": "email",
            "description": "Signer email. Defaults to the bid contact's email, then the first To address. Must be one of the recipients."
          },
          "signerName": {
            "type": "string",
            "maxLength": 255,
            "description": "Signer name. Required when requestSignature is true."
          }
        }
      },
      "ShareBidBody": {
        "type": "object",
        "properties": {
          "rotate": {
            "type": "boolean",
            "description": "When true, replace any existing public link. Otherwise reuse a live link."
          }
        }
      },
      "ProjectBillingFigures": {
        "type": "object",
        "properties": {
          "invoiced": {
            "type": "number"
          },
          "invoicedPreTax": {
            "type": "number"
          },
          "paid": {
            "type": "number"
          },
          "outstanding": {
            "type": "number"
          },
          "invoiceCount": {
            "type": "integer"
          },
          "payAppBilled": {
            "type": "number"
          },
          "billedTotal": {
            "type": "number"
          },
          "billedCash": {
            "type": "number"
          },
          "outstandingCash": {
            "type": "number"
          },
          "retainageHeld": {
            "type": "number"
          },
          "remaining": {
            "type": "number"
          },
          "hasBilling": {
            "type": "boolean"
          }
        },
        "required": [
          "invoiced",
          "invoicedPreTax",
          "paid",
          "outstanding",
          "invoiceCount",
          "payAppBilled",
          "billedTotal",
          "billedCash",
          "outstandingCash",
          "retainageHeld",
          "remaining",
          "hasBilling"
        ]
      },
      "InvoiceBillingSourceResult": {
        "type": "object",
        "properties": {
          "invoice": {
            "$ref": "#/components/schemas/Invoice"
          },
          "before": {
            "type": "object",
            "properties": {
              "sourcePayAppId": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "paymentTermInstallmentId": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "sourceBidId": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "sourceType": {
                "type": "string",
                "enum": [
                  "bid",
                  "pay_app",
                  "ad_hoc"
                ]
              }
            },
            "required": [
              "sourcePayAppId",
              "paymentTermInstallmentId",
              "sourceBidId",
              "sourceType"
            ]
          },
          "after": {
            "type": "object",
            "properties": {
              "sourcePayAppId": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "paymentTermInstallmentId": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "sourceBidId": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "sourceType": {
                "type": "string",
                "enum": [
                  "bid",
                  "pay_app",
                  "ad_hoc"
                ]
              }
            },
            "required": [
              "sourcePayAppId",
              "paymentTermInstallmentId",
              "sourceBidId",
              "sourceType"
            ]
          },
          "changed": {
            "type": "boolean"
          },
          "dryRun": {
            "type": "boolean"
          },
          "billing": {
            "type": "object",
            "properties": {
              "before": {
                "$ref": "#/components/schemas/ProjectBillingFigures"
              },
              "after": {
                "$ref": "#/components/schemas/ProjectBillingFigures"
              }
            },
            "required": [
              "before",
              "after"
            ]
          }
        },
        "required": [
          "invoice",
          "before",
          "after",
          "changed",
          "dryRun",
          "billing"
        ]
      },
      "OauthTokenError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "error_description": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "OauthTokenRequest": {
        "type": "object",
        "properties": {
          "grant_type": {
            "type": "string",
            "description": "OAuth 2.1 grant type. Supported: authorization_code, refresh_token.",
            "example": "authorization_code"
          },
          "code": {
            "type": "string",
            "description": "Authorization code (required for grant_type=authorization_code)."
          },
          "redirect_uri": {
            "type": "string",
            "description": "Must exact-match the redirect_uri originally used to obtain the code."
          },
          "client_id": {
            "type": "string",
            "description": "OAuth client identifier issued by the registration endpoint."
          },
          "client_secret": {
            "type": "string",
            "description": "Required for confidential clients; MUST be omitted for public clients."
          },
          "code_verifier": {
            "type": "string",
            "description": "PKCE verifier (S256). Required for grant_type=authorization_code."
          },
          "refresh_token": {
            "type": "string",
            "description": "Refresh token (required for grant_type=refresh_token)."
          },
          "scope": {
            "type": "string",
            "description": "Optional space-separated scope list. For grant_type=refresh_token, MUST be a subset of the original refresh token's scopes (scope downgrade only). Upgrade is rejected with invalid_scope."
          }
        },
        "required": [
          "grant_type",
          "client_id"
        ]
      },
      "OauthRevokeError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "error_description": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "OauthRevokeRequest": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "The token to revoke. Can be either an access token (prefix bwp_at_) or a refresh token (prefix bwp_rt_)."
          },
          "token_type_hint": {
            "type": "string",
            "enum": [
              "access_token",
              "refresh_token"
            ],
            "description": "Optional hint about the token type. The server still falls back to the other type if the hinted lookup misses (RFC 7009 §4.1.2)."
          },
          "client_id": {
            "type": "string",
            "description": "OAuth client identifier issued by the registration endpoint."
          },
          "client_secret": {
            "type": "string",
            "description": "Required for confidential clients; MUST be omitted for public clients."
          }
        },
        "required": [
          "token",
          "client_id"
        ]
      },
      "OauthRegistrationError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "error_description": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "OauthClientRegistrationRequest": {
        "type": "object",
        "properties": {
          "client_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "minItems": 1,
            "maxItems": 10
          },
          "grant_types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "authorization_code",
                "refresh_token"
              ]
            },
            "default": [
              "authorization_code",
              "refresh_token"
            ]
          },
          "scope": {
            "type": "string",
            "default": ""
          },
          "token_endpoint_auth_method": {
            "type": "string",
            "enum": [
              "none",
              "client_secret_basic",
              "client_secret_post"
            ],
            "default": "none"
          },
          "contact": {
            "type": "string",
            "format": "email"
          }
        },
        "required": [
          "client_name",
          "redirect_uris"
        ]
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "bwp_live_... or bwp_test_...",
        "description": "Long-lived tenant-wide API key. Provision in Settings → Developer → API Keys."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 per-user token. Available in Phase 4.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.buildworkpro.com/api/v1/oauth/authorize",
            "tokenUrl": "https://app.buildworkpro.com/api/v1/oauth/token",
            "refreshUrl": "https://app.buildworkpro.com/api/v1/oauth/token",
            "scopes": {}
          }
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Idempotent retry key for mutating operations. The first request with a given key executes normally and BuildWorkPro caches the response. Replaying the same key with the same request body (method + path + JSON body, SHA-256 hashed) returns the cached response without re-executing the mutation. Replaying with a DIFFERENT body returns `409 idempotency_key_reused`. The cache TTL is 24 hours; after expiry the key is treated as a new operation. Keys are tenant-scoped. Recommended: a fresh UUID v4 per logical operation.",
        "schema": {
          "type": "string",
          "format": "uuid",
          "maxLength": 255
        }
      }
    }
  },
  "paths": {
    "/contacts": {
      "get": {
        "summary": "List contacts",
        "description": "Returns a cursor-paginated list of contacts for the authenticated tenant.",
        "security": [
          {
            "bearerAuth": [
              "contacts:read"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "type",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Contacts page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getContacts",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create contact",
        "description": "Creates a new contact.",
        "security": [
          {
            "bearerAuth": [
              "contacts:write"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateContact"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postContacts",
        "x-rate-limit-cost": 5
      }
    },
    "/contacts/get-many": {
      "post": {
        "summary": "Bulk-fetch contacts by ID",
        "description": "Fetch up to 50 contacts by ID in a single round-trip. Returns rows for IDs that exist in the tenant — IDs that don't exist (or belong to another tenant) are silently omitted. Compare `meta.requested` to `meta.returned` to detect missing.",
        "security": [
          {
            "bearerAuth": [
              "contacts:read"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 50
                  }
                },
                "required": [
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contacts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ResponseMeta"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "requested": {
                              "type": "integer"
                            },
                            "returned": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "requested",
                            "returned"
                          ]
                        }
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postContactsGetMany",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/contacts/count": {
      "get": {
        "summary": "Count contacts",
        "description": "Returns the total number of contacts in the tenant matching the optional `search`, `type`, `dateFrom`/`dateTo` filters. Mirrors the same filters as `GET /contacts` minus pagination — use this when you only need cardinality and don't want to walk every page.",
        "security": [
          {
            "bearerAuth": [
              "contacts:read"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "type",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "total"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getContactsCount",
        "x-rate-limit-cost": 1
      }
    },
    "/contacts/summary": {
      "get": {
        "summary": "Aggregate contact totals and breakdown by type",
        "description": "Returns the total contact count plus a breakdown by type (customer / vendor / subcontractor / ...). Date filter applies to createdAt and is inclusive on both bounds. Use this instead of contacts list + walk-and-count when you only need roll-ups.",
        "security": [
          {
            "bearerAuth": [
              "contacts:read"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Contact summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "totals": {
                          "type": "object",
                          "properties": {
                            "count": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "count"
                          ]
                        },
                        "by_type": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "type",
                              "count"
                            ]
                          }
                        }
                      },
                      "required": [
                        "totals",
                        "by_type"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getContactsSummary",
        "x-rate-limit-cost": 1
      }
    },
    "/contacts/{id}": {
      "get": {
        "summary": "Get contact by id",
        "description": "Returns a single contact.",
        "security": [
          {
            "bearerAuth": [
              "contacts:read"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Contact",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getContactsById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update contact",
        "description": "Partially updates a contact.",
        "security": [
          {
            "bearerAuth": [
              "contacts:write"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateContact"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchContactsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete contact",
        "description": "Soft-deletes a contact.",
        "security": [
          {
            "bearerAuth": [
              "contacts:delete"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteContactsById",
        "x-rate-limit-cost": 5
      }
    },
    "/contacts/exports": {
      "post": {
        "summary": "Start CSV export of contacts",
        "description": "Enqueues a CSV export job for contacts. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "contacts:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postContactsExports",
        "x-rate-limit-cost": 20
      }
    },
    "/contacts/imports": {
      "post": {
        "summary": "Start CSV import of contacts",
        "description": "Enqueues a CSV import job. sourcePath must be an opaque upload path returned by POST /jobs/uploads.",
        "security": [
          {
            "bearerAuth": [
              "contacts:write",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourcePath": {
                    "type": "string"
                  },
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "updateInPlace": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "sourcePath",
                  "mapping"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postContactsImports",
        "x-rate-limit-cost": 200
      }
    },
    "/contacts/bulk": {
      "post": {
        "summary": "Start bulk operation on contacts",
        "description": "Enqueues a bulk delete/restore job for up to 500 contact ids.",
        "security": [
          {
            "bearerAuth": [
              "contacts:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postContactsBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/leads/summary": {
      "get": {
        "summary": "Aggregate lead totals and breakdowns by stage and source",
        "description": "Returns flat totals (count, estimated_value_total) plus a breakdown by pipeline stage (with stage name joined from lead_stages) and a breakdown by source. `totals.count` counts every matching lead; `totals.estimated_value_total` and each `by_source[].estimated_value_total` are the OPEN pipeline value — leads in a Won or Lost stage are excluded, matching the app's Pipeline Value. `by_stage[]` reports each stage's own sum (a Won/Lost bucket carries its closed value) and flags closed stages with `isWon` / `isLost`. Null source becomes its own bucket. Money values are decimal strings. Date filter applies to createdAt.",
        "security": [
          {
            "bearerAuth": [
              "leads:read"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "assignedTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "stageId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Lead summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "totals": {
                          "type": "object",
                          "properties": {
                            "count": {
                              "type": "integer"
                            },
                            "estimated_value_total": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "count",
                            "estimated_value_total"
                          ]
                        },
                        "by_stage": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "stageId": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "stageName": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "isWon": {
                                "type": [
                                  "boolean",
                                  "null"
                                ]
                              },
                              "isLost": {
                                "type": [
                                  "boolean",
                                  "null"
                                ]
                              },
                              "count": {
                                "type": "integer"
                              },
                              "estimated_value_total": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "stageId",
                              "stageName",
                              "isWon",
                              "isLost",
                              "count",
                              "estimated_value_total"
                            ]
                          }
                        },
                        "by_source": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "source": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "count": {
                                "type": "integer"
                              },
                              "estimated_value_total": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "source",
                              "count",
                              "estimated_value_total"
                            ]
                          }
                        }
                      },
                      "required": [
                        "totals",
                        "by_stage",
                        "by_source"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getLeadsSummary",
        "x-rate-limit-cost": 1
      }
    },
    "/leads/count": {
      "get": {
        "summary": "Count leads",
        "description": "Returns the total number of leads in the tenant matching the optional `search`, `stageId`, `assignedTo`, `dateFrom`/`dateTo`, `updatedFrom` filters. Mirrors GET /leads minus pagination.",
        "security": [
          {
            "bearerAuth": [
              "leads:read"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "stageId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "assignedTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "updatedFrom",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "total"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getLeadsCount",
        "x-rate-limit-cost": 1
      }
    },
    "/leads": {
      "get": {
        "summary": "List leads",
        "description": "Returns a cursor-paginated list of leads for the authenticated tenant.",
        "security": [
          {
            "bearerAuth": [
              "leads:read"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "stageId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "assignedTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "updatedFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Leads page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Lead"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getLeads",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create lead",
        "description": "Creates a new lead.",
        "security": [
          {
            "bearerAuth": [
              "leads:write"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLead"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Lead"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postLeads",
        "x-rate-limit-cost": 5
      }
    },
    "/leads/{id}": {
      "get": {
        "summary": "Get lead by id",
        "description": "Returns a single lead.",
        "security": [
          {
            "bearerAuth": [
              "leads:read"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Lead",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Lead"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getLeadsById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update lead",
        "description": "Partially updates a lead.",
        "security": [
          {
            "bearerAuth": [
              "leads:write"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateLead"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Lead"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchLeadsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete lead",
        "description": "Soft-deletes a lead.",
        "security": [
          {
            "bearerAuth": [
              "leads:delete"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteLeadsById",
        "x-rate-limit-cost": 5
      }
    },
    "/leads/{id}/convert": {
      "post": {
        "summary": "Convert lead to project",
        "description": "Promotes a lead to a project and moves the lead to the Won stage. When the lead has an accepted bid, the project is linked to that bid and its original contract is the bid's pre-tax total; if that bid already has a project, that project is returned (200) instead of creating a duplicate. Rejects with 409 if the lead is already in the Won stage.",
        "security": [
          {
            "bearerAuth": [
              "leads:write",
              "projects:write"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The lead's accepted bid already had a project; that project is returned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {},
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "meta"
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Project created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {},
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postLeadsByIdConvert",
        "x-rate-limit-cost": 5
      }
    },
    "/lead-stages": {
      "get": {
        "summary": "List lead stages",
        "security": [
          {
            "bearerAuth": [
              "leads:read"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "responses": {
          "200": {
            "description": "Stages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LeadStage"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getLeadStages",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create lead stage",
        "security": [
          {
            "bearerAuth": [
              "leads:write"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLeadStage"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LeadStage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postLeadStages",
        "x-rate-limit-cost": 5
      }
    },
    "/lead-stages/{id}": {
      "patch": {
        "summary": "Update lead stage",
        "security": [
          {
            "bearerAuth": [
              "leads:write"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateLeadStage"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LeadStage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchLeadStagesById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete lead stage",
        "security": [
          {
            "bearerAuth": [
              "leads:delete"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteLeadStagesById",
        "x-rate-limit-cost": 5
      }
    },
    "/leads/exports": {
      "post": {
        "summary": "Start CSV export of leads",
        "description": "Enqueues a CSV export job for leads. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "leads:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postLeadsExports",
        "x-rate-limit-cost": 20
      }
    },
    "/leads/imports": {
      "post": {
        "summary": "Start CSV import of leads",
        "description": "Enqueues a CSV import job. sourcePath must be an opaque upload path returned by POST /jobs/uploads.",
        "security": [
          {
            "bearerAuth": [
              "leads:write",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourcePath": {
                    "type": "string"
                  },
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "updateInPlace": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "sourcePath",
                  "mapping"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postLeadsImports",
        "x-rate-limit-cost": 200
      }
    },
    "/leads/bulk": {
      "post": {
        "summary": "Start bulk operation on leads",
        "description": "Enqueues a bulk delete/restore job for up to 500 lead ids.",
        "security": [
          {
            "bearerAuth": [
              "leads:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Leads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postLeadsBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/invoices/{id}/move": {
      "post": {
        "summary": "Move a invoice to another project",
        "description": "Re-points the invoice at `targetProjectId`, which must belong to your organization and must not be deleted. Needs `projects:reassign` rather than this resource's own write scope. QuickBooks is deliberately NOT rewritten: the QBO invoice keeps the customer it was pushed with, so BuildWorkPro and QuickBooks disagree about the owning job until someone reconciles them by hand. The invoice's `qboId` is left intact, and any payment-term installment link is cleared.",
        "security": [
          {
            "bearerAuth": [
              "projects:reassign"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "targetProjectId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  }
                },
                "required": [
                  "targetProjectId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Moved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MoveResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdMove",
        "x-rate-limit-cost": 5
      }
    },
    "/change-orders/{id}/move": {
      "post": {
        "summary": "Move a change order to another project",
        "description": "Re-points the change order at `targetProjectId`, which must belong to your organization and must not be deleted. Needs `projects:reassign` rather than this resource's own write scope.",
        "security": [
          {
            "bearerAuth": [
              "projects:reassign"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "targetProjectId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  }
                },
                "required": [
                  "targetProjectId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Moved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MoveResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrdersByIdMove",
        "x-rate-limit-cost": 5
      }
    },
    "/site-logs/{id}/move": {
      "post": {
        "summary": "Move a site log to another project",
        "description": "Re-points the site log at `targetProjectId`, which must belong to your organization and must not be deleted. Needs `projects:reassign` rather than this resource's own write scope.",
        "security": [
          {
            "bearerAuth": [
              "projects:reassign"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "targetProjectId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  }
                },
                "required": [
                  "targetProjectId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Moved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MoveResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postSiteLogsByIdMove",
        "x-rate-limit-cost": 5
      }
    },
    "/time-entries/{id}/move": {
      "post": {
        "summary": "Move a time entry to another project",
        "description": "Re-points the time entry at `targetProjectId`, which must belong to your organization and must not be deleted. Needs `projects:reassign` rather than this resource's own write scope. Its task and phase links are cleared, because those belong to the old project.",
        "security": [
          {
            "bearerAuth": [
              "projects:reassign"
            ]
          }
        ],
        "tags": [
          "TimeEntries"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "targetProjectId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  }
                },
                "required": [
                  "targetProjectId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Moved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MoveResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postTimeEntriesByIdMove",
        "x-rate-limit-cost": 5
      }
    },
    "/projects/{id}/attached-records": {
      "get": {
        "summary": "Count the records still attached to a project",
        "description": "What a sweep would touch, broken down by record type, with the total invoiced value. Answers with an empty summary rather than 404 when nothing is attached. Needs only `projects:read`: this is the count that warns a caller before deleting a project, and deleting does not require the reassignment scope.",
        "security": [
          {
            "bearerAuth": [
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AttachedRecords"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProjectsByIdAttachedRecords",
        "x-rate-limit-cost": 1
      }
    },
    "/projects/{id}/archived": {
      "get": {
        "summary": "Read a soft-deleted project",
        "description": "The ordinary project endpoints hide deleted rows. This one returns a deleted project read-only, so records stranded on it can be identified before they are moved.",
        "security": [
          {
            "bearerAuth": [
              "projects:reassign"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Archived project",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {},
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProjectsByIdArchived",
        "x-rate-limit-cost": 1
      }
    },
    "/projects/{id}/reassign-records": {
      "post": {
        "summary": "Move every record off a project",
        "description": "Sweeps all reassignable records from this project onto `targetProjectId` in one operation — the tool for a project deleted by mistake. `dropped` counts records the policy could not carry across (pay apps stay put: their certificates are a cumulative per-project chain). QuickBooks is never rewritten.",
        "security": [
          {
            "bearerAuth": [
              "projects:reassign"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "targetProjectId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  }
                },
                "required": [
                  "targetProjectId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Swept",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SweepResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProjectsByIdReassignRecords",
        "x-rate-limit-cost": 5
      }
    },
    "/projects/count": {
      "get": {
        "summary": "Count projects",
        "description": "Returns the total number of projects in the tenant matching the optional `search`, `status`, `contactId`, `dateFrom`/`dateTo`, `updatedFrom` filters. Mirrors GET /projects minus pagination.",
        "security": [
          {
            "bearerAuth": [
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "contactId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "updatedFrom",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "total"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProjectsCount",
        "x-rate-limit-cost": 1
      }
    },
    "/projects/summary": {
      "get": {
        "summary": "Aggregate project totals and breakdowns by status and type",
        "description": "Returns totals (count, contract_value_total, approved_co_delta_total, overdue_count) plus breakdowns by status and by project type. Money values are decimal strings. Date filter applies to createdAt and is inclusive on both bounds.",
        "security": [
          {
            "bearerAuth": [
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "contactId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "assignedTo",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Project summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "totals": {
                          "type": "object",
                          "properties": {
                            "count": {
                              "type": "integer"
                            },
                            "contract_value_total": {
                              "type": "string"
                            },
                            "approved_co_delta_total": {
                              "type": "string"
                            },
                            "overdue_count": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "count",
                            "contract_value_total",
                            "approved_co_delta_total",
                            "overdue_count"
                          ]
                        },
                        "by_status": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "status": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              },
                              "contract_value_total": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "status",
                              "count",
                              "contract_value_total"
                            ]
                          }
                        },
                        "by_type": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "project_type": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "count": {
                                "type": "integer"
                              },
                              "contract_value_total": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "project_type",
                              "count",
                              "contract_value_total"
                            ]
                          }
                        }
                      },
                      "required": [
                        "totals",
                        "by_status",
                        "by_type"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProjectsSummary",
        "x-rate-limit-cost": 1
      }
    },
    "/projects/get-many": {
      "post": {
        "summary": "Bulk-fetch projects by ID",
        "description": "Fetch up to 50 projects by ID in a single round-trip. Returns flat project rows only (no phases / tasks / contacts). IDs that don't exist are silently omitted; compare meta.requested vs meta.returned to detect missing.",
        "security": [
          {
            "bearerAuth": [
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 50
                  }
                },
                "required": [
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Projects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ResponseMeta"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "requested": {
                              "type": "integer"
                            },
                            "returned": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "requested",
                            "returned"
                          ]
                        }
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProjectsGetMany",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/projects": {
      "get": {
        "summary": "List projects",
        "description": "Returns a cursor-paginated list of projects for the authenticated tenant.",
        "security": [
          {
            "bearerAuth": [
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "contactId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "updatedFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Projects page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProjects",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create project",
        "description": "Creates a new project.",
        "security": [
          {
            "bearerAuth": [
              "projects:write"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProject"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Project"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProjects",
        "x-rate-limit-cost": 5
      }
    },
    "/projects/{id}": {
      "get": {
        "summary": "Get project by id",
        "description": "Returns a single project.",
        "security": [
          {
            "bearerAuth": [
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Project",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Project"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProjectsById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update project",
        "description": "Partially updates a project. Passing `bidId` links (or, with `null`, unlinks) an existing bid — see the field.",
        "security": [
          {
            "bearerAuth": [
              "projects:write"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProject"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Project"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient, or no access to the project or bid",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "The bid is linked to another live project, or this project already has a different bid",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchProjectsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete project",
        "description": "Soft-deletes a project.",
        "security": [
          {
            "bearerAuth": [
              "projects:delete"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteProjectsById",
        "x-rate-limit-cost": 5
      }
    },
    "/projects/exports": {
      "post": {
        "summary": "Start CSV export of projects",
        "description": "Enqueues a CSV export job for projects. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "projects:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProjectsExports",
        "x-rate-limit-cost": 20
      }
    },
    "/projects/imports": {
      "post": {
        "summary": "Start CSV import of projects",
        "description": "Enqueues a CSV import job. sourcePath must be an opaque upload path returned by POST /jobs/uploads.",
        "security": [
          {
            "bearerAuth": [
              "projects:write",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourcePath": {
                    "type": "string"
                  },
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "updateInPlace": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "sourcePath",
                  "mapping"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProjectsImports",
        "x-rate-limit-cost": 200
      }
    },
    "/projects/bulk": {
      "post": {
        "summary": "Start bulk operation on projects",
        "description": "Enqueues a bulk delete/restore job for up to 500 project ids.",
        "security": [
          {
            "bearerAuth": [
              "projects:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProjectsBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/bids": {
      "get": {
        "summary": "List bids",
        "description": "Returns a cursor-paginated list of bids for the authenticated tenant.",
        "security": [
          {
            "bearerAuth": [
              "bids:read"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "contactId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "leadId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "updatedFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Bids page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Bid"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getBids",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create bid",
        "description": "Creates a new bid in draft status.",
        "security": [
          {
            "bearerAuth": [
              "bids:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "bidNumber": {
                    "type": "string"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 255
                  },
                  "leadId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "contactId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "dueDate": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "validUntil": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "rejectedAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "cancelReason": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "cancelledAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "cancelledFromStatus": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 50
                  },
                  "taxRate": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Bid-level sales tax rate. A percent, not a fraction: 7 means 7% (send 7, not 0.07). 0-100, at most 3 decimal places."
                  },
                  "discountType": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 20
                  },
                  "discount": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Discount amount, or a percent when discountType is \"percentage\". At most 2 decimal places."
                  },
                  "siteAddress": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "siteCity": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 255
                  },
                  "siteState": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 100
                  },
                  "siteZipCode": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 20
                  },
                  "latitude": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "longitude": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "terms": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "paymentTermsText": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "paymentTermId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "marginPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Margin as a percent of the selling price (18 means 18%). At most 2 decimal places."
                  },
                  "companyOhPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Company overhead as a percent of the selling price (10 means 10%). At most 2 decimal places."
                  },
                  "commissionPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Sales commission as a percent of the selling price. At most 2 decimal places."
                  },
                  "showSublines": {
                    "type": [
                      "boolean",
                      "null"
                    ]
                  },
                  "roundSalesAmounts": {
                    "type": [
                      "boolean",
                      "null"
                    ]
                  },
                  "proposalDetail": {
                    "type": "string",
                    "enum": [
                      "total",
                      "line_items",
                      "full"
                    ],
                    "description": "What the customer sees on the proposal PDF and share page: total (a scope paragraph and the totals, no line table), line_items (the line table, no sub-lines) or full (the line table with each line's sub-lines)."
                  },
                  "proposalShowQuantities": {
                    "type": "boolean",
                    "description": "Print the Qty column on the proposal."
                  },
                  "proposalShowUnitPrices": {
                    "type": "boolean",
                    "description": "Print the Unit price column on the proposal."
                  },
                  "kanbanOrder": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Bid"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBids",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}": {
      "get": {
        "summary": "Get bid by id",
        "description": "Returns a single bid, including its line items. Each line's unitPrice and total are the pre-markup direct cost; customerUnitPrice and customerTotal are the customer-facing prices. A soft-deleted bid is not found.",
        "security": [
          {
            "bearerAuth": [
              "bids:read"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Bid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Bid"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getBidsById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update bid",
        "description": "Partially updates a bid. Accepted bids are locked.",
        "security": [
          {
            "bearerAuth": [
              "bids:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "bidNumber": {
                    "type": "string"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 255
                  },
                  "leadId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "contactId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "dueDate": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "validUntil": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sentAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "acceptedAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "acceptedBy": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 255
                  },
                  "rejectedAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "cancelReason": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "cancelledAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "cancelledFromStatus": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 50
                  },
                  "taxRate": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Bid-level sales tax rate. A percent, not a fraction: 7 means 7% (send 7, not 0.07). 0-100, at most 3 decimal places."
                  },
                  "discountType": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 20
                  },
                  "discount": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Discount amount, or a percent when discountType is \"percentage\". At most 2 decimal places."
                  },
                  "siteAddress": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "siteCity": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 255
                  },
                  "siteState": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 100
                  },
                  "siteZipCode": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 20
                  },
                  "latitude": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "longitude": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "terms": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "paymentTermsText": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "paymentTermId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "marginPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Margin as a percent of the selling price (18 means 18%). At most 2 decimal places."
                  },
                  "companyOhPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Company overhead as a percent of the selling price (10 means 10%). At most 2 decimal places."
                  },
                  "commissionPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Sales commission as a percent of the selling price. At most 2 decimal places."
                  },
                  "showSublines": {
                    "type": [
                      "boolean",
                      "null"
                    ]
                  },
                  "roundSalesAmounts": {
                    "type": [
                      "boolean",
                      "null"
                    ]
                  },
                  "proposalDetail": {
                    "type": "string",
                    "enum": [
                      "total",
                      "line_items",
                      "full"
                    ],
                    "description": "What the customer sees on the proposal PDF and share page: total (a scope paragraph and the totals, no line table), line_items (the line table, no sub-lines) or full (the line table with each line's sub-lines)."
                  },
                  "proposalShowQuantities": {
                    "type": "boolean",
                    "description": "Print the Qty column on the proposal."
                  },
                  "proposalShowUnitPrices": {
                    "type": "boolean",
                    "description": "Print the Unit price column on the proposal."
                  },
                  "kanbanOrder": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Bid"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchBidsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete bid",
        "description": "Soft-deletes a bid. Accepted bids are locked.",
        "security": [
          {
            "bearerAuth": [
              "bids:delete"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteBidsById",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}/send": {
      "post": {
        "summary": "Send a bid to the customer",
        "description": "Emails a draft bid to the customer with the PDF attached, then marks it sent. `to` and `cc` are optional; when `to` is omitted the bid's contact email is used. If there is no recipient, the bid is left unchanged. A `validUntil` already in the past is moved to today plus the organization's default bid validity, so the bid is not re-expired the night it is sent. Set `requestSignature` to true to include a signing link in that same email. That also requires the `signatures:send` scope, a signer name, and e-signature to be enabled for the organization. The signer email defaults to the bid contact, then the first To address. The response includes `signatureRequested` and, when a request was created, `signatureRequestId`. It never includes a signing URL or recipient token. A soft-deleted bid is not found.",
        "security": [
          {
            "bearerAuth": [
              "bids:send"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendBidBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SentBid"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsByIdSend",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}/share": {
      "post": {
        "summary": "Create a public share link for a bid",
        "description": "Returns a `/b/:token` URL the customer can open without an account. Reuses a live link unless `rotate` is true. Draft and cancelled bids are refused — send the bid first. A soft-deleted bid is not found.",
        "security": [
          {
            "bearerAuth": [
              "bids:send"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShareBidBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Share link",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "url": {
                          "type": "string"
                        },
                        "shareTokenCreatedAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      },
                      "required": [
                        "url",
                        "shareTokenCreatedAt"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsByIdShare",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}/accept": {
      "post": {
        "summary": "Accept a bid",
        "description": "Marks any non-accepted bid (draft, sent, expired, or rejected) as accepted. Returns 409 if the bid is already accepted.",
        "security": [
          {
            "bearerAuth": [
              "bids:accept_reject"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "acceptedBy": {
                    "type": "string",
                    "minLength": 1
                  }
                },
                "required": [
                  "acceptedBy"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Bid"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsByIdAccept",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}/revert-acceptance": {
      "post": {
        "summary": "Revert acceptance of a bid",
        "description": "Moves an accepted bid back to a non-accepted status (draft, sent, expired, or rejected) and clears acceptedAt/acceptedBy. Use to undo an acceptance recorded by mistake.",
        "security": [
          {
            "bearerAuth": [
              "bids:accept_reject"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "sent",
                      "expired",
                      "rejected"
                    ]
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reverted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Bid"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsByIdRevertAcceptance",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}/reject": {
      "post": {
        "summary": "Reject a bid",
        "security": [
          {
            "bearerAuth": [
              "bids:accept_reject"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Rejected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Bid"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsByIdReject",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}/duplicate": {
      "post": {
        "summary": "Duplicate a bid",
        "description": "Creates a new draft bid copied from the given bid.",
        "security": [
          {
            "bearerAuth": [
              "bids:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "201": {
            "description": "Duplicated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Bid"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsByIdDuplicate",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}/convert-to-project": {
      "post": {
        "summary": "Convert accepted bid to project",
        "security": [
          {
            "bearerAuth": [
              "bids:write",
              "projects:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mappingStrategy": {
                    "type": "string",
                    "enum": [
                      "phases",
                      "tasks",
                      "blank",
                      "sections"
                    ],
                    "description": "How bid line items map onto the new project: one phase per line item (\"phases\"), one task per line item (\"tasks\"), no phases/tasks (\"blank\"), or one phase per section header with its lines as tasks (\"sections\"). The legacy web route's \"template\" strategy is not supported on v1."
                  },
                  "templateId": {
                    "type": "integer"
                  },
                  "lineItemPhaseMapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "integer"
                    }
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "startDate": {
                    "type": "string"
                  },
                  "expectedEndDate": {
                    "type": "string"
                  }
                },
                "required": [
                  "mappingStrategy"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Project created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {},
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsByIdConvertToProject",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}/line-items": {
      "get": {
        "summary": "List bid line items",
        "description": "Returns every line of the bid, including sections and notes. unitPrice and total are the pre-markup direct cost. customerUnitPrice and customerTotal are the customer-facing prices (null on sections and notes). The sum of customerTotal over priced, non-optional lines equals the bid's pre-discount subtotal. A soft-deleted bid is not found.",
        "security": [
          {
            "bearerAuth": [
              "bids:read"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Line items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BidLineItem"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getBidsByIdLineItems",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create bid line item",
        "security": [
          {
            "bearerAuth": [
              "bids:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "productId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "markName": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 100
                  },
                  "description": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "width": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "height": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "depth": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "area": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "perimeterLf": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "volumeCf": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "buyTaxPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Purchase-tax rate on the cost. A percent, not a fraction: 7 means 7% (send 7, not 0.07). 0-100, at most 3 decimal places."
                  },
                  "sellTaxPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Sales-tax rate for this line. null inherits the bid rate, 0 exempts. A percent, not a fraction: 7 means 7% (send 7, not 0.07). 0-100, at most 3 decimal places."
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "item",
                      "section",
                      "note"
                    ],
                    "description": "item = priced line; section = header grouping the rows after it; note = customer-visible text."
                  },
                  "isOptional": {
                    "type": "boolean",
                    "description": "An optional (alternate) priced line, quoted outside the bid total."
                  },
                  "order": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "unitPrice": {
                    "type": "string",
                    "description": "Pre-markup direct cost per unit for a flat line with no sublines. Not the customer price — read customerUnitPrice. Ignored once the line has sublines."
                  }
                },
                "required": [
                  "description"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BidLineItem"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsByIdLineItems",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/{id}/line-items/{itemId}": {
      "patch": {
        "summary": "Update bid line item",
        "security": [
          {
            "bearerAuth": [
              "bids:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "productId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "markName": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 100
                  },
                  "description": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "width": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "height": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "depth": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "area": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "perimeterLf": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "volumeCf": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "buyTaxPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Purchase-tax rate on the cost. A percent, not a fraction: 7 means 7% (send 7, not 0.07). 0-100, at most 3 decimal places."
                  },
                  "sellTaxPercent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Sales-tax rate for this line. null inherits the bid rate, 0 exempts. A percent, not a fraction: 7 means 7% (send 7, not 0.07). 0-100, at most 3 decimal places."
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "item",
                      "section",
                      "note"
                    ],
                    "description": "item = priced line; section = header grouping the rows after it; note = customer-visible text."
                  },
                  "isOptional": {
                    "type": "boolean",
                    "description": "An optional (alternate) priced line, quoted outside the bid total."
                  },
                  "order": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "unitPrice": {
                    "type": "string",
                    "description": "Pre-markup direct cost per unit for a flat line with no sublines. Not the customer price — read customerUnitPrice. Ignored once the line has sublines."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BidLineItem"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchBidsByIdLineItemsByItemId",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete bid line item",
        "security": [
          {
            "bearerAuth": [
              "bids:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteBidsByIdLineItemsByItemId",
        "x-rate-limit-cost": 5
      }
    },
    "/bids/exports": {
      "post": {
        "summary": "Start CSV export of bids",
        "description": "Enqueues a CSV export job for bids. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "bids:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsExports",
        "x-rate-limit-cost": 20
      }
    },
    "/bids/bulk": {
      "post": {
        "summary": "Start bulk operation on bids",
        "description": "Enqueues a bulk delete/restore job for up to 500 bid ids.",
        "security": [
          {
            "bearerAuth": [
              "bids:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/bids/get-many": {
      "post": {
        "summary": "Bulk-fetch bids by ID",
        "description": "Fetch up to 50 bids by ID in a single round-trip. Returns flat bid rows only (no line items / sublines / distributed costs — call GET /bids/{id} per id for the full tree). IDs that don't exist are silently omitted; compare meta.requested vs meta.returned to detect missing.",
        "security": [
          {
            "bearerAuth": [
              "bids:read"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 50
                  }
                },
                "required": [
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bids",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Bid"
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ResponseMeta"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "requested": {
                              "type": "integer"
                            },
                            "returned": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "requested",
                            "returned"
                          ]
                        }
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsGetMany",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/bids/count": {
      "get": {
        "summary": "Count bids",
        "description": "Returns the total number of bids in the tenant matching the optional filters (`search`, `status`, `contactId`, `leadId`, `projectId`, `dateFrom`/`dateTo`, `updatedFrom`). Mirrors GET /bids minus pagination.",
        "security": [
          {
            "bearerAuth": [
              "bids:read"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "contactId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "leadId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "updatedFrom",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "total"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getBidsCount",
        "x-rate-limit-cost": 1
      }
    },
    "/bids/summary": {
      "get": {
        "summary": "Aggregate bid totals and breakdown by status",
        "description": "Returns flat totals (count, total_value) plus a breakdown by status (draft / sent / accepted / rejected / expired). Money values are decimal strings. Filters on createdAt for date range.",
        "security": [
          {
            "bearerAuth": [
              "bids:read"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "contactId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "createdBy",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Bid summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "totals": {
                          "type": "object",
                          "properties": {
                            "count": {
                              "type": "integer"
                            },
                            "total_value": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "count",
                            "total_value"
                          ]
                        },
                        "by_status": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "status": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              },
                              "total_value": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "status",
                              "count",
                              "total_value"
                            ]
                          }
                        }
                      },
                      "required": [
                        "totals",
                        "by_status"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getBidsSummary",
        "x-rate-limit-cost": 1
      }
    },
    "/bids/{id}/pdf": {
      "post": {
        "summary": "Generate PDF for a bid (async)",
        "description": "Enqueues an async PDF generation job. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "bids:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postBidsByIdPdf",
        "x-rate-limit-cost": 50
      }
    },
    "/pay-apps/count": {
      "get": {
        "summary": "Count pay applications",
        "description": "Returns the total number of pay apps in the tenant matching the optional `search`, `status`, `projectId`, `dateFrom`/`dateTo` filters. Mirrors GET /pay-apps minus pagination.",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:read"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "total"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getPayAppsCount",
        "x-rate-limit-cost": 1
      }
    },
    "/pay-apps/summary": {
      "get": {
        "summary": "Aggregate pay-app totals and breakdown by status",
        "description": "Returns totals (count, invoiced_total, paid_total, outstanding_total) plus a breakdown by status. Money values are decimal strings. Date filter applies to submittedAt.",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:read"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Pay-app summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "totals": {
                          "type": "object",
                          "properties": {
                            "count": {
                              "type": "integer"
                            },
                            "invoiced_total": {
                              "type": "string"
                            },
                            "paid_total": {
                              "type": "string"
                            },
                            "outstanding_total": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "count",
                            "invoiced_total",
                            "paid_total",
                            "outstanding_total"
                          ]
                        },
                        "by_status": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "status": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              },
                              "current_billed_total": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "status",
                              "count",
                              "current_billed_total"
                            ]
                          }
                        }
                      },
                      "required": [
                        "totals",
                        "by_status"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getPayAppsSummary",
        "x-rate-limit-cost": 1
      }
    },
    "/pay-apps/get-many": {
      "post": {
        "summary": "Bulk-fetch pay apps by ID",
        "description": "Fetch up to 50 pay apps by ID in a single round-trip. Returns flat rows only (no line items / project / approver). IDs that don't exist are silently omitted.",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:read"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 50
                  }
                },
                "required": [
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "PayApps",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PayApp"
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ResponseMeta"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "requested": {
                              "type": "integer"
                            },
                            "returned": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "requested",
                            "returned"
                          ]
                        }
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayAppsGetMany",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/pay-apps": {
      "get": {
        "summary": "List pay applications",
        "description": "Returns a cursor-paginated list of pay apps for the authenticated tenant.",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:read"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "PayApps page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PayApp"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getPayApps",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create pay application (draft)",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:write"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "payAppNumber": {
                    "type": "string"
                  },
                  "projectId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "periodStart": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Calendar day, YYYY-MM-DD. A datetime is accepted and read as the day it names."
                  },
                  "periodEnd": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Calendar day, YYYY-MM-DD. A datetime is accepted and read as the day it names."
                  },
                  "contractSum": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "changeOrdersTotal": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "retainagePercent": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "includeNotary": {
                    "type": "boolean"
                  },
                  "changeOrders": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "number"
                      },
                      {
                        "type": "boolean"
                      },
                      {
                        "type": "null"
                      },
                      {
                        "type": "object",
                        "additionalProperties": {}
                      },
                      {
                        "type": "array",
                        "items": {}
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "voidedBy": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "voidedAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "voidReason": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sentAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sentBy": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "projectId"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PayApp"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayApps",
        "x-rate-limit-cost": 5
      }
    },
    "/pay-apps/{id}": {
      "get": {
        "summary": "Get pay application by id",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:read"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "PayApp",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PayApp"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getPayAppsById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update draft pay application",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:write"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "payAppNumber": {
                    "type": "string"
                  },
                  "projectId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "periodStart": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Calendar day, YYYY-MM-DD. A datetime is accepted and read as the day it names."
                  },
                  "periodEnd": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Calendar day, YYYY-MM-DD. A datetime is accepted and read as the day it names."
                  },
                  "contractSum": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "changeOrdersTotal": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "retainagePercent": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "includeNotary": {
                    "type": "boolean"
                  },
                  "changeOrders": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "number"
                      },
                      {
                        "type": "boolean"
                      },
                      {
                        "type": "null"
                      },
                      {
                        "type": "object",
                        "additionalProperties": {}
                      },
                      {
                        "type": "array",
                        "items": {}
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "voidedBy": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "voidedAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "voidReason": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sentAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sentBy": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PayApp"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchPayAppsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete draft pay application",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:delete"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deletePayAppsById",
        "x-rate-limit-cost": 5
      }
    },
    "/pay-apps/{id}/submit": {
      "post": {
        "summary": "Submit pay app for approval",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:write"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Submitted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PayApp"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayAppsByIdSubmit",
        "x-rate-limit-cost": 5
      }
    },
    "/pay-apps/{id}/approve": {
      "post": {
        "summary": "Approve pay application",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:approve"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Approved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PayApp"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayAppsByIdApprove",
        "x-rate-limit-cost": 5
      }
    },
    "/pay-apps/{id}/reject": {
      "post": {
        "summary": "Reject pay application",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:approve"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  }
                },
                "required": [
                  "reason"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rejected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PayApp"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayAppsByIdReject",
        "x-rate-limit-cost": 5
      }
    },
    "/pay-apps/{id}/mark-paid": {
      "post": {
        "summary": "Mark approved pay app as paid",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:mark_paid"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Paid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PayApp"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayAppsByIdMarkPaid",
        "x-rate-limit-cost": 5
      }
    },
    "/pay-apps/exports": {
      "post": {
        "summary": "Start CSV export of pay applications",
        "description": "Enqueues a CSV export job for pay apps. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayAppsExports",
        "x-rate-limit-cost": 20
      }
    },
    "/pay-apps/bulk": {
      "post": {
        "summary": "Start bulk operation on pay applications",
        "description": "Enqueues a bulk delete/restore job for up to 500 pay app ids.",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayAppsBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/pay-apps/{id}/pdf": {
      "post": {
        "summary": "Generate PDF for a pay application (async)",
        "description": "Enqueues an async PDF generation job. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayAppsByIdPdf",
        "x-rate-limit-cost": 50
      }
    },
    "/pay-apps/{id}/send": {
      "post": {
        "summary": "Email a pay application to the customer",
        "description": "Emails the pay application PDF to the given recipients and marks it sent. Same delivery pipeline as the product's send action. Requires pay_apps:send.",
        "security": [
          {
            "bearerAuth": [
              "pay_apps:send"
            ]
          }
        ],
        "tags": [
          "PayApps"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "templateId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  },
                  "recipientEmails": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "minItems": 1,
                    "maxItems": 20
                  },
                  "cc": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "maxItems": 20
                  },
                  "bcc": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "maxItems": 20
                  },
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "body": {
                    "type": "string",
                    "maxLength": 50000
                  }
                },
                "required": [
                  "recipientEmails"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "success": {
                          "type": "boolean"
                        },
                        "messageId": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "recipientCount": {
                          "type": "integer"
                        },
                        "payAppId": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "success",
                        "messageId",
                        "recipientCount",
                        "payAppId"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postPayAppsByIdSend",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices": {
      "get": {
        "summary": "List invoices",
        "description": "Returns a page of invoices for the authenticated tenant. This is the one v1 list endpoint that pages by `page`/`pageSize` rather than a cursor — the tenant UI's invoice table jumps to arbitrary pages, and that shape is mirrored here rather than reshaped. Page details are returned in `meta.pagination`. `status` accepts a comma-separated list.",
        "security": [
          {
            "bearerAuth": [
              "invoices:read"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "required": false,
            "name": "page",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "required": false,
            "name": "pageSize",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "contactId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dueBefore",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dueAfter",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "q",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Invoice page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ResponseMeta"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "pagination": {
                              "type": "object",
                              "properties": {
                                "page": {
                                  "type": "integer"
                                },
                                "pageSize": {
                                  "type": "integer"
                                },
                                "total": {
                                  "type": "integer"
                                }
                              },
                              "required": [
                                "page",
                                "pageSize",
                                "total"
                              ]
                            }
                          },
                          "required": [
                            "pagination"
                          ]
                        }
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getInvoices",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create an invoice",
        "description": "Creates a draft invoice together with its line items.",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "invoice": {
                    "type": "object",
                    "properties": {
                      "invoiceNumber": {
                        "type": "string"
                      },
                      "projectId": {
                        "type": "integer",
                        "minimum": -2147483648,
                        "maximum": 2147483647
                      },
                      "contactId": {
                        "type": "integer",
                        "minimum": -2147483648,
                        "maximum": 2147483647
                      },
                      "sourceBidId": {
                        "type": [
                          "integer",
                          "null"
                        ],
                        "minimum": -2147483648,
                        "maximum": 2147483647
                      },
                      "sourcePayAppId": {
                        "type": [
                          "integer",
                          "null"
                        ],
                        "minimum": -2147483648,
                        "maximum": 2147483647
                      },
                      "sourceType": {
                        "type": "string",
                        "enum": [
                          "bid",
                          "pay_app",
                          "ad_hoc"
                        ]
                      },
                      "issueDate": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "dueDate": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "taxRate": {
                        "type": "string"
                      },
                      "discountType": {
                        "type": "string",
                        "enum": [
                          "fixed",
                          "percentage"
                        ]
                      },
                      "discount": {
                        "type": "string"
                      },
                      "notesToCustomer": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "terms": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "paymentTermsText": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "paymentTermId": {
                        "type": [
                          "integer",
                          "null"
                        ],
                        "minimum": -2147483648,
                        "maximum": 2147483647
                      },
                      "paymentTermInstallmentId": {
                        "type": [
                          "integer",
                          "null"
                        ],
                        "exclusiveMinimum": 0
                      },
                      "percentOfBid": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    },
                    "required": [
                      "projectId",
                      "contactId",
                      "issueDate"
                    ]
                  },
                  "lines": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "position": {
                          "type": "integer",
                          "minimum": -2147483648,
                          "maximum": 2147483647
                        },
                        "productId": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "minimum": -2147483648,
                          "maximum": 2147483647
                        },
                        "description": {
                          "type": "string"
                        },
                        "quantity": {
                          "type": "string"
                        },
                        "unitPrice": {
                          "type": "string"
                        },
                        "isTaxable": {
                          "type": "boolean"
                        },
                        "deletedAt": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "description"
                      ]
                    }
                  }
                },
                "required": [
                  "invoice"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Invoice"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "lineItems": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/InvoiceLineItem"
                              }
                            }
                          }
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoices",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/summary": {
      "get": {
        "summary": "Aggregate invoice totals",
        "description": "Tenant-wide KPI summary: counts and money totals by status. Money values are decimal strings.",
        "security": [
          {
            "bearerAuth": [
              "invoices:read"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "responses": {
          "200": {
            "description": "Summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "additionalProperties": {}
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getInvoicesSummary",
        "x-rate-limit-cost": 1
      }
    },
    "/invoices/{id}": {
      "get": {
        "summary": "Fetch an invoice",
        "description": "Returns the invoice with its line items.",
        "security": [
          {
            "bearerAuth": [
              "invoices:read"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Invoice",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Invoice"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "lineItems": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/InvoiceLineItem"
                              }
                            }
                          }
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getInvoicesById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update a draft invoice",
        "description": "Draft-only. A sent, partial, paid or voided invoice answers 409.",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "invoiceNumber": {
                    "type": "string"
                  },
                  "contactId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "sourceBidId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "sourcePayAppId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "sourceType": {
                    "type": "string",
                    "enum": [
                      "bid",
                      "pay_app",
                      "ad_hoc"
                    ]
                  },
                  "issueDate": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "dueDate": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "taxRate": {
                    "type": "string"
                  },
                  "discountType": {
                    "type": "string",
                    "enum": [
                      "fixed",
                      "percentage"
                    ]
                  },
                  "discount": {
                    "type": "string"
                  },
                  "notesToCustomer": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "terms": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "paymentTermsText": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "paymentTermId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "paymentTermInstallmentId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "exclusiveMinimum": 0
                  },
                  "percentOfBid": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Invoice"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "lineItems": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/InvoiceLineItem"
                              }
                            }
                          }
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchInvoicesById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Soft-delete a draft invoice",
        "description": "Draft-only, and a soft delete — the row is retained with `deletedAt` set.",
        "security": [
          {
            "bearerAuth": [
              "invoices:delete"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteInvoicesById",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/from-bid": {
      "post": {
        "summary": "Create an invoice from a bid",
        "description": "Bills an accepted bid. `mode: full` invoices the whole contract; `percent` invoices `value` percent of it; `fixed` invoices `value` as an amount. `value` is required for `percent` and `fixed`.",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "bidId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "full",
                      "percent",
                      "fixed"
                    ]
                  },
                  "value": {
                    "type": "number",
                    "exclusiveMinimum": 0
                  },
                  "dueDate": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "notesToCustomer": {
                    "type": "string"
                  }
                },
                "required": [
                  "bidId",
                  "mode"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Invoice"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "lineItems": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/InvoiceLineItem"
                              }
                            }
                          }
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesFromBid",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/from-pay-app": {
      "post": {
        "summary": "Create an invoice from an approved pay application",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "payAppId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  },
                  "dueDate": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "notesToCustomer": {
                    "type": "string"
                  }
                },
                "required": [
                  "payAppId"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Invoice"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "lineItems": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/InvoiceLineItem"
                              }
                            }
                          }
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesFromPayApp",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/send": {
      "post": {
        "summary": "Email the invoice and mark it sent",
        "description": "Renders the invoice PDF, emails it, and transitions draft → sent. Omitted recipient defaults to the linked contact's email.",
        "security": [
          {
            "bearerAuth": [
              "invoices:send"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "recipientEmail": {
                    "type": "string",
                    "format": "email"
                  },
                  "ccEmails": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    }
                  },
                  "subject": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  },
                  "templateId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdSend",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/mark-sent": {
      "post": {
        "summary": "Mark an invoice sent without emailing it",
        "description": "For invoices delivered out of band. Transitions draft → sent.",
        "security": [
          {
            "bearerAuth": [
              "invoices:send"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdMarkSent",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/send-via-quickbooks": {
      "post": {
        "summary": "Send an invoice from QuickBooks Online",
        "description": "Pushes the draft invoice to QuickBooks Online, then asks QuickBooks to email it (with a Pay Now link when the organization uses QuickBooks Payments). A send failure after a successful push leaves the invoice as a draft so a retry can update and re-send.",
        "security": [
          {
            "bearerAuth": [
              "invoices:send"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sendTo": {
                    "type": "string",
                    "maxLength": 254,
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdSendViaQuickbooks",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/mark-paid": {
      "post": {
        "summary": "Mark an invoice paid in full",
        "description": "Idempotent for an already-paid invoice. A draft or voided invoice answers 409.",
        "security": [
          {
            "bearerAuth": [
              "invoices:mark_paid"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "paidAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdMarkPaid",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/void": {
      "post": {
        "summary": "Void an invoice",
        "description": "Terminal — a voided invoice cannot be edited, paid or un-voided.",
        "security": [
          {
            "bearerAuth": [
              "invoices:void"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "minLength": 3
                  }
                },
                "required": [
                  "reason"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Voided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdVoid",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/billing-source": {
      "post": {
        "summary": "Link or unlink what an invoice bills",
        "description": "Attach an existing invoice to the pay application, payment-schedule installment and/or bid it bills, or detach it (send `null`). Allowed in every status except `voided`, so an imported, manual or already-paid invoice can be linked after the fact; only the link columns and the derived `sourceType` change — never lines, totals or status — and nothing is pushed to QuickBooks. The pay app must be on the invoice's own project, live (submitted, sent, approved or paid) and not already claimed by another live invoice; the installment must be on the project's bid's payment term and not billed by another live invoice. An invoice bills a pay app or an installment, not both. The response carries the project's billed figures before and after; `dryRun: true` returns them without saving.",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourcePayAppId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "exclusiveMinimum": 0,
                    "description": "Pay application this invoice bills (same project, submitted/sent/approved/paid, not claimed by another live invoice). null unlinks."
                  },
                  "paymentTermInstallmentId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "exclusiveMinimum": 0,
                    "description": "Payment-schedule installment this invoice bills (on the project's bid's payment term, not billed by another live invoice). null unlinks."
                  },
                  "sourceBidId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "exclusiveMinimum": 0,
                    "description": "The project's bid, printed on the invoice PDF. null unlinks."
                  },
                  "dryRun": {
                    "type": "boolean",
                    "description": "Validate and return the billed-figure preview without saving."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Linked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InvoiceBillingSourceResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdBillingSource",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/qbo/sync": {
      "post": {
        "summary": "Queue a QuickBooks Online push",
        "description": "Enqueues a background sync and returns 202 immediately — the response does not mean QBO accepted it.",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "queued": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "queued"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdQboSync",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/payments": {
      "get": {
        "summary": "List the invoice's payment ledger",
        "security": [
          {
            "bearerAuth": [
              "invoices:read"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Payments",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/InvoicePayment"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getInvoicesByIdPayments",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Record a manual payment",
        "description": "Only against a sent or partially-paid invoice. A draft answers 409 (send it first), as does a voided or fully-paid one.",
        "security": [
          {
            "bearerAuth": [
              "invoices:mark_paid"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "string"
                  },
                  "paidAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "check",
                      "cash",
                      "ach",
                      "card",
                      "other"
                    ]
                  },
                  "reference": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 255
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 2000
                  }
                },
                "required": [
                  "amount",
                  "paidAt"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Recorded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "payment": {
                          "$ref": "#/components/schemas/InvoicePayment"
                        },
                        "invoice": {
                          "$ref": "#/components/schemas/Invoice"
                        }
                      },
                      "required": [
                        "payment",
                        "invoice"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdPayments",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/payments/{paymentId}": {
      "patch": {
        "summary": "Edit a manual payment",
        "security": [
          {
            "bearerAuth": [
              "invoices:mark_paid"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "paymentId",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "string"
                  },
                  "paidAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "check",
                      "cash",
                      "ach",
                      "card",
                      "other"
                    ]
                  },
                  "reference": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 255
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "payment": {
                          "$ref": "#/components/schemas/InvoicePayment"
                        },
                        "invoice": {
                          "$ref": "#/components/schemas/Invoice"
                        }
                      },
                      "required": [
                        "payment",
                        "invoice"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchInvoicesByIdPaymentsByPaymentId",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Void a manual payment",
        "description": "Voids rather than erases — the ledger row is retained with `voidedAt` set.",
        "security": [
          {
            "bearerAuth": [
              "invoices:mark_paid"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "paymentId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Voided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "payment": {
                          "$ref": "#/components/schemas/InvoicePayment"
                        },
                        "invoice": {
                          "$ref": "#/components/schemas/Invoice"
                        }
                      },
                      "required": [
                        "payment",
                        "invoice"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteInvoicesByIdPaymentsByPaymentId",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/lines": {
      "post": {
        "summary": "Append a line item to a draft",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "position": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "productId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "description": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": "string"
                  },
                  "unitPrice": {
                    "type": "string"
                  },
                  "isTaxable": {
                    "type": "boolean"
                  },
                  "deletedAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "description"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InvoiceLineItem"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdLines",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/lines/{lineId}": {
      "patch": {
        "summary": "Update a line item",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "lineId",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "position": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "productId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "description": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": "string"
                  },
                  "unitPrice": {
                    "type": "string"
                  },
                  "isTaxable": {
                    "type": "boolean"
                  },
                  "deletedAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InvoiceLineItem"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchInvoicesByIdLinesByLineId",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Remove a line item",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "lineId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteInvoicesByIdLinesByLineId",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/lines/reorder": {
      "post": {
        "summary": "Reorder line items",
        "description": "Atomic. `orderedIds` must list every line on the invoice exactly once.",
        "security": [
          {
            "bearerAuth": [
              "invoices:write"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "orderedIds": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1
                  }
                },
                "required": [
                  "orderedIds"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reordered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/InvoiceLineItem"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postInvoicesByIdLinesReorder",
        "x-rate-limit-cost": 5
      }
    },
    "/invoices/{id}/pdf": {
      "get": {
        "summary": "Render the invoice as a PDF",
        "description": "Streams `application/pdf`.",
        "security": [
          {
            "bearerAuth": [
              "invoices:read"
            ]
          }
        ],
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getInvoicesByIdPdf",
        "x-rate-limit-cost": 50
      }
    },
    "/notifications": {
      "get": {
        "summary": "List notifications for the API key creator",
        "description": "Returns the most recent notifications for the user that owns the API key. Supports filtering by type and unread state.",
        "security": [
          {
            "bearerAuth": [
              "notifications:read"
            ]
          }
        ],
        "tags": [
          "Notifications"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "type",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "boolean",
                "null"
              ]
            },
            "required": false,
            "name": "unreadOnly",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Notifications page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Notification"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getNotifications",
        "x-rate-limit-cost": 1
      }
    },
    "/change-orders/count": {
      "get": {
        "summary": "Count change orders",
        "description": "Returns the total number of change orders in the tenant matching the optional `search`, `status`, `projectId`, `dateFrom`/`dateTo` filters. Mirrors GET /change-orders minus pagination.",
        "security": [
          {
            "bearerAuth": [
              "change_orders:read"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "total"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getChangeOrdersCount",
        "x-rate-limit-cost": 1
      }
    },
    "/change-orders/summary": {
      "get": {
        "summary": "Aggregate change order totals and breakdown by status",
        "description": "Returns flat totals (count, total_amount) plus a breakdown by status (draft / submitted / approved / rejected / void). Money values are decimal strings. Filters on createdAt for date range.",
        "security": [
          {
            "bearerAuth": [
              "change_orders:read"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Change order summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "totals": {
                          "type": "object",
                          "properties": {
                            "count": {
                              "type": "integer"
                            },
                            "total_amount": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "count",
                            "total_amount"
                          ]
                        },
                        "by_status": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "status": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              },
                              "total_amount": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "status",
                              "count",
                              "total_amount"
                            ]
                          }
                        }
                      },
                      "required": [
                        "totals",
                        "by_status"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getChangeOrdersSummary",
        "x-rate-limit-cost": 1
      }
    },
    "/change-orders/get-many": {
      "post": {
        "summary": "Bulk-fetch change orders by ID",
        "description": "Fetch up to 50 change orders by ID in a single round-trip. Returns flat rows only (no items / project / creator). IDs that don't exist are silently omitted.",
        "security": [
          {
            "bearerAuth": [
              "change_orders:read"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 50
                  }
                },
                "required": [
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "ChangeOrders",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ChangeOrder"
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ResponseMeta"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "requested": {
                              "type": "integer"
                            },
                            "returned": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "requested",
                            "returned"
                          ]
                        }
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrdersGetMany",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/change-orders": {
      "get": {
        "summary": "List change orders",
        "description": "Returns a cursor-paginated list of change orders.",
        "security": [
          {
            "bearerAuth": [
              "change_orders:read"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "ChangeOrders page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ChangeOrder"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getChangeOrders",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create change order",
        "security": [
          {
            "bearerAuth": [
              "change_orders:write"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "changeOrderNumber": {
                    "type": "string"
                  },
                  "title": {
                    "type": "string"
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  },
                  "roundSalesAmounts": {
                    "type": [
                      "boolean",
                      "null"
                    ]
                  }
                },
                "required": [
                  "projectId",
                  "title",
                  "reason"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ChangeOrder"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrders",
        "x-rate-limit-cost": 5
      }
    },
    "/change-orders/{id}": {
      "get": {
        "summary": "Get change order by id",
        "security": [
          {
            "bearerAuth": [
              "change_orders:read"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "ChangeOrder",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ChangeOrder"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getChangeOrdersById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update draft change order",
        "security": [
          {
            "bearerAuth": [
              "change_orders:write"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "changeOrderNumber": {
                    "type": "string"
                  },
                  "title": {
                    "type": "string"
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  },
                  "roundSalesAmounts": {
                    "type": [
                      "boolean",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ChangeOrder"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchChangeOrdersById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete change order",
        "security": [
          {
            "bearerAuth": [
              "change_orders:delete"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteChangeOrdersById",
        "x-rate-limit-cost": 5
      }
    },
    "/change-orders/{id}/submit": {
      "post": {
        "summary": "Submit change order for approval",
        "security": [
          {
            "bearerAuth": [
              "change_orders:write"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Submitted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ChangeOrder"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrdersByIdSubmit",
        "x-rate-limit-cost": 5
      }
    },
    "/change-orders/{id}/approve": {
      "post": {
        "summary": "Approve change order",
        "security": [
          {
            "bearerAuth": [
              "change_orders:approve"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "approvedBy": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Approved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ChangeOrder"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrdersByIdApprove",
        "x-rate-limit-cost": 5
      }
    },
    "/change-orders/{id}/reject": {
      "post": {
        "summary": "Reject change order",
        "security": [
          {
            "bearerAuth": [
              "change_orders:approve"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  }
                },
                "required": [
                  "reason"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rejected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ChangeOrder"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrdersByIdReject",
        "x-rate-limit-cost": 5
      }
    },
    "/change-orders/exports": {
      "post": {
        "summary": "Start CSV export of change orders",
        "description": "Enqueues a CSV export job for change orders. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "change_orders:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrdersExports",
        "x-rate-limit-cost": 20
      }
    },
    "/change-orders/bulk": {
      "post": {
        "summary": "Start bulk operation on change orders",
        "description": "Enqueues a bulk delete/restore job for up to 500 change order ids.",
        "security": [
          {
            "bearerAuth": [
              "change_orders:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrdersBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/change-orders/{id}/pdf": {
      "post": {
        "summary": "Generate PDF for a change order (async)",
        "description": "Enqueues an async PDF generation job. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "change_orders:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrdersByIdPdf",
        "x-rate-limit-cost": 50
      }
    },
    "/change-orders/{id}/send": {
      "post": {
        "summary": "Email a change order to the customer",
        "description": "Emails the change order PDF to the given recipients and marks it sent. Same delivery pipeline as the product's send action. Requires change_orders:send. Set requestSignature to true to include a signing link in that same email. That also requires the signatures:send scope, a signer name, and e-signature to be enabled for the organization. The signer email defaults to the project contact, then the first To address. Approved, rejected, and voided change orders cannot be sent for signature; a plain email of those change orders is still allowed. The response includes signatureRequested and, when a request was created, signatureRequestId. It never includes a signing URL or recipient token.",
        "security": [
          {
            "bearerAuth": [
              "change_orders:send"
            ]
          }
        ],
        "tags": [
          "ChangeOrders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "templateId": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  },
                  "recipientEmails": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "minItems": 1,
                    "maxItems": 20
                  },
                  "cc": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "maxItems": 20
                  },
                  "bcc": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "maxItems": 20
                  },
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "body": {
                    "type": "string",
                    "maxLength": 50000
                  },
                  "requestSignature": {
                    "type": "boolean",
                    "default": false,
                    "description": "When true, this send also starts an e-signature and the email includes the signing link. Defaults to false. Also requires the signatures:send scope and a signer name."
                  },
                  "signerEmail": {
                    "type": "string",
                    "maxLength": 320,
                    "format": "email",
                    "description": "Signer email. Defaults to the project contact's email, then the first To address. Must be one of the recipients."
                  },
                  "signerName": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Signer name. Required when requestSignature is true."
                  }
                },
                "required": [
                  "recipientEmails"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "success": {
                          "type": "boolean"
                        },
                        "messageId": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "recipientCount": {
                          "type": "integer"
                        },
                        "changeOrderId": {
                          "type": "integer"
                        },
                        "signatureRequested": {
                          "type": "boolean",
                          "description": "True when this send created an e-signature request."
                        },
                        "signatureRequestId": {
                          "type": "integer",
                          "description": "Id of the signature request created by this send. Never a signing URL or recipient token."
                        }
                      },
                      "required": [
                        "success",
                        "messageId",
                        "recipientCount",
                        "changeOrderId",
                        "signatureRequested"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postChangeOrdersByIdSend",
        "x-rate-limit-cost": 5
      }
    },
    "/site-logs": {
      "get": {
        "summary": "List site logs",
        "description": "Returns a cursor-paginated list of site logs.",
        "security": [
          {
            "bearerAuth": [
              "site_logs:read"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "tag",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "required": false,
            "name": "reviewed",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "SiteLogs page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SiteLog"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getSiteLogs",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create site log",
        "security": [
          {
            "bearerAuth": [
              "site_logs:write"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "logDate": {
                    "type": "string"
                  },
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "notes": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "progress",
                        "issue",
                        "delay",
                        "safety",
                        "delivery",
                        "inspection",
                        "general"
                      ]
                    }
                  },
                  "personnelCount": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "workPerformed": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "materialsReceived": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "visitorsOnSite": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "issues": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "lat": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -90,
                    "maximum": 90
                  },
                  "lng": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -180,
                    "maximum": 180
                  },
                  "clientId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid",
                    "description": "Optional idempotency key (a UUID the client mints once per queued create and repeats on every retry). A repeat with the same key from the same user returns the existing record with 200 instead of creating a duplicate."
                  }
                },
                "required": [
                  "projectId",
                  "logDate",
                  "notes"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Replay of an earlier create with the same `clientId` — the existing site log, unchanged",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SiteLog"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SiteLog"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postSiteLogs",
        "x-rate-limit-cost": 5
      }
    },
    "/site-logs/{id}": {
      "get": {
        "summary": "Get site log by id",
        "security": [
          {
            "bearerAuth": [
              "site_logs:read"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "SiteLog",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SiteLog"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getSiteLogsById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update site log",
        "security": [
          {
            "bearerAuth": [
              "site_logs:write"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "logDate": {
                    "type": "string"
                  },
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "notes": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "progress",
                        "issue",
                        "delay",
                        "safety",
                        "delivery",
                        "inspection",
                        "general"
                      ]
                    }
                  },
                  "personnelCount": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "workPerformed": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "materialsReceived": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "visitorsOnSite": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "issues": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "lat": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -90,
                    "maximum": 90
                  },
                  "lng": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -180,
                    "maximum": 180
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SiteLog"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchSiteLogsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete site log",
        "security": [
          {
            "bearerAuth": [
              "site_logs:delete"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteSiteLogsById",
        "x-rate-limit-cost": 5
      }
    },
    "/site-logs/{id}/report.pdf": {
      "get": {
        "summary": "Download a site log as a daily report PDF",
        "description": "Renders the site log as an `application/pdf` daily report: tenant branding, project and address, date and weather, tags, personnel, notes, work performed / materials / visitors / issues, photo thumbnails, and a filed-by / reviewed-by footer. Same renderer and branding as the session `GET /api/site-logs/:id/report.pdf` route.",
        "operationId": "getSiteLogsByIdReportPdf",
        "security": [
          {
            "bearerAuth": [
              "site_logs:read"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Daily report PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "x-rate-limit-cost": 50
      }
    },
    "/site-logs/exports": {
      "post": {
        "summary": "Start CSV export of site logs",
        "description": "Enqueues a CSV export job for site logs. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "site_logs:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postSiteLogsExports",
        "x-rate-limit-cost": 20
      }
    },
    "/site-logs/imports": {
      "post": {
        "summary": "Start CSV import of site logs",
        "description": "Enqueues a CSV import job. sourcePath must be an opaque upload path returned by POST /jobs/uploads. Required CSV columns: projectId, logDate (ISO), notes. Optional: id (triggers update-in-place when updateInPlace=true), title, workPerformed, issues. Review status is not importable — use the review endpoints, which require site_logs:review.",
        "security": [
          {
            "bearerAuth": [
              "site_logs:write",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourcePath": {
                    "type": "string"
                  },
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "updateInPlace": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "sourcePath",
                  "mapping"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postSiteLogsImports",
        "x-rate-limit-cost": 200
      }
    },
    "/site-logs/bulk": {
      "post": {
        "summary": "Start bulk operation on site logs",
        "description": "Enqueues a bulk delete/restore job for up to 500 site log ids.",
        "security": [
          {
            "bearerAuth": [
              "site_logs:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "SiteLogs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postSiteLogsBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/time-entries": {
      "get": {
        "summary": "List time entries",
        "description": "Returns a cursor-paginated list of time entries.",
        "security": [
          {
            "bearerAuth": [
              "time_entries:read"
            ]
          }
        ],
        "tags": [
          "TimeEntries"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "userId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "First work day to include (YYYY-MM-DD, inclusive). A datetime is read as the day it names."
            },
            "required": false,
            "description": "First work day to include (YYYY-MM-DD, inclusive). A datetime is read as the day it names.",
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Last work day to include (YYYY-MM-DD, inclusive). A datetime is read as the day it names."
            },
            "required": false,
            "description": "Last work day to include (YYYY-MM-DD, inclusive). A datetime is read as the day it names.",
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "required": false,
            "name": "isReviewed",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "TimeEntries page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TimeEntry"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getTimeEntries",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create time entry",
        "security": [
          {
            "bearerAuth": [
              "time_entries:write"
            ]
          }
        ],
        "tags": [
          "TimeEntries"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "projectId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "taskId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "phaseId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "date": {
                    "type": "string",
                    "description": "Calendar day, YYYY-MM-DD. A datetime is accepted and read as the day it names."
                  },
                  "hours": {
                    "type": "number",
                    "minimum": 0.25,
                    "maximum": 24
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "entryType": {
                    "type": "string",
                    "maxLength": 20
                  },
                  "clockIn": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "clockOut": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "lat": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -90,
                    "maximum": 90
                  },
                  "lng": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -180,
                    "maximum": 180
                  },
                  "clockInAccuracyM": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 100000
                  },
                  "clockOutLat": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -90,
                    "maximum": 90
                  },
                  "clockOutLng": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -180,
                    "maximum": 180
                  },
                  "clockOutAccuracyM": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 100000
                  },
                  "clientId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid",
                    "description": "Optional idempotency key (a UUID the client mints once per queued create and repeats on every retry). A repeat with the same key from the same user returns the existing record with 200 instead of creating a duplicate."
                  }
                },
                "required": [
                  "userId",
                  "projectId",
                  "date",
                  "hours"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Replay of an earlier create with the same `clientId` — the existing time entry, unchanged",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/TimeEntry"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/TimeEntry"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postTimeEntries",
        "x-rate-limit-cost": 5
      }
    },
    "/time-entries/{id}": {
      "patch": {
        "summary": "Update time entry",
        "security": [
          {
            "bearerAuth": [
              "time_entries:write"
            ]
          }
        ],
        "tags": [
          "TimeEntries"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "projectId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "taskId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "phaseId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "date": {
                    "type": "string",
                    "description": "Calendar day, YYYY-MM-DD. A datetime is accepted and read as the day it names."
                  },
                  "hours": {
                    "type": "number",
                    "minimum": 0.25,
                    "maximum": 24
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "entryType": {
                    "type": "string",
                    "maxLength": 20
                  },
                  "clockIn": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "clockOut": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "lat": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -90,
                    "maximum": 90
                  },
                  "lng": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -180,
                    "maximum": 180
                  },
                  "clockInAccuracyM": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 100000
                  },
                  "clockOutLat": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -90,
                    "maximum": 90
                  },
                  "clockOutLng": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": -180,
                    "maximum": 180
                  },
                  "clockOutAccuracyM": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 100000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/TimeEntry"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchTimeEntriesById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete time entry",
        "security": [
          {
            "bearerAuth": [
              "time_entries:delete"
            ]
          }
        ],
        "tags": [
          "TimeEntries"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteTimeEntriesById",
        "x-rate-limit-cost": 5
      }
    },
    "/time-entries/exports": {
      "post": {
        "summary": "Start CSV export of time entries",
        "description": "Enqueues a CSV export job for time entries. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "time_entries:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "TimeEntries"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postTimeEntriesExports",
        "x-rate-limit-cost": 20
      }
    },
    "/time-entries/imports": {
      "post": {
        "summary": "Start CSV import of time entries",
        "description": "Enqueues a CSV import job. sourcePath must be an opaque upload path returned by POST /jobs/uploads. Required CSV columns: projectId, userId, date (ISO), hours. Optional: id (triggers update-in-place when updateInPlace=true), taskId, phaseId, notes. Review status is not importable — use the review endpoints, which require time_tracking:review. Reviewed time entries are immutable — update-by-id will reject them. hourlyRate and totalCost are recomputed server-side from labor rates.",
        "security": [
          {
            "bearerAuth": [
              "time_entries:write",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "TimeEntries"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourcePath": {
                    "type": "string"
                  },
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "updateInPlace": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "sourcePath",
                  "mapping"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postTimeEntriesImports",
        "x-rate-limit-cost": 200
      }
    },
    "/time-entries/bulk": {
      "post": {
        "summary": "Start bulk operation on time entries",
        "description": "Enqueues a bulk delete/restore job for up to 500 time entry ids.",
        "security": [
          {
            "bearerAuth": [
              "time_entries:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "TimeEntries"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postTimeEntriesBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/time-entries/summary": {
      "get": {
        "summary": "Aggregate time-entry metrics",
        "description": "Returns entry_count, hours_total, cost_total, plus breakdowns by user and by project. Date filters apply to the work date (time_entries.date), NOT createdAt.",
        "security": [
          {
            "bearerAuth": [
              "time_entries:read"
            ]
          }
        ],
        "tags": [
          "TimeEntries"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "First work day to include (YYYY-MM-DD, inclusive). A datetime is read as the day it names."
            },
            "required": false,
            "description": "First work day to include (YYYY-MM-DD, inclusive). A datetime is read as the day it names.",
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Last work day to include (YYYY-MM-DD, inclusive). A datetime is read as the day it names."
            },
            "required": false,
            "description": "Last work day to include (YYYY-MM-DD, inclusive). A datetime is read as the day it names.",
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "userId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "totals": {
                          "type": "object",
                          "properties": {
                            "entry_count": {
                              "type": "integer"
                            },
                            "hours_total": {
                              "type": "string"
                            },
                            "cost_total": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "entry_count",
                            "hours_total",
                            "cost_total"
                          ]
                        },
                        "by_user": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "userId": {
                                "type": "integer"
                              },
                              "firstName": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "lastName": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "hours_total": {
                                "type": "string"
                              },
                              "cost_total": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "userId",
                              "firstName",
                              "lastName",
                              "hours_total",
                              "cost_total"
                            ]
                          }
                        },
                        "by_project": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "projectId": {
                                "type": "integer"
                              },
                              "projectName": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "hours_total": {
                                "type": "string"
                              },
                              "cost_total": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "projectId",
                              "projectName",
                              "hours_total",
                              "cost_total"
                            ]
                          }
                        }
                      },
                      "required": [
                        "totals",
                        "by_user",
                        "by_project"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getTimeEntriesSummary",
        "x-rate-limit-cost": 1
      }
    },
    "/products": {
      "get": {
        "summary": "List products",
        "description": "Returns a cursor-paginated list of products.",
        "security": [
          {
            "bearerAuth": [
              "products:read"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "type",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "categoryId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "required": false,
            "name": "isKit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false",
                "all"
              ]
            },
            "required": false,
            "name": "isActive",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Products page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProducts",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create product",
        "description": "Creates a product. A kit's costPrice and sellPrice are calculated from its components, so values sent with isKit: true are ignored and a new kit starts at 0; add components with POST /products/{id}/kit-items.",
        "security": [
          {
            "bearerAuth": [
              "products:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sku": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 50
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "salesDescription": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "type": {
                    "type": "string",
                    "maxLength": 50
                  },
                  "categoryId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "costPrice": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sellPrice": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "unit": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 50
                  },
                  "manufacturerId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "manufacturerCode": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 100
                  },
                  "isKit": {
                    "type": "boolean"
                  },
                  "isActive": {
                    "type": "boolean"
                  },
                  "buyTaxPercent": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sellTaxPercent": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Product"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProducts",
        "x-rate-limit-cost": 5
      }
    },
    "/products/count": {
      "get": {
        "summary": "Count products",
        "description": "Returns the total number of products in the tenant matching the optional filters. Mirrors the same filters as GET /products minus pagination — use this when you only need cardinality.",
        "security": [
          {
            "bearerAuth": [
              "products:read"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "type",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "categoryId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "boolean",
                "null"
              ]
            },
            "required": false,
            "name": "isKit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "boolean",
                "null"
              ]
            },
            "required": false,
            "name": "isActive",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "total"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProductsCount",
        "x-rate-limit-cost": 1
      }
    },
    "/products/{id}": {
      "get": {
        "summary": "Get product by id",
        "security": [
          {
            "bearerAuth": [
              "products:read"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Product",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Product"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProductsById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update product",
        "description": "Updates only the fields sent. A kit's costPrice and sellPrice are calculated from its components: values sent for a kit are ignored, setting isKit to true recalculates them, and the response carries the calculated prices.",
        "security": [
          {
            "bearerAuth": [
              "products:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sku": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 50
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "salesDescription": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "type": {
                    "type": "string",
                    "maxLength": 50
                  },
                  "categoryId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "costPrice": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sellPrice": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "unit": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 50
                  },
                  "manufacturerId": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "manufacturerCode": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 100
                  },
                  "isKit": {
                    "type": "boolean"
                  },
                  "isActive": {
                    "type": "boolean"
                  },
                  "buyTaxPercent": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sellTaxPercent": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Product"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchProductsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete product",
        "security": [
          {
            "bearerAuth": [
              "products:delete"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteProductsById",
        "x-rate-limit-cost": 5
      }
    },
    "/products/{id}/kit-items": {
      "get": {
        "summary": "List product kit items",
        "security": [
          {
            "bearerAuth": [
              "products:read"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "KitItems",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProductKitItem"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProductsByIdKitItems",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Add product kit item",
        "security": [
          {
            "bearerAuth": [
              "products:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "componentProductId": {
                    "type": "integer",
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  },
                  "defaultQuantity": {
                    "type": "string"
                  },
                  "overrideCostPrice": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sortOrder": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": -2147483648,
                    "maximum": 2147483647
                  }
                },
                "required": [
                  "componentProductId"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Added",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductKitItem"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProductsByIdKitItems",
        "x-rate-limit-cost": 5
      }
    },
    "/products/{id}/kit-items/{itemId}": {
      "delete": {
        "summary": "Remove kit item",
        "security": [
          {
            "bearerAuth": [
              "products:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteProductsByIdKitItemsByItemId",
        "x-rate-limit-cost": 5
      },
      "patch": {
        "summary": "Update a kit item",
        "description": "Updates a kit component's quantity / override cost / sort order.",
        "security": [
          {
            "bearerAuth": [
              "products:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "defaultQuantity": {
                    "type": "string"
                  },
                  "overrideCostPrice": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated kit item",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductKitItem"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchProductsByIdKitItemsByItemId",
        "x-rate-limit-cost": 5
      }
    },
    "/products/exports": {
      "post": {
        "summary": "Start CSV export of products",
        "description": "Enqueues a CSV export job for products. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "products:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProductsExports",
        "x-rate-limit-cost": 20
      }
    },
    "/products/imports": {
      "post": {
        "summary": "Start CSV import of products",
        "description": "Enqueues a CSV import job. sourcePath must be an opaque upload path returned by POST /jobs/uploads.",
        "security": [
          {
            "bearerAuth": [
              "products:write",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourcePath": {
                    "type": "string"
                  },
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "updateInPlace": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "sourcePath",
                  "mapping"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProductsImports",
        "x-rate-limit-cost": 200
      }
    },
    "/products/bulk": {
      "post": {
        "summary": "Start bulk operation on products",
        "description": "Enqueues a bulk delete/restore job for up to 500 product ids.",
        "security": [
          {
            "bearerAuth": [
              "products:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProductsBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/products/get-many": {
      "post": {
        "summary": "Bulk-fetch products by ID",
        "description": "Fetch up to 50 products by ID in one round-trip. IDs that don't exist or belong to another tenant are silently omitted — compare meta.requested to meta.returned to detect missing.",
        "security": [
          {
            "bearerAuth": [
              "products:read"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 50
                  }
                },
                "required": [
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Products",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ResponseMeta"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "requested": {
                              "type": "integer"
                            },
                            "returned": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "requested",
                            "returned"
                          ]
                        }
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProductsGetMany",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/products/{id}/usage": {
      "get": {
        "summary": "Product usage counts",
        "description": "Returns how many active bids and kits reference the product ({ bidsCount, kitsCount }).",
        "security": [
          {
            "bearerAuth": [
              "products:read"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "bidsCount": {
                          "type": "integer"
                        },
                        "kitsCount": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "bidsCount",
                        "kitsCount"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProductsByIdUsage",
        "x-rate-limit-cost": 1
      }
    },
    "/products/{id}/restore": {
      "post": {
        "summary": "Restore a soft-deleted product",
        "description": "Clears the product's deletedAt. Gated at products:delete because restoring a soft-deleted record is a delete-class operation.",
        "security": [
          {
            "bearerAuth": [
              "products:delete"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Restored product",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Product"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProductsByIdRestore",
        "x-rate-limit-cost": 5
      }
    },
    "/products/{id}/duplicate": {
      "post": {
        "summary": "Duplicate a product",
        "description": "Creates a copy of the product (with a new SKU) and returns the new row.",
        "security": [
          {
            "bearerAuth": [
              "products:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "201": {
            "description": "Duplicated product",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Product"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProductsByIdDuplicate",
        "x-rate-limit-cost": 5
      }
    },
    "/products/bulk-delete": {
      "post": {
        "summary": "Bulk soft-delete products",
        "description": "Soft-deletes up to 500 products by id. Returns { deletedCount }.",
        "security": [
          {
            "bearerAuth": [
              "products:delete"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "deletedCount": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "deletedCount"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProductsBulkDelete",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/products/bulk-restore": {
      "post": {
        "summary": "Bulk restore products",
        "description": "Restores up to 500 soft-deleted products by id. Returns { restoredCount }. Gated at products:delete.",
        "security": [
          {
            "bearerAuth": [
              "products:delete"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Restored count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "restoredCount": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "restoredCount"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProductsBulkRestore",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/products/{id}/kit-items/reorder": {
      "put": {
        "summary": "Reorder kit items",
        "description": "Sets the sortOrder of a kit's component items to the given orderedIds order.",
        "security": [
          {
            "bearerAuth": [
              "products:write"
            ]
          }
        ],
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "orderedIds": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    }
                  }
                },
                "required": [
                  "orderedIds"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reordered kit items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProductKitItem"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "putProductsByIdKitItemsReorder",
        "x-rate-limit-cost": 5
      }
    },
    "/documents/count": {
      "get": {
        "summary": "Count documents",
        "description": "Returns the total number of documents in the tenant matching the optional `search`, `entityType`, `entityId`, `category`, `dateFrom`/`dateTo` filters. Mirrors GET /documents minus pagination.",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "entityType",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "entityId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "category",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "total"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getDocumentsCount",
        "x-rate-limit-cost": 1
      }
    },
    "/documents": {
      "get": {
        "summary": "List documents",
        "description": "Returns a cursor-paginated list of document metadata (not the file bytes).",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "entityType",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "entityId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "category",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Return compact row projection (id + key fields only)."
            },
            "required": false,
            "description": "Return compact row projection (id + key fields only).",
            "name": "compact",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Documents page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Document"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getDocuments",
        "x-rate-limit-cost": 1
      }
    },
    "/documents/{id}": {
      "get": {
        "summary": "Get document metadata",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Document"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getDocumentsById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update document metadata",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 64
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Document"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchDocumentsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete document (soft)",
        "security": [
          {
            "bearerAuth": [
              "documents:delete"
            ]
          }
        ],
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteDocumentsById",
        "x-rate-limit-cost": 5
      }
    },
    "/documents/{id}/download": {
      "get": {
        "summary": "Download document file",
        "description": "Streams the file bytes with appropriate Content-Type / Content-Disposition. Refuses a file that is still being scanned or whose scan failed (409) and a file the malware scan flagged (403).",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "File bytes"
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getDocumentsByIdDownload",
        "x-rate-limit-cost": 1
      }
    },
    "/documents/exports": {
      "post": {
        "summary": "Start CSV export of documents",
        "description": "Enqueues a CSV export job for documents. Poll /api/v1/jobs/{id} for status; GET /api/v1/jobs/{id}/download for the signed URL.",
        "security": [
          {
            "bearerAuth": [
              "documents:read",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postDocumentsExports",
        "x-rate-limit-cost": 20
      }
    },
    "/documents/bulk": {
      "post": {
        "summary": "Start bulk operation on documents",
        "description": "Enqueues a bulk delete/restore job for up to 500 document ids.",
        "security": [
          {
            "bearerAuth": [
              "documents:delete",
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "delete",
                      "restore"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "minItems": 1,
                    "maxItems": 500
                  }
                },
                "required": [
                  "operation",
                  "ids"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "job_id": {
                          "type": "string"
                        },
                        "status_url": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "job_id",
                        "status_url"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postDocumentsBulk",
        "x-rate-limit-cost": 100
      }
    },
    "/calendar/events": {
      "get": {
        "summary": "List calendar events",
        "description": "Returns a list of calendar events within the given time window. Aggregates projects, tasks, phases, leads, bids and activities. Max window 366 days.",
        "security": [
          {
            "bearerAuth": [
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Calendar"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": true,
            "name": "start",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": true,
            "name": "end",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "types",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "assignedTo",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Events",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CalendarEvent"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getCalendarEvents",
        "x-rate-limit-cost": 1
      }
    },
    "/search": {
      "get": {
        "summary": "Search across records",
        "description": "Full-text and fuzzy search across projects, contacts, site logs, documents, bids, leads, invoices, pay apps and change orders, blended into one relevance-ranked list. Requires `projects:read`; each other type is included only when the credential also holds that type's read scope (e.g. `bids:read`), and results are limited to records the authorizing user may open.",
        "security": [
          {
            "bearerAuth": [
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Search"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 2,
              "description": "Search text, at least 2 characters. `*` lists recent records."
            },
            "required": true,
            "description": "Search text, at least 2 characters. `*` lists recent records.",
            "name": "q",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "description": "Maximum results (default 20, max 50)."
            },
            "required": false,
            "description": "Maximum results (default 20, max 50).",
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Matches, best first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SearchResult"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getSearch",
        "x-rate-limit-cost": 1
      }
    },
    "/comments/{entityType}/{entityId}": {
      "get": {
        "summary": "List comments on a record",
        "description": "Returns the comment thread attached to a record. Requires access to the record itself — comments inherit the visibility of what they hang on.",
        "security": [
          {
            "bearerAuth": [
              "comments:read"
            ]
          }
        ],
        "tags": [
          "Comments"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Record type, e.g. `project`, `bid`, `change_order`."
            },
            "required": true,
            "description": "Record type, e.g. `project`, `bid`, `change_order`.",
            "name": "entityType",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "entityId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Comment thread",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Comment"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getCommentsByEntityTypeByEntityId",
        "x-rate-limit-cost": 1
      }
    },
    "/comments": {
      "post": {
        "summary": "Post a comment",
        "description": "Creates a comment on a record and fans out @mention and thread notifications (in-app only, never email).",
        "security": [
          {
            "bearerAuth": [
              "comments:write"
            ]
          }
        ],
        "tags": [
          "Comments"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "entityType": {
                    "type": "string"
                  },
                  "entityId": {
                    "type": "integer"
                  },
                  "content": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 10000
                  },
                  "parentId": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  }
                },
                "required": [
                  "entityType",
                  "entityId",
                  "content"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Comment"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postComments",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/comments/{id}": {
      "patch": {
        "summary": "Edit a comment",
        "description": "Edits a comment. Non-elevated roles may only edit their own.",
        "security": [
          {
            "bearerAuth": [
              "comments:write"
            ]
          }
        ],
        "tags": [
          "Comments"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 10000
                  }
                },
                "required": [
                  "content"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Comment"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchCommentsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete a comment",
        "description": "Soft-deletes a comment. Non-elevated roles may only delete their own.",
        "security": [
          {
            "bearerAuth": [
              "comments:delete"
            ]
          }
        ],
        "tags": [
          "Comments"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "deleted": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "id",
                        "deleted"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteCommentsById",
        "x-rate-limit-cost": 5
      }
    },
    "/users": {
      "get": {
        "summary": "List users",
        "description": "Returns a cursor-paginated list of tenant team members (name, email, role).",
        "security": [
          {
            "bearerAuth": [
              "users:read"
            ]
          }
        ],
        "tags": [
          "Users"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Users page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getUsers",
        "x-rate-limit-cost": 1
      }
    },
    "/users/{id}": {
      "get": {
        "summary": "Get user by id",
        "security": [
          {
            "bearerAuth": [
              "users:read"
            ]
          }
        ],
        "tags": [
          "Users"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "User",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/User"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getUsersById",
        "x-rate-limit-cost": 1
      }
    },
    "/me": {
      "get": {
        "summary": "Identify the caller",
        "description": "Returns the API key or OAuth token that authenticated this request: its id, name, organization, and granted scopes. No scope is required beyond a valid credential.",
        "tags": [
          "Users"
        ],
        "responses": {
          "200": {
            "description": "Caller identity",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Me"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credential",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getMe",
        "x-rate-limit-cost": 1
      }
    },
    "/project-templates": {
      "get": {
        "summary": "List project templates",
        "description": "Returns a cursor-paginated list of project templates.",
        "security": [
          {
            "bearerAuth": [
              "settings:read"
            ]
          }
        ],
        "tags": [
          "ProjectTemplates"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "ProjectTemplates page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProjectTemplate"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProjectTemplates",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create project template",
        "security": [
          {
            "bearerAuth": [
              "settings:write"
            ]
          }
        ],
        "tags": [
          "ProjectTemplates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "projectType": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 50
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProjectTemplate"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postProjectTemplates",
        "x-rate-limit-cost": 5
      }
    },
    "/project-templates/{id}": {
      "get": {
        "summary": "Get project template by id",
        "security": [
          {
            "bearerAuth": [
              "settings:read"
            ]
          }
        ],
        "tags": [
          "ProjectTemplates"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "ProjectTemplate",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProjectTemplate"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getProjectTemplatesById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update project template",
        "security": [
          {
            "bearerAuth": [
              "settings:write"
            ]
          }
        ],
        "tags": [
          "ProjectTemplates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "projectType": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 50
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProjectTemplate"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchProjectTemplatesById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete project template",
        "security": [
          {
            "bearerAuth": [
              "settings:write"
            ]
          }
        ],
        "tags": [
          "ProjectTemplates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteProjectTemplatesById",
        "x-rate-limit-cost": 5
      }
    },
    "/settings/branding": {
      "get": {
        "summary": "Get branding settings",
        "description": "Returns the tenant's branding (excludes logo bytes — download via internal app).",
        "security": [
          {
            "bearerAuth": [
              "users:read"
            ]
          }
        ],
        "tags": [
          "Settings"
        ],
        "responses": {
          "200": {
            "description": "Branding",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BrandingSettings"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getSettingsBranding",
        "x-rate-limit-cost": 1
      }
    },
    "/settings/data-retention": {
      "get": {
        "summary": "Get data retention settings",
        "security": [
          {
            "bearerAuth": [
              "users:read"
            ]
          }
        ],
        "tags": [
          "Settings"
        ],
        "responses": {
          "200": {
            "description": "DataRetention",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DataRetentionSettings"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getSettingsDataRetention",
        "x-rate-limit-cost": 1
      }
    },
    "/jobs": {
      "get": {
        "summary": "List async jobs",
        "description": "Returns a cursor-paginated list of async jobs in the authenticated tenant. Company admins, managers and tenant API keys see every job; other roles see only the jobs they started.",
        "security": [
          {
            "bearerAuth": [
              "jobs:read"
            ]
          }
        ],
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "type",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "resourceType",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Jobs page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Job"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getJobs",
        "x-rate-limit-cost": 1
      }
    },
    "/jobs/{id}": {
      "get": {
        "summary": "Get job by id",
        "description": "Returns a single async job (status, progress, result, error message).",
        "security": [
          {
            "bearerAuth": [
              "jobs:read"
            ]
          }
        ],
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Job"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getJobsById",
        "x-rate-limit-cost": 1
      },
      "delete": {
        "summary": "Soft-delete job",
        "description": "Soft-deletes the job row. Stored artifacts will be reaped on TTL expiry.",
        "security": [
          {
            "bearerAuth": [
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteJobsById",
        "x-rate-limit-cost": 5
      }
    },
    "/jobs/{id}/download": {
      "get": {
        "summary": "Download job result artifact",
        "description": "Redirects (302) to a short-lived pre-signed URL for the result artifact (succeeded/partial jobs only).",
        "security": [
          {
            "bearerAuth": [
              "jobs:read"
            ]
          }
        ],
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to signed download URL"
          },
          "404": {
            "description": "Not found or no artifact",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Job not in a downloadable state",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getJobsByIdDownload",
        "x-rate-limit-cost": 1
      }
    },
    "/jobs/{id}/errors": {
      "get": {
        "summary": "Download job errors artifact",
        "description": "Redirects (302) to a short-lived pre-signed URL for the row-level errors CSV (partial/failed jobs only).",
        "security": [
          {
            "bearerAuth": [
              "jobs:read"
            ]
          }
        ],
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to signed errors URL"
          },
          "404": {
            "description": "Not found or no errors artifact",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getJobsByIdErrors",
        "x-rate-limit-cost": 1
      }
    },
    "/jobs/{id}/cancel": {
      "post": {
        "summary": "Cancel job",
        "description": "Cancels a queued or running job. Already-terminal jobs return 409.",
        "security": [
          {
            "bearerAuth": [
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Job"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Job already terminal",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postJobsByIdCancel",
        "x-rate-limit-cost": 5
      }
    },
    "/jobs/uploads": {
      "post": {
        "summary": "Get signed URL for CSV upload",
        "description": "Returns a short-lived signed PUT URL. Caller uploads the CSV bytes directly to that URL, then passes the returned sourcePath to POST /{module}/imports.",
        "security": [
          {
            "bearerAuth": [
              "jobs:write"
            ]
          }
        ],
        "tags": [
          "Jobs"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "contentType": {
                    "type": "string",
                    "enum": [
                      "text/csv"
                    ],
                    "default": "text/csv"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "uploadUrl": {
                          "type": "string",
                          "format": "uri"
                        },
                        "sourcePath": {
                          "type": "string"
                        },
                        "expiresInSeconds": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "uploadUrl",
                        "sourcePath",
                        "expiresInSeconds"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postJobsUploads",
        "x-rate-limit-cost": 5,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/webhook-endpoints": {
      "get": {
        "summary": "List webhook endpoints",
        "description": "Returns all active webhook endpoints for the tenant (capped at 200, no cursor pagination yet). The signingSecret field is never returned here — it is only surfaced once on creation and once on rotation.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getWebhookEndpoints",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "Create webhook endpoint",
        "description": "Creates a new webhook endpoint. The url is SSRF-validated before creation. The signingSecret is returned in the response exactly once — store it now, it cannot be retrieved later.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "minLength": 1
                  },
                  "eventTypes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "bid.created",
                        "bid.updated",
                        "bid.deleted",
                        "bid.accepted",
                        "bid.acceptance_reverted",
                        "bid.rejected",
                        "bid.expired",
                        "bid.cancelled",
                        "bid.reopened",
                        "project.created",
                        "project.updated",
                        "project.deleted",
                        "project.status_changed",
                        "pay_app.submitted",
                        "pay_app.approved",
                        "pay_app.rejected",
                        "pay_app.paid",
                        "pay_app.voided",
                        "pay_app.updated",
                        "pay_app.deleted",
                        "change_order.submitted",
                        "change_order.approved",
                        "change_order.rejected",
                        "change_order.voided",
                        "change_order.updated",
                        "change_order.deleted",
                        "contact.created",
                        "contact.updated",
                        "contact.deleted",
                        "lead.created",
                        "lead.updated",
                        "lead.deleted",
                        "product.created",
                        "product.updated",
                        "product.deleted"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 35
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 2000
                  }
                },
                "required": [
                  "url",
                  "eventTypes"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookEndpointWithSecret"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postWebhookEndpoints",
        "x-rate-limit-cost": 5
      }
    },
    "/webhook-endpoints/{id}": {
      "get": {
        "summary": "Get webhook endpoint",
        "description": "Returns a single webhook endpoint. The signingSecret field is redacted.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookEndpoint"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getWebhookEndpointsById",
        "x-rate-limit-cost": 1
      },
      "patch": {
        "summary": "Update webhook endpoint",
        "description": "Partially updates a webhook endpoint. If url changes, it is re-validated against SSRF rules. The signingSecret is never returned.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "minLength": 1
                  },
                  "eventTypes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "bid.created",
                        "bid.updated",
                        "bid.deleted",
                        "bid.accepted",
                        "bid.acceptance_reverted",
                        "bid.rejected",
                        "bid.expired",
                        "bid.cancelled",
                        "bid.reopened",
                        "project.created",
                        "project.updated",
                        "project.deleted",
                        "project.status_changed",
                        "pay_app.submitted",
                        "pay_app.approved",
                        "pay_app.rejected",
                        "pay_app.paid",
                        "pay_app.voided",
                        "pay_app.updated",
                        "pay_app.deleted",
                        "change_order.submitted",
                        "change_order.approved",
                        "change_order.rejected",
                        "change_order.voided",
                        "change_order.updated",
                        "change_order.deleted",
                        "contact.created",
                        "contact.updated",
                        "contact.deleted",
                        "lead.created",
                        "lead.updated",
                        "lead.deleted",
                        "product.created",
                        "product.updated",
                        "product.deleted"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 35
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 2000
                  },
                  "isActive": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookEndpoint"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "patchWebhookEndpointsById",
        "x-rate-limit-cost": 5
      },
      "delete": {
        "summary": "Delete webhook endpoint",
        "description": "Soft-deletes a webhook endpoint. Future events will not be delivered.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteWebhookEndpointsById",
        "x-rate-limit-cost": 5
      }
    },
    "/webhook-endpoints/{id}/rotate-secret": {
      "post": {
        "summary": "Rotate webhook signing secret",
        "description": "Generates a new signing secret for the endpoint and returns it in the response. The old secret is invalidated immediately. Surface this value once and store it securely.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "signingSecret": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "signingSecret"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postWebhookEndpointsByIdRotateSecret",
        "x-rate-limit-cost": 5
      }
    },
    "/webhook-endpoints/{id}/deliveries": {
      "get": {
        "summary": "List recent deliveries for an endpoint",
        "description": "Returns the 100 most recent webhook delivery rows for the endpoint, ordered most-recent-first. When more exist, meta.pagination.has_more is true and cursor stays null — page the rest with GET /webhook-deliveries.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Deliveries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getWebhookEndpointsByIdDeliveries",
        "x-rate-limit-cost": 1
      }
    },
    "/webhook-deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "List webhook deliveries (tenant-wide)",
        "description": "Returns cursor-paginated webhook delivery rows for the calling tenant across all endpoints. Filterable by endpoint, status, event type, and createdAt date range. The per-endpoint list is capped at 100 and sets has_more when that cap is hit; use this endpoint to page further.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "required": false,
            "name": "endpointId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "succeeded",
                "failed",
                "exhausted"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "eventType",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Deliveries page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDeliveryFull"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "x-rate-limit-cost": 1
      }
    },
    "/webhook-deliveries/{id}": {
      "get": {
        "operationId": "getWebhookDelivery",
        "summary": "Get webhook delivery",
        "description": "Returns a single webhook delivery row, including the response body (capped at 10KB) and headers captured from the most recent attempt.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookDeliveryFull"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "x-rate-limit-cost": 1
      }
    },
    "/webhook-deliveries/{id}/replay": {
      "post": {
        "operationId": "replayWebhookDelivery",
        "summary": "Replay webhook delivery",
        "description": "Re-enqueues the delivery for another delivery attempt. The attemptCount is reset to 0, status is set back to pending, and the response/error fields are cleared. Returns 409 if the linked endpoint has been soft-deleted.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Replayed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookDeliveryFull"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Endpoint deleted",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "x-rate-limit-cost": 5
      }
    },
    "/api-audit-logs": {
      "get": {
        "operationId": "listApiAuditLogs",
        "summary": "List API audit log entries for the calling tenant",
        "description": "Returns cursor-paginated API audit log rows for the calling tenant. Each row records one v1 API request — method, path, status, duration, request id, and (when authenticated) the API key id, OAuth token id, or user id that made the call. Filterable by API key, HTTP method, status code, and date range. Tenant-scoped — Tenant A can never see Tenant B's audit log even if scopes are mis-issued.",
        "security": [
          {
            "bearerAuth": [
              "audit:read"
            ]
          }
        ],
        "tags": [
          "Observability"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "required": false,
            "name": "apiKeyId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "method",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 lower bound for createdAt (inclusive)"
            },
            "required": false,
            "description": "ISO 8601 lower bound for createdAt (inclusive)",
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 upper bound for createdAt (inclusive)"
            },
            "required": false,
            "description": "ISO 8601 upper bound for createdAt (inclusive)",
            "name": "dateTo",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Audit log page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiAuditLog"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "x-rate-limit-cost": 1
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Get current rate-limit consumption",
        "description": "Returns the calling tenant's current rate-limit consumption for both the tenant-wide cost-weighted budget (sliding hour) and the per-API-key request limit (sliding minute). Use this to surface live consumption in developer dashboards or to back off proactively before hitting `429`. Data is sourced from the same Redis-backed buckets that the rate limiter writes to, so the numbers returned here are exactly the limits enforced.",
        "security": [
          {
            "bearerAuth": [
              "audit:read"
            ]
          }
        ],
        "tags": [
          "Observability"
        ],
        "responses": {
          "200": {
            "description": "Current usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UsageResponse"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Scope insufficient",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "x-rate-limit-cost": 1
      }
    },
    "/signature-requests": {
      "get": {
        "summary": "List e-signature requests",
        "description": "Signature requests across bids and change orders, newest first. The recipient token is never returned; the signing URL requires signatures:send and an active request.",
        "security": [
          {
            "bearerAuth": [
              "signatures:read"
            ]
          }
        ],
        "tags": [
          "SignatureRequests"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "bid",
                "change_order"
              ]
            },
            "required": false,
            "name": "entityType",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "entityId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Signature requests page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SignatureRequest"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getSignatureRequests",
        "x-rate-limit-cost": 1
      }
    },
    "/signature-requests/{id}": {
      "get": {
        "summary": "Get an e-signature request",
        "security": [
          {
            "bearerAuth": [
              "signatures:read"
            ]
          }
        ],
        "tags": [
          "SignatureRequests"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Signature request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SignatureRequest"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getSignatureRequestsById",
        "x-rate-limit-cost": 1
      }
    },
    "/signature-requests/{id}/resend": {
      "post": {
        "summary": "Resend an e-signature request",
        "description": "Re-emails the SAME signing link. No new document is created. 409s when the request is not pending or sent. Shares the app's resend budget (20 per hour per organization and user); 429 past it.",
        "security": [
          {
            "bearerAuth": [
              "signatures:send"
            ]
          }
        ],
        "tags": [
          "SignatureRequests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Resend result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "emailSent": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "emailSent"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postSignatureRequestsByIdResend",
        "x-rate-limit-cost": 5
      }
    },
    "/signature-requests/{id}/void": {
      "post": {
        "summary": "Void an e-signature request",
        "description": "Cancels an active request and kills its signing link. The stored row is the source of truth; cancelling the remote document is best-effort.",
        "security": [
          {
            "bearerAuth": [
              "signatures:send"
            ]
          }
        ],
        "tags": [
          "SignatureRequests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Voided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SignatureRequest"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postSignatureRequestsByIdVoid",
        "x-rate-limit-cost": 5
      }
    },
    "/crew-assignments": {
      "get": {
        "summary": "List crew assignments",
        "description": "Returns crew assignments in a date range (inclusive, max 31 days). Callers without dispatch:write only receive their own rows. Paginated via limit/offset.",
        "security": [
          {
            "bearerAuth": [
              "dispatch:read"
            ]
          }
        ],
        "tags": [
          "CrewAssignments"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "required": true,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "required": true,
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "userId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0
            },
            "required": false,
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Crew assignments",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CrewAssignment"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getCrewAssignments",
        "x-rate-limit-cost": 1
      }
    },
    "/crew-assignments/{id}": {
      "get": {
        "summary": "Get a crew assignment",
        "security": [
          {
            "bearerAuth": [
              "dispatch:read"
            ]
          }
        ],
        "tags": [
          "CrewAssignments"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Crew assignment",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CrewAssignment"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getCrewAssignmentsById",
        "x-rate-limit-cost": 1
      },
      "delete": {
        "summary": "Delete a crew assignment",
        "description": "Soft-deletes a crew assignment.",
        "security": [
          {
            "bearerAuth": [
              "dispatch:write"
            ]
          }
        ],
        "tags": [
          "CrewAssignments"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "deleted": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "id",
                        "deleted"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteCrewAssignmentsById",
        "x-rate-limit-cost": 5
      }
    },
    "/crew-assignments/{date}/{projectId}": {
      "put": {
        "summary": "Replace a job-day's crew",
        "description": "Sets the crew assigned to a project on a calendar day. Inserts missing users, soft-deletes removed ones, and optionally updates startTime/note for the job-day.",
        "security": [
          {
            "bearerAuth": [
              "dispatch:write"
            ]
          }
        ],
        "tags": [
          "CrewAssignments"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "required": true,
            "name": "date",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer"
            },
            "required": true,
            "name": "projectId",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userIds": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "maxItems": 200
                  },
                  "startTime": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d)?$"
                  },
                  "note": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 2000
                  }
                },
                "required": [
                  "userIds"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Replaced crew",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CrewAssignment"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "putCrewAssignmentsByDateByProjectId",
        "x-rate-limit-cost": 5
      }
    },
    "/dashboard/briefing/dismissals": {
      "post": {
        "summary": "Dismiss or snooze a briefing row",
        "description": "Hides one \"Needs you today\" row for the credential's user. `until` of `7d` / `30d` snoozes it; null or omitted hides it until the row's underlying state changes (a bid is re-sent, an invoice balance moves). Re-dismissing an already-dismissed row refreshes it. 404 when the entity does not exist or the credential's user cannot read it (the two are indistinguishable on purpose). Requires the read scope of the entity the row points at: invoices:read, bids:read, pay_apps:read, site_logs:read or projects:read.",
        "security": [
          {
            "bearerAuth": [
              "invoices:read",
              "bids:read",
              "pay_apps:read",
              "site_logs:read",
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Briefing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemKey": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  },
                  "until": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "7d",
                      "30d"
                    ]
                  }
                },
                "required": [
                  "itemKey"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Dismissed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BriefingDismissal"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "postDashboardBriefingDismissals",
        "x-rate-limit-cost": 5
      }
    },
    "/dashboard/briefing/dismissals/{itemKey}": {
      "delete": {
        "summary": "Undo a briefing dismissal",
        "description": "Brings a dismissed or snoozed row back for the credential's user. 404 when the row is not currently dismissed. Requires the read scope of the entity the row points at: invoices:read, bids:read, pay_apps:read, site_logs:read or projects:read.",
        "security": [
          {
            "bearerAuth": [
              "invoices:read",
              "bids:read",
              "pay_apps:read",
              "site_logs:read",
              "projects:read"
            ]
          }
        ],
        "tags": [
          "Briefing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "schema": {
              "type": "string",
              "description": "The row's itemKey, URL-encoded."
            },
            "required": true,
            "description": "The row's itemKey, URL-encoded.",
            "name": "itemKey",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Undone",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BriefingDismissal"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "deleteDashboardBriefingDismissalsByItemKey",
        "x-rate-limit-cost": 5
      }
    },
    "/reports/catalog": {
      "get": {
        "summary": "List the reports available to the caller",
        "description": "Reports v2 catalog, filtered to the entity permissions the caller's role holds.",
        "security": [
          {
            "bearerAuth": [
              "reports:read"
            ]
          }
        ],
        "tags": [
          "Reports"
        ],
        "responses": {
          "200": {
            "description": "Catalog",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "group": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string"
                          },
                          "groupBys": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        },
                        "required": [
                          "slug",
                          "title",
                          "group",
                          "description",
                          "kind",
                          "groupBys"
                        ]
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getReportsCatalog",
        "x-rate-limit-cost": 1
      }
    },
    "/reports/review": {
      "get": {
        "summary": "Business review — Sold, Billed, Collected, Owed vs the prior period",
        "description": "The weekly business review: four scorecard tiles with definitions and honest deltas, plus Money / Sales / Work sections. Role-shaped. Balances are as of `date` (the caller's calendar day); flows cover dateFrom–dateTo.",
        "security": [
          {
            "bearerAuth": [
              "reports:read"
            ]
          }
        ],
        "tags": [
          "Reports"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "required": false,
            "name": "date",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 64
            },
            "required": false,
            "name": "tz",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 32
            },
            "required": false,
            "name": "preset",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "default": false
            },
            "required": false,
            "name": "compare",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "customerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "estimatorId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 64
            },
            "required": false,
            "name": "source",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 32
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 32
            },
            "required": false,
            "name": "projectType",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean"
            },
            "required": false,
            "name": "includeCompleted",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Business review",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "window": {
                          "type": "object",
                          "properties": {
                            "from": {
                              "type": "string"
                            },
                            "to": {
                              "type": "string"
                            },
                            "label": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "from",
                            "to",
                            "label"
                          ]
                        },
                        "scorecard": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "key": {
                                "type": "string"
                              },
                              "label": {
                                "type": "string"
                              },
                              "definition": {
                                "type": "string"
                              },
                              "value": {
                                "type": "string"
                              },
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "money",
                                  "count",
                                  "percent",
                                  "days"
                                ]
                              },
                              "detail": {
                                "type": "string"
                              },
                              "linkTo": {
                                "type": "string"
                              },
                              "delta": {
                                "type": "object",
                                "properties": {
                                  "kind": {
                                    "type": "string",
                                    "enum": [
                                      "percent",
                                      "money"
                                    ]
                                  },
                                  "value": {
                                    "type": "string"
                                  },
                                  "direction": {
                                    "type": "string",
                                    "enum": [
                                      "up",
                                      "down",
                                      "flat"
                                    ]
                                  },
                                  "goodWhen": {
                                    "type": "string",
                                    "enum": [
                                      "up",
                                      "down"
                                    ]
                                  }
                                },
                                "required": [
                                  "kind",
                                  "value",
                                  "direction",
                                  "goodWhen"
                                ]
                              }
                            },
                            "required": [
                              "key",
                              "label",
                              "definition",
                              "value",
                              "kind"
                            ]
                          }
                        },
                        "sections": {
                          "type": "object",
                          "additionalProperties": {}
                        }
                      },
                      "required": [
                        "window",
                        "scorecard",
                        "sections"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getReportsReview",
        "x-rate-limit-cost": 1
      }
    },
    "/reports/{slug}": {
      "get": {
        "summary": "Run a catalog report",
        "description": "KPIs, series and up to 500 rows for one report. Money values are decimal strings. `groupBy` must be one of the catalog entry's groupBys.",
        "security": [
          {
            "bearerAuth": [
              "reports:read"
            ]
          }
        ],
        "tags": [
          "Reports"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "ar-aging",
                "billed-vs-collected",
                "bid-performance",
                "pipeline",
                "wip"
              ]
            },
            "required": true,
            "name": "slug",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateFrom",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "dateTo",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "required": false,
            "name": "date",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 64
            },
            "required": false,
            "name": "tz",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 32
            },
            "required": false,
            "name": "preset",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "default": false
            },
            "required": false,
            "name": "compare",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 32
            },
            "required": false,
            "name": "groupBy",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "customerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            "required": false,
            "name": "estimatorId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 64
            },
            "required": false,
            "name": "source",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 32
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 32
            },
            "required": false,
            "name": "projectType",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean"
            },
            "required": false,
            "name": "includeCompleted",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 500
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 64
            },
            "required": false,
            "name": "sort",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Report payload",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "slug": {
                          "type": "string",
                          "enum": [
                            "ar-aging",
                            "billed-vs-collected",
                            "bid-performance",
                            "pipeline",
                            "wip"
                          ]
                        },
                        "window": {
                          "type": "object",
                          "properties": {
                            "from": {
                              "type": "string"
                            },
                            "to": {
                              "type": "string"
                            },
                            "label": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "from",
                            "to",
                            "label"
                          ]
                        },
                        "compareWindow": {
                          "type": "object",
                          "properties": {
                            "from": {
                              "type": "string"
                            },
                            "to": {
                              "type": "string"
                            },
                            "label": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "from",
                            "to",
                            "label"
                          ]
                        },
                        "kpis": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "key": {
                                "type": "string"
                              },
                              "label": {
                                "type": "string"
                              },
                              "definition": {
                                "type": "string"
                              },
                              "value": {
                                "type": "string"
                              },
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "money",
                                  "count",
                                  "percent",
                                  "days"
                                ]
                              },
                              "detail": {
                                "type": "string"
                              },
                              "linkTo": {
                                "type": "string"
                              },
                              "delta": {
                                "type": "object",
                                "properties": {
                                  "kind": {
                                    "type": "string",
                                    "enum": [
                                      "percent",
                                      "money"
                                    ]
                                  },
                                  "value": {
                                    "type": "string"
                                  },
                                  "direction": {
                                    "type": "string",
                                    "enum": [
                                      "up",
                                      "down",
                                      "flat"
                                    ]
                                  },
                                  "goodWhen": {
                                    "type": "string",
                                    "enum": [
                                      "up",
                                      "down"
                                    ]
                                  }
                                },
                                "required": [
                                  "kind",
                                  "value",
                                  "direction",
                                  "goodWhen"
                                ]
                              }
                            },
                            "required": [
                              "key",
                              "label",
                              "definition",
                              "value",
                              "kind"
                            ]
                          }
                        },
                        "series": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "bucket": {
                                "type": "string"
                              },
                              "label": {
                                "type": "string"
                              },
                              "values": {
                                "type": "object",
                                "additionalProperties": {
                                  "type": "string"
                                }
                              },
                              "compareValues": {
                                "type": "object",
                                "additionalProperties": {
                                  "type": "string"
                                }
                              }
                            },
                            "required": [
                              "bucket",
                              "label",
                              "values"
                            ]
                          }
                        },
                        "rows": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "additionalProperties": {}
                          }
                        },
                        "totals": {
                          "type": "object",
                          "additionalProperties": {}
                        },
                        "hasMore": {
                          "type": "boolean"
                        },
                        "meta": {
                          "type": "object",
                          "properties": {
                            "scope": {
                              "type": "string",
                              "enum": [
                                "tenant",
                                "own",
                                "assigned"
                              ]
                            },
                            "definitions": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "string"
                              }
                            },
                            "groupBy": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "scope",
                            "definitions",
                            "groupBy"
                          ]
                        }
                      },
                      "required": [
                        "slug",
                        "window",
                        "kpis",
                        "rows",
                        "hasMore",
                        "meta"
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ResponseMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "operationId": "getReportsBySlug",
        "x-rate-limit-cost": 1
      }
    },
    "/oauth/authorize": {
      "get": {
        "summary": "OAuth 2.1 authorization endpoint (consent screen)",
        "description": "Renders an HTML consent screen. The user must be authenticated via the first-party session cookie. On approval the user is redirected to redirect_uri with `?code=&state=`. On error in the validation phase, fatal errors render an HTML error page (no redirect); validation errors after redirect_uri verification redirect with `?error=&state=`.",
        "security": [],
        "tags": [
          "OAuth"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "code"
              ]
            },
            "required": true,
            "name": "response_type",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "client_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uri"
            },
            "required": true,
            "name": "redirect_uri",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 43,
              "maxLength": 128
            },
            "required": true,
            "name": "code_challenge",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "S256"
              ]
            },
            "required": true,
            "name": "code_challenge_method",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "scope",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "state",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Consent screen rendered (HTML)",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "302": {
            "description": "Redirect to client redirect_uri with code or error"
          },
          "400": {
            "description": "Fatal validation error (HTML)",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "operationId": "getOauthAuthorize",
        "x-rate-limit-cost": 1
      },
      "post": {
        "summary": "OAuth 2.1 consent decision",
        "description": "Receives the user's Approve/Deny decision from the consent form. Body is form-encoded with `request_uri` (the original GET URL) and `decision` (approve or deny). Always redirects back to the client's redirect_uri.",
        "security": [],
        "tags": [
          "OAuth"
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "request_uri": {
                    "type": "string"
                  },
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "deny"
                    ]
                  }
                },
                "required": [
                  "request_uri",
                  "decision"
                ]
              }
            }
          }
        },
        "responses": {
          "302": {
            "description": "Redirect to client redirect_uri"
          },
          "400": {
            "description": "Fatal validation error (HTML)",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "operationId": "postOauthAuthorize",
        "x-rate-limit-cost": 5
      }
    },
    "/oauth/token": {
      "post": {
        "summary": "OAuth 2.1 token endpoint",
        "description": "Exchanges an authorization code (with PKCE verifier) or a refresh token for a new access token (and rotated refresh token). Body MUST be application/x-www-form-urlencoded per RFC 6749. Response carries Cache-Control: no-store. Confidential clients authenticate by passing client_secret in the body; public clients omit it. Refresh token reuse triggers family-wide revocation of all access + refresh tokens issued from the original grant.",
        "security": [],
        "tags": [
          "OAuth"
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OauthTokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access token issued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OauthTokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "RFC 6749 §5.2 error envelope (invalid_request, invalid_grant, invalid_client, unsupported_grant_type)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OauthTokenError"
                }
              }
            }
          },
          "401": {
            "description": "Client authentication failed (invalid_client)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OauthTokenError"
                }
              }
            }
          }
        },
        "operationId": "postOauthToken",
        "x-rate-limit-cost": 5
      }
    },
    "/oauth/revoke": {
      "post": {
        "summary": "OAuth 2.0 Token Revocation (RFC 7009)",
        "description": "Revokes an access token or refresh token. Body MUST be application/x-www-form-urlencoded. Per RFC 7009 §2.2, the response is 200 OK whether the token was successfully revoked, already revoked, expired, or unknown — this prevents leaking information about token validity. Revoking a refresh token cascades to the entire token family (all access + refresh tokens issued from the same grant). Revoking an access token does NOT cascade.",
        "security": [],
        "tags": [
          "OAuth"
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OauthRevokeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token revoked, already revoked, expired, or unknown — all return 200 per RFC 7009 §2.2"
          },
          "400": {
            "description": "RFC 7009 error envelope (invalid_request, invalid_client, invalid_grant)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OauthRevokeError"
                }
              }
            }
          },
          "401": {
            "description": "Client authentication failed (invalid_client)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OauthRevokeError"
                }
              }
            }
          }
        },
        "operationId": "postOauthRevoke",
        "x-rate-limit-cost": 5
      }
    },
    "/oauth/userinfo": {
      "get": {
        "summary": "OAuth/OpenID userinfo endpoint",
        "description": "Returns identity information about the resource owner authenticated by the presented access token. Authentication is the standard `Authorization: Bearer` header — both API keys (bwp_live_/bwp_test_) and OAuth access tokens (bwp_at_) are accepted. No scope is required to call this endpoint.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": []
          }
        ],
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "Identity claims for the resource owner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OauthUserinfo"
                }
              }
            }
          },
          "401": {
            "description": "Authentication missing, invalid, expired, or membership revoked",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                }
              }
            }
          }
        },
        "operationId": "getOauthUserinfo",
        "x-rate-limit-cost": 1
      }
    },
    "/oauth/register": {
      "post": {
        "summary": "Dynamic Client Registration (RFC 7591)",
        "description": "Self-registers a new OAuth client. Public endpoint — no authentication required. Per-IP rate-limited to 5 requests/minute. Each redirect_uri must be https, a loopback http URL on any port (http://127.0.0.1:{port}, http://localhost:{port}, http://[::1]:{port} — RFC 8252 §7.3), or a private-use scheme such as cursor:// (RFC 8252 §7.1); script-injection schemes (javascript:, data:, file:, blob:), embedded credentials, and fragments are rejected.",
        "security": [],
        "tags": [
          "OAuth"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OauthClientRegistrationRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Client registered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OauthClientRegistration"
                }
              }
            }
          },
          "400": {
            "description": "RFC 7591 error envelope (invalid_client_metadata, invalid_redirect_uri, invalid_scope)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OauthRegistrationError"
                }
              }
            }
          },
          "429": {
            "description": "Per-IP rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OauthRegistrationError"
                }
              }
            }
          }
        },
        "operationId": "postOauthRegister",
        "x-rate-limit-cost": 5
      }
    }
  },
  "webhooks": {},
  "tags": [
    {
      "name": "Contacts",
      "description": "Customers, vendors and subcontractors, with their addresses, notes and tags."
    },
    {
      "name": "Leads",
      "description": "Sales pipeline records and their movement through stages."
    },
    {
      "name": "Bids",
      "description": "Estimates and proposals, including line items, sublines, distributed costs, templates and sending."
    },
    {
      "name": "Projects",
      "description": "Awarded work: phases, tasks, subtasks, dependencies and assigned contacts."
    },
    {
      "name": "ProjectTemplates",
      "description": "Reusable phase and task structures applied when a bid becomes a project."
    },
    {
      "name": "Reports",
      "description": "Reports v2 — the business review and the report catalog (AR aging, billed vs collected, bid performance, pipeline, WIP)."
    },
    {
      "name": "ChangeOrders",
      "description": "Scope changes against a project, with approval workflow and line items."
    },
    {
      "name": "PayApps",
      "description": "AIA-style payment applications: schedule of values, retainage and approval."
    },
    {
      "name": "Invoices",
      "description": "Accounts receivable: invoices, line items, payments, sending and voiding."
    },
    {
      "name": "SiteLogs",
      "description": "Daily field reports, photos and their review state."
    },
    {
      "name": "TimeEntries",
      "description": "Labor hours logged against projects, with review and cost roll-up."
    },
    {
      "name": "CrewAssignments",
      "description": "Daily crew assignments of tenant users to jobs (the dispatch board)."
    },
    {
      "name": "Briefing",
      "description": "Per-user Dismiss / Snooze of rows on the \"Needs you today\" morning briefing."
    },
    {
      "name": "Products",
      "description": "The catalog of materials and labor items bids are built from, including kits."
    },
    {
      "name": "Documents",
      "description": "File attachments on any entity, with versioning and signed upload/download URLs."
    },
    {
      "name": "Calendar",
      "description": "Scheduled events derived from project phases, tasks and bid deadlines."
    },
    {
      "name": "Search",
      "description": "One ranked search across records, limited to the types and rows the credential may read."
    },
    {
      "name": "Comments",
      "description": "Discussion threads attached to records. Visibility follows the record commented on."
    },
    {
      "name": "Notifications",
      "description": "In-app notifications for the user who owns the API key."
    },
    {
      "name": "Users",
      "description": "Team members within the authenticated organization."
    },
    {
      "name": "Settings",
      "description": "Organization-level configuration and branding."
    },
    {
      "name": "SignatureRequests",
      "description": "E-signature requests raised against bids and change orders."
    },
    {
      "name": "Webhooks",
      "description": "Outbound webhook endpoints and their delivery history."
    },
    {
      "name": "Jobs",
      "description": "Asynchronous work — CSV imports and exports — with progress and result artifacts."
    },
    {
      "name": "Observability",
      "description": "API audit logs and usage metering for the authenticated organization."
    },
    {
      "name": "OAuth",
      "description": "OAuth 2.1 authorization, token exchange and dynamic client registration."
    }
  ]
}
