{
  "openapi": "3.1.0",
  "info": {
    "title": "ServeLume API",
    "version": "1.0.0",
    "description": "REST API for the ServeLume field-service platform. All authenticated requests are scoped to the tenant that owns the access token and require a plan that includes the \"api\" feature for office resources. Core technician /me routes do not require that add-on and enforce assigned-job permissions.\n"
  },
  "servers": [
    {
      "url": "https://app.servelume.com/api/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/public/plans": {
      "get": {
        "summary": "List active public subscription plans and trial policy",
        "security": [],
        "responses": {
          "200": {
            "description": "Public plan fields and server-defined trial_days; no-store"
          },
          "429": {
            "description": "Too many requests"
          }
        }
      }
    },
    "/auth/token": {
      "post": {
        "summary": "Issue an access token",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password",
                  "device_name"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string",
                    "format": "password"
                  },
                  "device_name": {
                    "type": "string",
                    "example": "Zapier integration"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token issued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string",
                      "example": "Bearer"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid credentials"
          }
        }
      }
    },
    "/auth/me": {
      "get": {
        "summary": "Current user and tenant",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated"
          },
          "403": {
            "description": "Plan does not include API access"
          }
        }
      }
    },
    "/auth/logout": {
      "post": {
        "summary": "Revoke the current token",
        "responses": {
          "200": {
            "description": "Token revoked"
          }
        }
      }
    },
    "/clients": {
      "get": {
        "summary": "List clients",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated clients",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Client"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a client",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "company_name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "phone": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Client created"
          },
          "422": {
            "description": "Validation error"
          }
        }
      }
    },
    "/clients/{client}": {
      "get": {
        "summary": "Retrieve a client",
        "parameters": [
          {
            "name": "client",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/work-orders": {
      "get": {
        "summary": "List work orders",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated work orders"
          }
        }
      }
    },
    "/work-orders/{workOrder}": {
      "get": {
        "summary": "Retrieve a work order",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/estimates": {
      "get": {
        "summary": "List estimates",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated estimates"
          }
        }
      }
    },
    "/estimates/{estimate}": {
      "get": {
        "summary": "Retrieve an estimate",
        "parameters": [
          {
            "name": "estimate",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/invoices": {
      "get": {
        "summary": "List invoices",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated invoices"
          }
        }
      }
    },
    "/invoices/{invoice}": {
      "get": {
        "summary": "Retrieve an invoice",
        "parameters": [
          {
            "name": "invoice",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/requests": {
      "post": {
        "summary": "Create an inbound service request (lead)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "contact_name",
                  "contact_email",
                  "description"
                ],
                "properties": {
                  "contact_name": {
                    "type": "string"
                  },
                  "contact_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "contact_phone": {
                    "type": "string"
                  },
                  "category": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "preferred_at": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Request created"
          }
        }
      }
    },
    "/me/jobs/{workOrder}/start-travel": {
      "post": {
        "summary": "Start travel to an assigned job",
        "description": "Requires assigned-job execution permission and an active business. Moves an assigned job to en_route and saves its opted-in email notification atomically. An already en_route retry returns the current job without another message. After an uncertain response refresh the job before retrying; later statuses reject an obsolete transition. Delivery rechecks status, booking revision, assignment, recipient and preferences. No location payload is required.\n",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current job resource in the data envelope"
          },
          "401": {
            "description": "Unauthenticated"
          },
          "403": {
            "description": "Assignment",
            "permission or business status disallows travel": null
          },
          "404": {
            "description": "Job not found within the tenant"
          },
          "422": {
            "description": "Illegal lifecycle transition"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/me/jobs/{workOrder}/asset-services/commands/{commandId}": {
      "get": {
        "summary": "Reconcile an equipment service command",
        "description": "Checks under the job lock; recorded details require the original actor and current assigned-job execution permission. Retry absent open-job commands with the same UUID and original details.",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "commandId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "State is recorded, not_recorded or closed_unrecorded; data is the recorded service payload or null.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "state",
                    "data"
                  ],
                  "properties": {
                    "state": {
                      "type": "string",
                      "enum": [
                        "recorded",
                        "not_recorded",
                        "closed_unrecorded"
                      ]
                    },
                    "data": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/EquipmentServiceRecord"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Job permission or command ownership denied."
          },
          "404": {
            "description": "Job or UUID route unavailable."
          },
          "429": {
            "description": "Rate limit exceeded; 120 requests per minute."
          }
        }
      }
    },
    "/me/jobs/{workOrder}/asset-services": {
      "parameters": [
        {
          "name": "workOrder",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "summary": "List service records on an assigned job",
        "description": "Requires execution permission; captured equipment/actor details and summary with server service time and command UUID. Newest record first; 50 records per page.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Service data and current_page/last_page metadata; includes original and correction history.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EquipmentServiceRecord"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "current_page",
                        "last_page"
                      ],
                      "properties": {
                        "current_page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "last_page": {
                          "type": "integer",
                          "minimum": 1
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Assigned-job execution denied."
          },
          "404": {
            "description": "Job unavailable."
          },
          "429": {
            "description": "Rate limit exceeded; 120 requests per minute."
          }
        }
      },
      "post": {
        "summary": "Record explicit equipment servicing",
        "description": "Open job with live linked equipment at the matching property/client required. Persist command UUID before sending; repeat original details with the same UUID after uncertain response. Matching retries return original history even after closure/archive. No implicit maintenance-date advancement.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "asset_id",
                  "command_id",
                  "summary"
                ],
                "properties": {
                  "asset_id": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "command_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "summary": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 5000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Captured service record; matching retries return the same record.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EquipmentServiceRecord"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Assigned-job execution denied."
          },
          "404": {
            "description": "Job or live equipment unavailable for a new record."
          },
          "409": {
            "description": "Closed job or conflicting command/property/link rejected."
          },
          "422": {
            "description": "Invalid command UUID or summary."
          },
          "429": {
            "description": "Rate limit exceeded; 60 requests per minute."
          }
        }
      }
    },
    "/me/jobs/{workOrder}/assets": {
      "post": {
        "summary": "Link property equipment to an assigned job",
        "description": "Requires assigned-job execution permission. New links require an open job and active equipment belonging to its property and client. Linking is serialized under the job lock and captures equipment name/serial. Retry the same asset ID after an uncertain response; existing links return the original captured details even after job closure or asset archival, provided assignment and permission remain valid. No new archived link is allowed. Linking alone does not record servicing completion.\n",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "asset_id"
                ],
                "properties": {
                  "asset_id": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Captured link details for both new links and identical retries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "id",
                        "recorded_name",
                        "recorded_serial_number"
                      ],
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "recorded_name": {
                          "type": "string"
                        },
                        "recorded_serial_number": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Assignment or execution permission denied."
          },
          "404": {
            "description": "Job or equipment unavailable in this tenant."
          },
          "409": {
            "description": "New link rejected for closed job or archived/wrong-property equipment."
          },
          "422": {
            "description": "Invalid asset ID."
          },
          "429": {
            "description": "Rate limit exceeded; 60 requests per minute."
          }
        }
      },
      "get": {
        "summary": "List equipment linked to an assigned job",
        "description": "Requires assigned-job execution permission and matching tenant context. Captured visit name and serial are immutable link snapshots; other fields describe the current equipment record. Archived linked equipment remains visible but has no due-state warning. A link does not prove servicing. Internal notes and other client equipment are omitted. No offline cache or servicing mutation is provided by this endpoint.\n",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Linked equipment, 50 items per page in link order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JobEquipment"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "current_page",
                        "last_page"
                      ],
                      "properties": {
                        "current_page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "last_page": {
                          "type": "integer",
                          "minimum": 1
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Assignment, execution permission or tenant context denied."
          },
          "404": {
            "description": "Job unavailable in this tenant."
          },
          "429": {
            "description": "Rate limit exceeded; 120 requests per minute."
          }
        }
      }
    },
    "/me/jobs/{workOrder}/available-assets/{asset}": {
      "get": {
        "summary": "Resolve equipment label against an assigned job property",
        "description": "Current active equipment metadata only; requires matching job property/client and assigned execution permission. Resolving does not link or authorize servicing; writes recheck locked job state.",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "asset",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Equipment data with id/name and nullable serial_number."
          },
          "403": {
            "description": "Assignment or execution permission denied."
          },
          "404": {
            "description": "Job or active property equipment unavailable."
          },
          "429": {
            "description": "Rate limit exceeded; 120 requests per minute."
          }
        }
      }
    },
    "/me/jobs/{workOrder}/available-assets": {
      "get": {
        "summary": "Find equipment eligible for linking to an assigned job",
        "description": "Active equipment at the job property and client only; closed or propertyless jobs return an empty page. Requires execution permission.",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Equipment IDs, names and nullable serial numbers; current_page/last_page metadata, 50 items per page, name/ID order."
          },
          "403": {
            "description": "Assignment or execution permission denied."
          },
          "404": {
            "description": "Job unavailable in this tenant."
          },
          "422": {
            "description": "Invalid search input."
          },
          "429": {
            "description": "Rate limit exceeded; 120 requests per minute."
          }
        }
      }
    },
    "/me/jobs/{workOrder}/parts": {
      "parameters": [
        {
          "name": "workOrder",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "summary": "List available inventory for an assigned job",
        "description": "Active tenant parts matching job currency; cost fields are omitted. Requires job execution permission.",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Parts data and current_page/last_page metadata; 50 items per page."
          },
          "403": {
            "description": "Assigned-job execution permission required."
          },
          "404": {
            "description": "Job unavailable in this tenant."
          }
        }
      },
      "post": {
        "summary": "Record inventory consumed on an assigned job",
        "description": "Deducts whole-unit stock and captures current selling price. Persist the command UUID before sending and reuse it unchanged for retries. Identical retries return the original usage; conflicting reuse is rejected. The job must be open and the part active with matching currency. Cost fields are omitted. Requires job execution permission.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "part_id",
                  "quantity",
                  "command_id"
                ],
                "properties": {
                  "part_id": {
                    "type": "integer"
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 99999999
                  },
                  "command_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Recorded usage data; identical retries return the same record and status."
          },
          "403": {
            "description": "Assigned-job execution permission required."
          },
          "404": {
            "description": "Job or part unavailable in this tenant."
          },
          "409": {
            "description": "Closed job, insufficient stock, inactive/mismatched part, or conflicting command reference."
          },
          "422": {
            "description": "Invalid input."
          },
          "429": {
            "description": "Rate limit exceeded."
          }
        }
      }
    },
    "/me/jobs/{workOrder}/parts/commands/{commandId}": {
      "get": {
        "summary": "Reconcile a technician material command",
        "description": "Checks under tenant, job and current actor locks. Recorded data is returned only to the actor who submitted the command and still has assigned-job execution access. An absent command on a closed or invoiced job is reported as closed_unrecorded; an absent command on an open uninvoiced job may be retried with the same UUID.\n",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "commandId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "State is recorded, not_recorded, or closed_unrecorded; data contains usage only when recorded."
          },
          "403": {
            "description": "Job execution or command ownership denied."
          },
          "404": {
            "description": "Job or UUID route unavailable."
          },
          "429": {
            "description": "Rate limit exceeded."
          }
        }
      }
    },
    "/me/jobs/{workOrder}/parts/{usage}/returns": {
      "post": {
        "summary": "Return unused job parts to stock",
        "description": "Positive whole-unit return with immutable UUID retry receipt; only open uninvoiced assigned jobs accept new returns. Accepted preparation is saved and audited before stock execution so office staff can resolve interrupted requests. Retry original details; office cancellation prevents later stock restoration. Original usage remains retained and net quantity drives actual-material billing.",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "usage",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "quantity",
                  "reason",
                  "command_id"
                ],
                "properties": {
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 99999999
                  },
                  "reason": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 500
                  },
                  "command_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recorded receipt in data with id",
            "usage_id": null,
            "quantity": null,
            "reason and status. Exact retries return same receipt.": null
          },
          "403": {
            "description": "Current job execution denied."
          },
          "404": {
            "description": "Job or usage unavailable."
          },
          "422": {
            "description": "Invalid inputs or return not permitted; stock unchanged."
          },
          "429": {
            "description": "Rate limit exceeded."
          }
        }
      }
    },
    "/me/jobs/{workOrder}/part-returns/commands/{commandId}": {
      "get": {
        "summary": "Check an uncertain parts return",
        "description": "Does not cancel an in-flight request. Retry only original UUID and inputs when can_retry is true. Only own returns on currently accessible assigned jobs are disclosed.",
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "commandId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Data status recorded with receipt; cancelled with original usage_id/quantity/reason",
            "cancellation_reason and can_retry false; or not_recorded with can_retry boolean. Cancellation is definitive evidence that this command cannot restore stock.": null
          },
          "403": {
            "description": "Job access denied."
          },
          "404": {
            "description": "Job",
            "route or return ownership unavailable.": null
          },
          "429": {
            "description": "Rate limit exceeded."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "EquipmentServiceRecord": {
        "type": "object",
        "description": "Append-only service history. Corrections retain original serviced_at; recorded_at identifies the correction time. A superseded record is historical and its replacement should be consulted.",
        "required": [
          "id",
          "asset_id",
          "asset_name",
          "serial_number",
          "recorded_by_name",
          "summary",
          "serviced_at",
          "command_id",
          "corrects_record_id",
          "correction_reason",
          "superseded_by_record_id",
          "recorded_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "asset_id": {
            "type": "integer"
          },
          "asset_name": {
            "type": "string"
          },
          "serial_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "recorded_by_name": {
            "type": "string"
          },
          "summary": {
            "type": "string",
            "minLength": 3,
            "maxLength": 5000
          },
          "serviced_at": {
            "type": "string",
            "format": "date-time"
          },
          "command_id": {
            "type": "string",
            "format": "uuid"
          },
          "corrects_record_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "correction_reason": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "superseded_by_record_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "recorded_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "JobEquipment": {
        "type": "object",
        "required": [
          "id",
          "recorded_name",
          "recorded_serial_number",
          "name",
          "serial_number",
          "type",
          "manufacturer",
          "model",
          "installed_at",
          "warranty_expires_at",
          "next_service_due_at",
          "service_due_state",
          "archived",
          "linked_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "recorded_name": {
            "type": "string"
          },
          "recorded_serial_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "serial_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturer": {
            "type": [
              "string",
              "null"
            ]
          },
          "model": {
            "type": [
              "string",
              "null"
            ]
          },
          "installed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "warranty_expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "next_service_due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "service_due_state": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "overdue",
              "today",
              "upcoming",
              null
            ]
          },
          "archived": {
            "type": "boolean"
          },
          "linked_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Client": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "company_name": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MoneyLine": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": "number"
          },
          "unit_price_minor": {
            "type": "integer"
          },
          "tax_rate": {
            "type": "number"
          },
          "line_total_minor": {
            "type": "integer"
          }
        }
      }
    }
  }
}
