{
  "openapi": "3.1.0",
  "info": {
    "title": "HailMate API",
    "version": "2.1.0",
    "summary": "Jobs, contacts, tasks, money, photos, door knocks and hail history for your HailMate workspace.",
    "description": "The HailMate API lets another app read and write your workspace's records,\nand have HailMate call it the moment something happens.\n\n**Authentication.** Create an API key in HailMate under\n**Settings → Integrations → API & Webhooks** and send it as\n`Authorization: Bearer hm_live_…` (or `X-API-Key: hm_live_…`). A key\nbelongs to ONE workspace and can only ever see that workspace's records.\nA key is either **full access** or **read only**.\n\n**Log in with HailMate (OAuth 2.0).** An app that connects many\nHailMate companies — Zapier is the first — sends the person to\n`https://app.hailmate.ai/oauth/authorize`, where they pick a company and\npress Allow; the app then trades the code for an access token at\n`POST /oauth/token` and sends it as `Authorization: Bearer hm_oat_…`.\nAccess tokens last an hour; the refresh token renews them. Apps are\nregistered by HailMate — write to contact@hailmate.ai.\n\n**Limits.** 120 requests a minute per key. Every response carries\n`X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`, and a\n`429` carries `Retry-After`.\n\n**Pagination.** Lists are newest first, paged with an opaque cursor: pass\n`next_cursor` back as `?cursor=` until it is `null`.\n\n**Idempotency.** Send `Idempotency-Key: <any unique string>` on a POST,\nPATCH or DELETE and a retry with the same key replays the first answer\ninstead of doing the work twice. Keys are remembered for 24 hours.\n\n**Errors.** Every error is `{ \"error\": { \"code\", \"message\", \"field?\",\n\"request_id\", \"doc_url\" } }`. Quote `request_id` (also sent as\n`X-Request-Id`) when you contact us.\n\n**Webhooks.** Subscribe a URL to any of the events below and HailMate\nposts it a signed JSON body within seconds of the change. See the\n`webhooks` section of this document and https://hailmate.ai/docs/api/webhooks.\n",
    "contact": {
      "name": "HailMate support",
      "email": "contact@hailmate.ai",
      "url": "https://hailmate.ai/docs/api"
    },
    "termsOfService": "https://hailmate.ai/terms",
    "license": {
      "name": "Proprietary",
      "identifier": "LicenseRef-Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://app.hailmate.ai/api/v1",
      "description": "Production. There is no sandbox — use a separate workspace to test."
    }
  ],
  "security": [
    {
      "BearerAuth": []
    },
    {
      "ApiKeyHeader": []
    },
    {
      "OAuth2": []
    }
  ],
  "tags": [
    {
      "name": "Connection",
      "description": "Check a key and see which workspace it belongs to."
    },
    {
      "name": "Jobs",
      "description": "The storm-restoration jobs on your pipeline boards."
    },
    {
      "name": "Contacts",
      "description": "Homeowners, adjusters, subcontractors and everyone else in your book."
    },
    {
      "name": "Tasks",
      "description": "Tasks and appointments. An appointment is a task with an `appointment_type`."
    },
    {
      "name": "Estimates",
      "description": "Estimates and proposals — made from one of your templates or as your standard proposal, then finished and sent in HailMate."
    },
    {
      "name": "Invoices",
      "description": "Draft invoices, totalled exactly as HailMate totals them; mark them sent, or void them."
    },
    {
      "name": "Payments",
      "description": "Money received against a job — carrier cheques, deductibles, card payments."
    },
    {
      "name": "Notes",
      "description": "Notes on a job's timeline."
    },
    {
      "name": "Files",
      "description": "Photos and documents on a job."
    },
    {
      "name": "Canvassing",
      "description": "Door knocks — pins on the canvassing map with a knock result."
    },
    {
      "name": "Storm Lists",
      "description": "Every property under hail of a chosen size, with the owner of record (read only)."
    },
    {
      "name": "Hail",
      "description": "Hail and damaging-wind history at an address."
    },
    {
      "name": "Lookups",
      "description": "Pipelines, stages, teammates and the event catalogue — what a dropdown is built from."
    },
    {
      "name": "Webhooks",
      "description": "Subscribe a URL to events."
    },
    {
      "name": "OAuth",
      "description": "Log in with HailMate: the token endpoints for an app that connects on a person's behalf."
    }
  ],
  "paths": {
    "/ping": {
      "get": {
        "tags": [
          "Connection"
        ],
        "operationId": "ping",
        "summary": "Check a key",
        "description": "The cheapest authenticated call. Returns the workspace the key belongs to and what the key may do. `/me` is the same endpoint.\n",
        "responses": {
          "200": {
            "description": "The key is valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ping"
                },
                "example": {
                  "ok": true,
                  "object": "workspace",
                  "workspace_id": "5f0c2a8e-1b3d-4c7a-9e21-7a3b8d4c6e10",
                  "workspace": "Ridgeline Roofing",
                  "api_version": "v1",
                  "key": {
                    "name": "Zapier",
                    "access": "write"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PlanRequired"
          }
        }
      }
    },
    "/jobs": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "listJobs",
        "summary": "List jobs",
        "description": "Newest first. Archived jobs are left out unless you ask for them.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "name": "stage",
            "in": "query",
            "description": "A stage `key` from `/stages`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "pipeline_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "job_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "insurance",
                "retail"
              ]
            }
          },
          {
            "name": "assigned_to",
            "in": "query",
            "description": "A user id. Matches the primary assignee OR any co-assignee.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "description": "Jobs where this contact is the homeowner, the adjuster, or a secondary contact.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "archived",
            "in": "query",
            "description": "Omitted, only live jobs. `true` returns archived jobs, `any` returns both.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "any"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Jobs"
        ],
        "operationId": "createJob",
        "summary": "Create a job",
        "description": "Only `name` and `address` are required. With no `stage` the job lands\non the first column of your default pipeline. `stage` may be a stage\n**key** or its **label** as your board shows it (\"Claim Approved\").\n`notes`, when sent, becomes the first note on the job's timeline.\n\nA homeowner, adjuster or assignee named by id must belong to this\nworkspace. The new job fires `job.created`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JobCreate"
              },
              "example": {
                "name": "1804 Cedar Ridge Dr",
                "address": "1804 Cedar Ridge Dr",
                "city": "Plano",
                "state": "TX",
                "postal_code": "75024",
                "job_type": "insurance",
                "stage": "Inspection Scheduled",
                "homeowner_id": "b7c2d3e4-4444-4000-8000-0000000000dd",
                "lead_source": "Website",
                "insurance_company": "Example Mutual",
                "claim_number": "CLM-88213",
                "notes": "Homeowner reports hail on the back slope."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/jobs/search": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "searchJobs",
        "summary": "Find jobs",
        "description": "`address` and `name` match part of the value (\"1804 Cedar\" finds \"1804 Cedar Ridge Dr\"); `job_number` and `claim_number` must match whole (case-insensitive). With no criteria the result is EMPTY — never \"the newest jobs\", which is how a blank search step writes to the wrong job.\n",
        "parameters": [
          {
            "name": "address",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "job_number",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "claim_number",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching jobs, newest first. Not paged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/jobs/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "getJob",
        "summary": "Get a job",
        "responses": {
          "200": {
            "description": "The job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Jobs"
        ],
        "operationId": "updateJob",
        "summary": "Update a job",
        "description": "Send only the fields to change. A field sent as `null` (or `\"\"`) is\ncleared; a field left out is untouched. `name` and `address` cannot be\ncleared.\n\n**Moving a job** is `{\"stage\": \"Claim Approved\"}` — a key or a label.\nIt runs every stage automation, writes the timeline entry and fires\n`job.stage_changed`, exactly as dragging the card does. Setting\n`assigned_to` makes that teammate the primary assignee.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JobUpdate"
              },
              "example": {
                "stage": "claim_approved",
                "rcv_amount": 24380.5,
                "assigned_to": "5d0e1c2b-7777-4000-8000-0000000000ab"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The job, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Jobs"
        ],
        "operationId": "deleteJob",
        "summary": "Delete a job",
        "description": "The job goes to **Settings → Recently Deleted** for 30 days with everything on it, and can be restored from there. Fires `job.deleted`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                },
                "example": {
                  "id": "f2b1a0c4-1111-4000-8000-0000000000aa",
                  "object": "job",
                  "deleted": true
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/jobs/{id}/notes": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "listJobNotes",
        "summary": "List a job's notes",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of notes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NoteList"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/jobs/{id}/files": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "listJobFiles",
        "summary": "List a job's photos and documents",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "photo",
                "document",
                "scope",
                "estimate",
                "contract",
                "other"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of files.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileList"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/jobs/{id}/photos": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "listJobPhotos",
        "summary": "List a job's photos",
        "description": "`/jobs/{id}/files?category=photo`, as its own path.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of photos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/jobs/{id}/tasks": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "listJobTasks",
        "summary": "List a job's tasks and appointments",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of tasks.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/jobs/{id}/estimates": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "listJobEstimates",
        "summary": "List a job's estimates",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of estimates.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EstimateList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/jobs/{id}/invoices": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "listJobInvoices",
        "summary": "List a job's invoices",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of invoices.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/jobs/{id}/payments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "listJobPayments",
        "summary": "List a job's payments",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of payments.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "listContacts",
        "summary": "List contacts",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ContactType"
            }
          },
          {
            "name": "assigned_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "operationId": "createContact",
        "summary": "Create a contact",
        "description": "At least one of a name, `email`, `phone` or `company` is required — a\ncontact with none of them is refused rather than filling your CRM with\nblank rows. Send `name` alone and it is split into first and last.\n\nUS phone numbers are stored as `(615) 555-0148`, the way your team\ntypes them. `sms_consent` cannot be set: no integration can establish\nthat a homeowner agreed to be texted.\n\nTo avoid duplicates, search first — `GET /contacts/search` matches a\nphone number however it was typed.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactCreate"
              },
              "example": {
                "first_name": "Dana",
                "last_name": "Reed",
                "email": "dana@example.com",
                "phone": "+19725550148",
                "address": "1804 Cedar Ridge Dr",
                "city": "Plano",
                "state": "TX",
                "postal_code": "75024"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          }
        }
      }
    },
    "/contacts/search": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "searchContacts",
        "summary": "Find contacts",
        "description": "Tries `email` first, then `phone`, then `name`, and returns the first that matches. A phone number matches however it was typed into HailMate — `+16155550148`, `615-555-0148` and `(615) 555-0148` are the same number. `name` matches a first name, last name, \"first last\" or a company. With no criteria the result is empty.\n",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "phone",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching contacts. Not paged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/contacts/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "getContact",
        "summary": "Get a contact",
        "responses": {
          "200": {
            "description": "The contact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Contacts"
        ],
        "operationId": "updateContact",
        "summary": "Update a contact",
        "description": "Send only the fields to change; `null` clears a field.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactUpdate"
              },
              "example": {
                "email": "dana.reed@example.com",
                "notes": "Prefers texts after 5pm."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The contact, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Contacts"
        ],
        "operationId": "deleteContact",
        "summary": "Delete a contact",
        "description": "Kept for 30 days under Settings → Recently Deleted. Fires `contact.deleted`.",
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/contacts/{id}/jobs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "listContactJobs",
        "summary": "List a contact's jobs",
        "description": "Every job this person is on — as homeowner, adjuster or secondary contact — archived included.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/tasks": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "operationId": "listTasks",
        "summary": "List tasks and appointments",
        "description": "`/appointments` is the same list narrowed to appointments.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "name": "completed",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          },
          {
            "name": "appointments",
            "in": "query",
            "description": "`true` returns appointments only.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          },
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "assigned_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "due_after",
            "in": "query",
            "description": "Wall-clock, e.g. `2026-10-01` or `2026-10-01T08:00:00`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "due_before",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of tasks.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Tasks"
        ],
        "operationId": "createTask",
        "summary": "Create a task or an appointment",
        "description": "Set `appointment_type` (`inspection`, `adjuster_meeting`, `build_day`)\nto make it an appointment; `POST /appointments` requires one.\n\n**Times are wall-clock.** `due_date` is the local time your crew will\nread. If you send an offset (`2026-10-02T15:00:00-05:00`, which is what\nZapier sends) the offset is dropped and 3:00 PM is kept — HailMate\nnever shifts an appointment by a time zone.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskCreate"
              },
              "example": {
                "title": "Adjuster meeting — 1804 Cedar Ridge Dr",
                "due_date": "2026-10-02T15:00:00",
                "duration_minutes": 60,
                "appointment_type": "adjuster_meeting",
                "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
                "assigned_to": "5d0e1c2b-7777-4000-8000-0000000000ab"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          }
        }
      }
    },
    "/tasks/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Tasks"
        ],
        "operationId": "getTask",
        "summary": "Get a task",
        "responses": {
          "200": {
            "description": "The task.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Tasks"
        ],
        "operationId": "updateTask",
        "summary": "Update a task",
        "description": "Reschedule it, reassign it, or finish it with `{\"completed\": true}` (fires `task.completed`). `null` clears a field.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskUpdate"
              },
              "example": {
                "completed": true,
                "outcome": "completed"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The task, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Tasks"
        ],
        "operationId": "deleteTask",
        "summary": "Delete a task",
        "description": "Permanent. Fires `task.deleted`.",
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/estimates": {
      "get": {
        "tags": [
          "Estimates"
        ],
        "operationId": "listEstimates",
        "summary": "List estimates",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "sent",
                "viewed",
                "signed",
                "expired",
                "declined"
              ]
            }
          },
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "estimate_number",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of estimates.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EstimateList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Estimates"
        ],
        "operationId": "createEstimate",
        "summary": "Create an estimate",
        "description": "Makes a **draft** estimate on a job, one of two ways — the same two the\napp offers:\n\n- **From one of your templates** (`template_id`, from\n  `GET /estimate_templates`): an exact copy, with its smart fields\n  (`{{customer_name}}`, `{{property_address}}`…) filled in from the job.\n- **Your standard proposal**: cover, introduction, Scope of Work,\n  warranty, terms and signature. The Scope of Work is the lines you\n  send, or HailMate's standard checklist when you send none.\n\n`price` states one price for the whole job, which is what the homeowner\nsees as the total. The estimate is not sent — a person finishes and\nsends it in HailMate. Fires `estimate.created`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateCreate"
              },
              "example": {
                "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
                "template_id": "7a1c9e02-2222-4000-8000-0000000000c3",
                "price": 14500
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created, as a draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Estimate"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/estimates/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Estimates"
        ],
        "operationId": "getEstimate",
        "summary": "Get an estimate",
        "responses": {
          "200": {
            "description": "The estimate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Estimate"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Estimates"
        ],
        "operationId": "updateEstimate",
        "summary": "Update an estimate",
        "description": "Changes what sits outside the document: its title, how long it is\nvalid, and one stated price (`null` takes the price off). The sections\nand lines are edited in HailMate. Refused with `409` once the estimate\nis out for signature or signed.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateUpdate"
              },
              "example": {
                "price": 15250,
                "valid_until": "2026-12-01"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The estimate, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Estimate"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/estimate_templates": {
      "get": {
        "tags": [
          "Estimates"
        ],
        "operationId": "listEstimateTemplates",
        "summary": "List estimate templates",
        "description": "The workspace's own templates, most recently changed first — what a \"which template?\" dropdown lists.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "The templates.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EstimateTemplateList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/invoices": {
      "get": {
        "tags": [
          "Invoices"
        ],
        "operationId": "listInvoices",
        "summary": "List invoices",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "sent",
                "viewed",
                "partially_paid",
                "paid",
                "overdue",
                "void",
                "bad_debt"
              ]
            }
          },
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "invoice_number",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of invoices.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Invoices"
        ],
        "operationId": "createInvoice",
        "summary": "Create an invoice",
        "description": "Makes a **draft** invoice for a job (`job_id`) or a contact\n(`contact_id`), with at least one line. HailMate numbers it\n(`INV-0312`) and totals it with the same calculator the app uses: lines,\nminus any discount, plus tax on the taxable lines. A negative price is a\ncredit. `purpose` fills the matching figure on the job — a `deductible`\ninvoice sets the job's deductible — exactly as in the app.\n\nA draft is not sent. Send it from HailMate, or call `mark_sent` when it\nwent out another way. Fires `invoice.created`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceCreate"
              },
              "example": {
                "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
                "title": "Deductible",
                "purpose": "deductible",
                "due_date": "2026-10-12",
                "line_items": [
                  {
                    "name": "Insurance deductible",
                    "quantity": 1,
                    "unit_price": 2500
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created, as a draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/invoices/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Invoices"
        ],
        "operationId": "getInvoice",
        "summary": "Get an invoice",
        "responses": {
          "200": {
            "description": "The invoice.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Invoices"
        ],
        "operationId": "updateInvoice",
        "summary": "Update a draft invoice",
        "description": "Only a **draft** can change — once an invoice is sent the homeowner\nholds a copy, so a sent one is voided and replaced instead (`409`).\n`line_items`, when sent, replace every line; the totals are worked out\nagain either way.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceUpdate"
              },
              "example": {
                "due_date": "2026-10-20",
                "tax_rate": 8.25
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The invoice, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      },
      "delete": {
        "tags": [
          "Invoices"
        ],
        "operationId": "deleteInvoice",
        "summary": "Delete a draft invoice",
        "description": "Only a draft. A sent invoice is voided instead, so its history stays on the job.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                },
                "example": {
                  "id": "c90d7b31-3333-4000-8000-0000000000f2",
                  "object": "invoice",
                  "deleted": true
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/invoices/{id}/mark_sent": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "tags": [
          "Invoices"
        ],
        "operationId": "markInvoiceSent",
        "summary": "Mark an invoice sent",
        "description": "Records that the invoice went out some other way. A draft becomes\n`sent`, which turns on the homeowner's pay page (`payment_url`); an\ninvoice already out keeps its status. Refused for a void invoice.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The invoice, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/invoices/{id}/void": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "tags": [
          "Invoices"
        ],
        "operationId": "voidInvoice",
        "summary": "Void an invoice",
        "description": "Voids the invoice. Voiding one that is already void answers with it, unchanged.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The invoice, now void.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/payments": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "listPayments",
        "summary": "List payments",
        "description": "A refund is its own row with a negative `amount`, so summing `amount` gives the net.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "invoice_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "payment_type",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PaymentType"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of payments.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "createPayment",
        "summary": "Record a payment",
        "description": "Records money that arrived — a cheque logged in your accounting app, a\ndeposit from a bank feed. The job's money and, when you name one, the\ninvoice's balance and status update exactly as they do for a payment\nlogged in HailMate.\n\n**The invoice it pays is never guessed.** Send `invoice_id` to apply it\nto an invoice on the same job; leave it out and the payment sits on the\njob. Fires `payment.received`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentCreate"
              },
              "example": {
                "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
                "amount": 13164.2,
                "payment_type": "acv",
                "payer_type": "insurance",
                "payment_method": "check",
                "check_number": "004471",
                "date_received": "2026-09-28"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          }
        }
      }
    },
    "/payments/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "getPayment",
        "summary": "Get a payment",
        "responses": {
          "200": {
            "description": "The payment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/notes": {
      "get": {
        "tags": [
          "Notes"
        ],
        "operationId": "listNotes",
        "summary": "List notes",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of notes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NoteList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Notes"
        ],
        "operationId": "createNote",
        "summary": "Add a note to a job",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NoteCreate"
              },
              "example": {
                "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
                "content": "Adjuster confirmed for Thursday at 3."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Note"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          }
        }
      }
    },
    "/notes/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Notes"
        ],
        "operationId": "getNote",
        "summary": "Get a note",
        "responses": {
          "200": {
            "description": "The note.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Note"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/files": {
      "get": {
        "tags": [
          "Files"
        ],
        "operationId": "listFiles",
        "summary": "List photos and documents",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "photo",
                "document",
                "scope",
                "estimate",
                "contract",
                "other"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of files.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Files"
        ],
        "operationId": "uploadFile",
        "summary": "Upload a photo or document to a job",
        "description": "Two ways in:\n\n- **multipart/form-data** with a `file` field and a `job_id` field\n  (plus optional `category`, `description`, `file_name`).\n- **JSON** with a public `file_url` HailMate downloads for you.\n\nUp to 25 MB. A JPEG, PNG, WebP, GIF or MP4 is filed as a **photo**;\nanything else (a PDF, a HEIC, a spreadsheet) as a **document**, so it\nnever shows as a broken tile. Fires `file.created`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/FileUploadForm"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FileFromUrl"
              },
              "example": {
                "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
                "file_url": "https://example.com/claim/scope-of-loss.pdf",
                "category": "scope"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "413": {
            "description": "The file is larger than 25 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/files/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Files"
        ],
        "operationId": "getFile",
        "summary": "Get a file",
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/pins": {
      "get": {
        "tags": [
          "Canvassing"
        ],
        "operationId": "listPins",
        "summary": "List canvassing pins",
        "description": "Every door a rep dropped a pin on, newest first.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "name": "knock_result",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/KnockResult"
            }
          },
          {
            "name": "pin_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "knock",
                "inspection",
                "job"
              ]
            }
          },
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "created_by",
            "in": "query",
            "description": "The rep who dropped the pin.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of pins.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Canvassing"
        ],
        "operationId": "createPin",
        "summary": "Log a door knock",
        "description": "Drops a pin on the canvassing map. Send `latitude` and `longitude`, or\nan `address` and HailMate places it. It is credited to the teammate in\n`created_by_id`, else to whoever connected the integration. Fires\n`pin.created`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PinCreate"
              },
              "example": {
                "address": "1804 Cedar Ridge Dr",
                "city": "Plano",
                "state": "TX",
                "postal_code": "75024",
                "knock_result": "interested",
                "homeowner_name": "Dana Reed"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Logged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pin"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/pins/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Canvassing"
        ],
        "operationId": "getPin",
        "summary": "Get a pin",
        "responses": {
          "200": {
            "description": "The pin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pin"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Canvassing"
        ],
        "operationId": "updatePin",
        "summary": "Update a door knock",
        "description": "Most often its knock result. The rep who logged it never changes. Fires `pin.result_changed` when the result moves.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PinUpdate"
              },
              "example": {
                "knock_result": "appointment_scheduled"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The pin, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pin"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Canvassing"
        ],
        "operationId": "deletePin",
        "summary": "Delete a door knock",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                },
                "example": {
                  "id": "0e4c2a19-8888-4000-8000-0000000000c1",
                  "object": "pin",
                  "deleted": true
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/storm_lists": {
      "get": {
        "tags": [
          "Storm Lists"
        ],
        "operationId": "listStormLists",
        "summary": "List storm lists",
        "description": "Read only — building a list spends data credits and happens in HailMate. `source: auto` is a nightly build off the hail data; `manual` is one somebody made.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "building",
                "ready",
                "partial",
                "failed"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of storm lists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StormListList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/storm_lists/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Storm Lists"
        ],
        "operationId": "getStormList",
        "summary": "Get a storm list",
        "responses": {
          "200": {
            "description": "The storm list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StormList"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/storm_lists/{id}/properties": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Storm Lists"
        ],
        "operationId": "listStormListProperties",
        "summary": "List the properties on a storm list",
        "description": "The rows a mail house wants. `mail_deliverable` is our own judgement from the address we hold — it is NOT a CASS or NCOA result; run your own validation before you spend postage.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "mailable",
            "in": "query",
            "description": "`true` drops properties with no usable mailing address or owner name.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          },
          {
            "name": "residential",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of properties.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StormListPropertyList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/hail_history": {
      "get": {
        "tags": [
          "Hail"
        ],
        "operationId": "getHailHistory",
        "summary": "Hail and wind history at an address",
        "description": "Every hail day and damaging-wind day at an address (or a latitude and\nlongitude) since `since` — the same Evidence Grade™ ruling HailMate\nshows on its maps and storm reports, combining radar-estimated hail\nsize, severe-hail probability and verified ground reports.\n\n`confidence` is `confirmed` (a ground report backs the size), `likely`\nor `radar` (radar only). Default window: the last five years. United\nStates only. Limited to 20 lookups a minute per key, inside the\ngeneral limit.\n",
        "parameters": [
          {
            "name": "address",
            "in": "query",
            "description": "A US street address. Or send `latitude` and `longitude`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "latitude",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "longitude",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "since",
            "in": "query",
            "description": "`YYYY-MM-DD`. Defaults to five years ago.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The history.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HailReport"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/pipelines": {
      "get": {
        "tags": [
          "Lookups"
        ],
        "operationId": "listPipelines",
        "summary": "List pipelines and their stages",
        "responses": {
          "200": {
            "description": "Every pipeline, default first, with its stages in board order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Pipeline"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/stages": {
      "get": {
        "tags": [
          "Lookups"
        ],
        "operationId": "listStages",
        "summary": "List pipeline stages",
        "description": "Every stage on every board. `key` is what you send and filter on; `label` is what to show a person. `/pipeline_stages` is the same list.\n",
        "responses": {
          "200": {
            "description": "The stages, default pipeline first, in board order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PipelineStage"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/users": {
      "get": {
        "tags": [
          "Lookups"
        ],
        "operationId": "listUsers",
        "summary": "List your team",
        "responses": {
          "200": {
            "description": "The workspace's people.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/events": {
      "get": {
        "tags": [
          "Lookups"
        ],
        "operationId": "listEvents",
        "summary": "List every webhook event",
        "responses": {
          "200": {
            "description": "The event catalogue, with the filters each one accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EventDefinition"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhooks",
        "summary": "List this key's webhooks",
        "description": "A key only ever sees the subscriptions it created.",
        "responses": {
          "200": {
            "description": "The subscriptions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhook",
        "summary": "Subscribe to an event",
        "description": "Send `event` (one event, or `\"*\"` for every event including ones added\nlater) or `events` (several). The URL must be `https`, public, and not\na private or link-local address. The response carries the signing\n`secret` — **once**. Several events for one URL share one secret.\n\n`filters` narrows a single-event subscription, e.g.\n`{\"stage\": \"Claim Approved\"}` on `job.stage_changed` fires only when a\njob enters that stage. `GET /events` lists what each event accepts.\n\nSubscribing the same event, URL and filters twice returns the existing\nsubscription. A read-only key can subscribe.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              },
              "example": {
                "event": "job.stage_changed",
                "target_url": "https://yourapp.example.com/hooks/hailmate",
                "filters": {
                  "stage": "claim_approved"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscribed. One event returns the subscription; several return `{ \"object\": \"list\", \"data\": [...], \"secret\": \"…\" }`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          }
        }
      }
    },
    "/webhooks/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhook",
        "summary": "Get a webhook",
        "responses": {
          "200": {
            "description": "The subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook",
        "summary": "Unsubscribe",
        "description": "Answering a delivery with **410 Gone** unsubscribes too.",
        "responses": {
          "200": {
            "description": "Removed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks/samples/{event}": {
      "parameters": [
        {
          "name": "event",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "example": "job.created"
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhookSamples",
        "summary": "See what an event delivers",
        "description": "Up to three REAL recent records from your workspace, wrapped exactly as a delivery of that event wraps them. Use it to map fields before any event has happened.\n",
        "responses": {
          "200": {
            "description": "Sample deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "tags": [
          "OAuth"
        ],
        "operationId": "oauthToken",
        "summary": "Get or refresh an access token",
        "description": "The token endpoint of the OAuth 2.0 authorization-code flow. After the\nperson presses Allow at `https://app.hailmate.ai/oauth/authorize`, your\napp is sent back with `?code=…&state=…`; trade the code here within ten\nminutes. The access token lasts an hour — trade the refresh token for a\nnew one. The refresh token does not change.\n\nAuthenticate your app with `client_id` and `client_secret` in the body\nor as HTTP Basic. The body may be form-encoded (the standard) or JSON.\nErrors use the OAuth standard's own shape: `{ \"error\": \"invalid_grant\",\n\"error_description\": \"…\" }`. A code used twice disconnects the\nconnection it made.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              },
              "example": {
                "grant_type": "authorization_code",
                "code": "hm_oac_3v7k2m9q4x8w1c6t5r0p2n8j4h6g2f0d9s7a5l3e",
                "redirect_uri": "https://zapier.com/dashboard/auth/oauth/return/App246952CLIAPI/",
                "client_id": "your_client_id",
                "client_secret": "your_client_secret"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthToken"
                }
              }
            }
          },
          "400": {
            "description": "The code or refresh token is not valid (`invalid_grant`), or the request is malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "Unknown app or wrong secret (`invalid_client`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/revoke": {
      "post": {
        "tags": [
          "OAuth"
        ],
        "operationId": "oauthRevoke",
        "summary": "Disconnect",
        "description": "RFC 7009. Send a refresh token to disconnect the connection — its\ntokens stop working and the webhooks it set up are switched off — or an\naccess token to retire just that token. Always answers `200`.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthRevokeRequest"
              },
              "example": {
                "token": "hm_ort_8d2k6m1q9x4w7c3t0r5p6n2j8h1g4f7d3s9a2l6e",
                "client_id": "your_client_id",
                "client_secret": "your_client_secret"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "example": {}
                }
              }
            }
          },
          "401": {
            "description": "Unknown app or wrong secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "job.created": {
      "post": {
        "operationId": "jobCreatedEvent",
        "summary": "A job was created",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Job"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "job.updated": {
      "post": {
        "operationId": "jobUpdatedEvent",
        "summary": "A job changed (`changed_fields` says what)",
        "description": "Several quick changes to one job arrive as one delivery carrying every changed field.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Job"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "job.stage_changed": {
      "post": {
        "operationId": "jobStageChangedEvent",
        "summary": "A job moved stage (`previous_stage` says from where)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Job"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "job.assigned": {
      "post": {
        "operationId": "jobAssignedEvent",
        "summary": "A job got a new primary assignee",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Job"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "job.deleted": {
      "post": {
        "operationId": "jobDeletedEvent",
        "summary": "A job was deleted",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/DeletedSnapshot"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "contact.created": {
      "post": {
        "operationId": "contactCreatedEvent",
        "summary": "A contact was added",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Contact"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "contact.updated": {
      "post": {
        "operationId": "contactUpdatedEvent",
        "summary": "A contact changed",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Contact"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "contact.deleted": {
      "post": {
        "operationId": "contactDeletedEvent",
        "summary": "A contact was deleted",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/DeletedSnapshot"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "task.created": {
      "post": {
        "operationId": "taskCreatedEvent",
        "summary": "A task or appointment was created",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Task"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "task.updated": {
      "post": {
        "operationId": "taskUpdatedEvent",
        "summary": "A task changed",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Task"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "task.completed": {
      "post": {
        "operationId": "taskCompletedEvent",
        "summary": "A task was marked done",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Task"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "task.deleted": {
      "post": {
        "operationId": "taskDeletedEvent",
        "summary": "A task was deleted",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/DeletedSnapshot"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "appointment.created": {
      "post": {
        "operationId": "appointmentCreatedEvent",
        "summary": "An appointment was scheduled",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Task"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "estimate.created": {
      "post": {
        "operationId": "estimateCreatedEvent",
        "summary": "An estimate was created",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Estimate"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "estimate.sent": {
      "post": {
        "operationId": "estimateSentEvent",
        "summary": "An estimate went out",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Estimate"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "estimate.viewed": {
      "post": {
        "operationId": "estimateViewedEvent",
        "summary": "The homeowner opened an estimate for the first time",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Estimate"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "estimate.signed": {
      "post": {
        "operationId": "estimateSignedEvent",
        "summary": "A homeowner signed an estimate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Estimate"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "estimate.declined": {
      "post": {
        "operationId": "estimateDeclinedEvent",
        "summary": "A homeowner declined an estimate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Estimate"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "invoice.created": {
      "post": {
        "operationId": "invoiceCreatedEvent",
        "summary": "An invoice was created",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Invoice"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "invoice.sent": {
      "post": {
        "operationId": "invoiceSentEvent",
        "summary": "An invoice was sent",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Invoice"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "invoice.paid": {
      "post": {
        "operationId": "invoicePaidEvent",
        "summary": "An invoice was paid in full",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Invoice"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "invoice.overdue": {
      "post": {
        "operationId": "invoiceOverdueEvent",
        "summary": "An invoice went past due",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Invoice"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "invoice.voided": {
      "post": {
        "operationId": "invoiceVoidedEvent",
        "summary": "An invoice was voided",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Invoice"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "payment.received": {
      "post": {
        "operationId": "paymentReceivedEvent",
        "summary": "Money came in on a job",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Payment"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "payment.refunded": {
      "post": {
        "operationId": "paymentRefundedEvent",
        "summary": "A payment was refunded (negative amount)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Payment"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "note.created": {
      "post": {
        "operationId": "noteCreatedEvent",
        "summary": "A note was added to a job",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Note"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "file.created": {
      "post": {
        "operationId": "fileCreatedEvent",
        "summary": "A photo or document was added to a job",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/File"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "pin.created": {
      "post": {
        "operationId": "pinCreatedEvent",
        "summary": "A rep dropped a pin on the canvassing map",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Pin"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "pin.result_changed": {
      "post": {
        "operationId": "pinResultChangedEvent",
        "summary": "A pin got a new knock result",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/Pin"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    },
    "storm_list.ready": {
      "post": {
        "operationId": "stormListReadyEvent",
        "summary": "A storm list finished building",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "$ref": "#/components/schemas/StormList"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx settles the delivery."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "`Authorization: Bearer hm_live_…`"
      },
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      },
      "OAuth2": {
        "type": "oauth2",
        "description": "Log in with HailMate. Apps are registered by HailMate — write to contact@hailmate.ai.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.hailmate.ai/oauth/authorize",
            "tokenUrl": "https://app.hailmate.ai/api/v1/oauth/token",
            "refreshUrl": "https://app.hailmate.ai/api/v1/oauth/token",
            "scopes": {
              "read": "See records and be told when they change.",
              "write": "See, create and change records."
            }
          }
        }
      }
    },
    "parameters": {
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "description": "Page size. Out-of-range values fall back to the default.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "The `next_cursor` from the previous page, exactly as received. A cursor we did not issue is a 400.",
        "schema": {
          "type": "string"
        }
      },
      "UpdatedSince": {
        "name": "updated_since",
        "in": "query",
        "description": "Only records changed at or after this ISO date or date-time. The way to sync only what moved.",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "CreatedAfter": {
        "name": "created_after",
        "in": "query",
        "description": "Only records created at or after this ISO date or date-time.",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "CreatedBefore": {
        "name": "created_before",
        "in": "query",
        "description": "Only records created before this ISO date or date-time.",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "description": "Any unique string (a UUID is ideal). A retry with the same key within 24 hours replays the first answer — with `Idempotent-Replayed: true` — instead of doing the work again. Reusing a key for a DIFFERENT request is a 409.\n",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "The key is missing, wrong or revoked.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "unauthorized",
                "message": "That API key is not valid, or it has been revoked.",
                "request_id": "req_4c1n8y2m0q7p3x5z9a1b",
                "doc_url": "https://hailmate.ai/docs/api#errors"
              }
            }
          }
        }
      },
      "PlanRequired": {
        "description": "The workspace has no active subscription.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "A read-only key tried to change something.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such record in this workspace.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InvalidRequest": {
        "description": "Something in the request was wrong; `field` names what.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "invalid_request",
                "message": "No stage called \"Aproved\" on this workspace's boards. Use a key or a label from GET /v1/stages.",
                "field": "stage",
                "request_id": "req_4c1n8y2m0q7p3x5z9a1b",
                "doc_url": "https://hailmate.ai/docs/api#errors"
              }
            }
          }
        }
      },
      "Conflict": {
        "description": "An Idempotency-Key clash — reused for a different request, or the first is still running.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Over 120 requests a minute (or 20 hail lookups a minute). Wait `Retry-After` seconds.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "doc_url"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "invalid_request",
                  "conflict",
                  "payload_too_large",
                  "rate_limited",
                  "plan_required",
                  "method_not_allowed",
                  "server_error"
                ],
                "description": "Machine-readable. Branch on this."
              },
              "message": {
                "type": "string",
                "description": "For a person. May be reworded."
              },
              "field": {
                "type": "string",
                "description": "The request field that was wrong, when there was one."
              },
              "request_id": {
                "type": "string"
              },
              "doc_url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        },
        "example": {
          "error": {
            "code": "invalid_request",
            "message": "A job needs a name.",
            "field": "name",
            "request_id": "req_jfvtxfboxyzopov1pi4d",
            "doc_url": "https://hailmate.ai/docs/api#errors"
          }
        }
      },
      "Ping": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "object": {
            "type": "string",
            "const": "workspace"
          },
          "workspace_id": {
            "type": "string",
            "format": "uuid"
          },
          "workspace": {
            "type": [
              "string",
              "null"
            ],
            "description": "The workspace's name."
          },
          "api_version": {
            "type": "string",
            "example": "v1"
          },
          "key": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "access": {
                "type": "string",
                "enum": [
                  "read",
                  "write"
                ]
              }
            }
          }
        },
        "example": {
          "ok": true,
          "object": "workspace",
          "workspace_id": "5f0c2a8e-1b3d-4c7a-9e21-7a3b8d4c6e10",
          "workspace": "Ridgeline Roofing",
          "api_version": "v1",
          "key": {
            "name": "Zapier",
            "access": "write"
          }
        }
      },
      "Deleted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean",
            "const": true
          }
        },
        "example": {
          "id": "f2b1a0c4-1111-4000-8000-0000000000aa",
          "object": "job",
          "deleted": true
        }
      },
      "DeletedSnapshot": {
        "type": "object",
        "description": "What a `*.deleted` delivery carries — the record is gone, so these are the fields that identify your copy of it.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "enum": [
              "job",
              "contact",
              "task"
            ]
          },
          "deleted": {
            "type": "boolean",
            "const": true
          },
          "job_number": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "job_id": {
            "type": "string",
            "format": "uuid"
          }
        },
        "example": {
          "id": "f2b1a0c4-1111-4000-8000-0000000000aa",
          "object": "job",
          "deleted": true,
          "job_number": "JOB-00142",
          "name": "1804 Cedar Ridge Dr",
          "address": "1804 Cedar Ridge Dr"
        }
      },
      "PersonRef": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "example": {
          "id": "b7c2d3e4-4444-4000-8000-0000000000dd",
          "name": "Dana Reed",
          "email": "dana@example.com",
          "phone": "(972) 555-0148"
        }
      },
      "ListMeta": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass back as `?cursor=`. `null` means that was the last page."
          }
        },
        "example": {
          "object": "list",
          "has_more": true,
          "next_cursor": "eyJjIjoiMjAyNi0wOS0yOFQxNDowMjowMFoiLCJpIjoiZjJiMWEwYzQifQ"
        }
      },
      "JobList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          }
        ]
      },
      "ContactList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          }
        ]
      },
      "TaskList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          }
        ]
      },
      "EstimateList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Estimate"
                }
              }
            }
          }
        ]
      },
      "InvoiceList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          }
        ]
      },
      "PaymentList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            }
          }
        ]
      },
      "NoteList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Note"
                }
              }
            }
          }
        ]
      },
      "FileList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          }
        ]
      },
      "PinList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Pin"
                }
              }
            }
          }
        ]
      },
      "StormListList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/StormList"
                }
              }
            }
          }
        ]
      },
      "StormListPropertyList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListMeta"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/StormListProperty"
                }
              }
            }
          }
        ]
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "job"
          },
          "job_number": {
            "type": [
              "string",
              "null"
            ],
            "example": "JOB-00142"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "example": "1804 Cedar Ridge Dr"
          },
          "job_type": {
            "type": "string",
            "enum": [
              "insurance",
              "retail"
            ]
          },
          "stage": {
            "type": [
              "string",
              "null"
            ],
            "description": "The stage key. Filter on this.",
            "example": "inspection_scheduled"
          },
          "stage_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "The stage as your board names it.",
            "example": "Inspection Scheduled"
          },
          "stage_is_completed": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "The stage is a won / completed column."
          },
          "stage_is_lost": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "The stage is a lost column."
          },
          "stage_entered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "pipeline_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "pipeline_name": {
            "type": [
              "string",
              "null"
            ],
            "example": "Insurance"
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "description": "Two-letter code.",
            "example": "TX"
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "county": {
            "type": [
              "string",
              "null"
            ]
          },
          "homeowner_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "homeowner": {
            "$ref": "#/components/schemas/PersonRef"
          },
          "adjuster_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "adjuster": {
            "$ref": "#/components/schemas/PersonRef"
          },
          "secondary_contact_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "secondary_adjuster_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "assigned_to_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The primary assignee."
          },
          "assigned_to_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "assignee_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Everyone on the job, primary included."
          },
          "assignee_names": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "insurance_company": {
            "type": [
              "string",
              "null"
            ]
          },
          "claim_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "policy_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "date_of_loss": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "damage_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "lead_source": {
            "type": [
              "string",
              "null"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "normal",
              "high",
              "urgent",
              null
            ]
          },
          "mortgage_company": {
            "type": [
              "string",
              "null"
            ]
          },
          "financing_method": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "cash",
              "check",
              "credit_card",
              "financing",
              "other",
              null
            ]
          },
          "rcv_amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Replacement cost value on the claim."
          },
          "acv_amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Actual cash value."
          },
          "deductible": {
            "type": [
              "number",
              "null"
            ]
          },
          "supplements_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "estimated_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "final_amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Contract value on a retail job."
          },
          "total_job_value": {
            "type": [
              "number",
              "null"
            ],
            "description": "What the job is worth — the same figure the Money tab shows."
          },
          "amount_received": {
            "type": [
              "number",
              "null"
            ],
            "description": "Money received on the job so far."
          },
          "balance_due": {
            "type": [
              "number",
              "null"
            ]
          },
          "contract_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "installation_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "archived": {
            "type": "boolean"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lost_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lost_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Opens the job in HailMate."
          },
          "portal_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The homeowner's job page — the same link \"Copy Homeowner Link\" gives."
          }
        },
        "example": {
          "id": "f2b1a0c4-1111-4000-8000-0000000000aa",
          "object": "job",
          "job_number": "JOB-00142",
          "name": "1804 Cedar Ridge Dr",
          "job_type": "insurance",
          "stage": "inspection_scheduled",
          "stage_label": "Inspection Scheduled",
          "stage_is_completed": false,
          "stage_is_lost": false,
          "stage_entered_at": "2026-09-16T14:02:00.000Z",
          "pipeline_id": "1cf7d87b-7d99-4ba0-b277-0e88ac127e75",
          "pipeline_name": "Insurance",
          "address": "1804 Cedar Ridge Dr",
          "city": "Plano",
          "state": "TX",
          "postal_code": "75024",
          "county": "Collin",
          "homeowner_id": "b7c2d3e4-4444-4000-8000-0000000000dd",
          "homeowner": {
            "id": "b7c2d3e4-4444-4000-8000-0000000000dd",
            "name": "Dana Reed",
            "email": "dana@example.com",
            "phone": "(972) 555-0148"
          },
          "adjuster_id": null,
          "adjuster": null,
          "secondary_contact_ids": [],
          "secondary_adjuster_ids": [],
          "assigned_to_id": "5d0e1c2b-7777-4000-8000-0000000000ab",
          "assigned_to_name": "Sam Carter",
          "assignee_ids": [
            "5d0e1c2b-7777-4000-8000-0000000000ab"
          ],
          "assignee_names": [
            "Sam Carter"
          ],
          "insurance_company": "Example Mutual",
          "claim_number": "CLM-88213",
          "policy_number": null,
          "date_of_loss": "2026-09-12",
          "damage_types": [
            "hail"
          ],
          "tags": [],
          "lead_source": "Website",
          "priority": "normal",
          "mortgage_company": null,
          "financing_method": null,
          "rcv_amount": 24380.5,
          "acv_amount": 19240,
          "deductible": 2500,
          "supplements_amount": null,
          "estimated_amount": null,
          "final_amount": null,
          "total_job_value": 24380.5,
          "amount_received": 0,
          "balance_due": 24380.5,
          "contract_date": null,
          "installation_date": null,
          "archived": false,
          "completed_at": null,
          "lost_at": null,
          "lost_reason": null,
          "created_at": "2026-09-16T14:02:00.000Z",
          "updated_at": "2026-09-16T14:02:00.000Z",
          "url": "https://app.hailmate.ai/job/f2b1a0c4-1111-4000-8000-0000000000aa",
          "portal_url": "https://app.hailmate.ai/project/0f0f0f0f-3333-4000-8000-0000000000cc"
        }
      },
      "JobCreate": {
        "type": "object",
        "required": [
          "name",
          "address"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 200,
            "description": "`job_name` works too."
          },
          "address": {
            "type": "string",
            "maxLength": 300,
            "description": "`property_address` works too."
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "description": "A code or a name — \"TX\" or \"Texas\"."
          },
          "postal_code": {
            "type": "string",
            "description": "`zip` works too."
          },
          "county": {
            "type": "string"
          },
          "job_type": {
            "type": "string",
            "enum": [
              "insurance",
              "retail"
            ],
            "default": "insurance"
          },
          "stage": {
            "type": "string",
            "description": "A stage key or label. Defaults to the first stage of the default pipeline."
          },
          "pipeline_id": {
            "type": "string",
            "format": "uuid"
          },
          "homeowner_id": {
            "type": "string",
            "format": "uuid"
          },
          "adjuster_id": {
            "type": "string",
            "format": "uuid"
          },
          "assigned_to": {
            "type": "string",
            "format": "uuid",
            "description": "A teammate's user id from `/users`."
          },
          "lead_source": {
            "type": "string"
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ]
          },
          "insurance_company": {
            "type": "string"
          },
          "claim_number": {
            "type": "string"
          },
          "policy_number": {
            "type": "string"
          },
          "date_of_loss": {
            "type": "string",
            "format": "date"
          },
          "mortgage_company": {
            "type": "string"
          },
          "financing_method": {
            "type": "string",
            "enum": [
              "cash",
              "check",
              "credit_card",
              "financing",
              "other"
            ]
          },
          "rcv_amount": {
            "type": "number"
          },
          "acv_amount": {
            "type": "number"
          },
          "deductible": {
            "type": "number"
          },
          "supplements_amount": {
            "type": "number"
          },
          "estimated_amount": {
            "type": "number"
          },
          "final_amount": {
            "type": "number"
          },
          "contract_date": {
            "type": "string",
            "format": "date"
          },
          "installation_date": {
            "type": "string",
            "format": "date"
          },
          "damage_types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Or a comma-separated string."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Or a comma-separated string."
          },
          "notes": {
            "type": "string",
            "description": "Becomes the first note on the job."
          }
        }
      },
      "JobUpdate": {
        "type": "object",
        "description": "Any subset of JobCreate's fields (except `notes`), plus `archived` and `lost_reason`. `null` clears.",
        "properties": {
          "name": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "county": {
            "type": [
              "string",
              "null"
            ]
          },
          "job_type": {
            "type": "string",
            "enum": [
              "insurance",
              "retail"
            ]
          },
          "stage": {
            "type": "string"
          },
          "pipeline_id": {
            "type": "string",
            "format": "uuid"
          },
          "homeowner_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "adjuster_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "assigned_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "lead_source": {
            "type": [
              "string",
              "null"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "normal",
              "high",
              "urgent",
              null
            ]
          },
          "insurance_company": {
            "type": [
              "string",
              "null"
            ]
          },
          "claim_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "policy_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "date_of_loss": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "mortgage_company": {
            "type": [
              "string",
              "null"
            ]
          },
          "financing_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "rcv_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "acv_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "deductible": {
            "type": [
              "number",
              "null"
            ]
          },
          "supplements_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "estimated_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "final_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "contract_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "installation_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "damage_types": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "tags": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "archived": {
            "type": "boolean"
          },
          "lost_reason": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ContactType": {
        "type": "string",
        "enum": [
          "homeowner",
          "commercial",
          "adjuster",
          "contractor",
          "subcontractor",
          "install_crew",
          "inspector",
          "mortgage_company",
          "other"
        ]
      },
      "Contact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "contact"
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "full_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "example": "(972) 555-0148"
          },
          "company": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "$ref": "#/components/schemas/ContactType"
          },
          "trades": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "For a subcontractor — gutters, siding…"
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "claim_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "adjuster_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "adjuster_extension": {
            "type": [
              "string",
              "null"
            ]
          },
          "assigned_to_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "assigned_to_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "example": {
          "id": "b7c2d3e4-4444-4000-8000-0000000000dd",
          "object": "contact",
          "first_name": "Dana",
          "last_name": "Reed",
          "full_name": "Dana Reed",
          "email": "dana@example.com",
          "phone": "(972) 555-0148",
          "company": null,
          "type": "homeowner",
          "trades": [],
          "address": "1804 Cedar Ridge Dr",
          "city": "Plano",
          "state": "TX",
          "postal_code": "75024",
          "notes": null,
          "claim_number": null,
          "adjuster_type": null,
          "adjuster_extension": null,
          "assigned_to_id": null,
          "assigned_to_name": null,
          "created_at": "2026-09-16T13:40:00.000Z",
          "updated_at": "2026-09-16T13:40:00.000Z",
          "url": "https://app.hailmate.ai/contact/b7c2d3e4-4444-4000-8000-0000000000dd"
        }
      },
      "ContactCreate": {
        "type": "object",
        "description": "At least one of a name, email, phone or company.",
        "properties": {
          "name": {
            "type": "string",
            "description": "A full name, split into first and last when those are not sent."
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": "string",
            "description": "Any format."
          },
          "company": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/ContactType"
          },
          "trades": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "address": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "postal_code": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          },
          "claim_number": {
            "type": "string"
          },
          "adjuster_type": {
            "type": "string"
          },
          "adjuster_extension": {
            "type": "string"
          },
          "assigned_to": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ContactUpdate": {
        "type": "object",
        "description": "Any subset of ContactCreate's fields (except `name`). `null` clears.",
        "properties": {
          "first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "company": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "$ref": "#/components/schemas/ContactType"
          },
          "trades": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "claim_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "adjuster_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "adjuster_extension": {
            "type": [
              "string",
              "null"
            ]
          },
          "assigned_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "Task": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "task"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "description": "Wall-clock — the local time the crew reads. No offset.",
            "example": "2026-10-02T15:00:00"
          },
          "end_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The last day of a task that blocks off several days."
          },
          "duration_minutes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "normal",
              "high",
              null
            ]
          },
          "completed": {
            "type": "boolean"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "is_appointment": {
            "type": "boolean"
          },
          "appointment_type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "inspection",
              "adjuster_meeting",
              "build_day",
              null
            ]
          },
          "outcome": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "completed",
              "no_show",
              "rescheduled",
              "canceled",
              null
            ]
          },
          "customer_reminder": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "none",
              "sms",
              "email",
              "both",
              null
            ],
            "description": "Whether HailMate reminds the homeowner the day before."
          },
          "reminder_minutes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "job_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "contact_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "assigned_to_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "assigned_to_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "example": {
          "id": "d4e5f6a7-5555-4000-8000-0000000000ee",
          "object": "task",
          "title": "Adjuster meeting — 1804 Cedar Ridge Dr",
          "description": null,
          "due_date": "2026-10-02T15:00:00",
          "end_date": null,
          "duration_minutes": 60,
          "priority": "normal",
          "completed": false,
          "completed_at": null,
          "is_appointment": true,
          "appointment_type": "adjuster_meeting",
          "outcome": null,
          "customer_reminder": "sms",
          "reminder_minutes": 60,
          "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
          "contact_id": "b7c2d3e4-4444-4000-8000-0000000000dd",
          "assigned_to_id": "5d0e1c2b-7777-4000-8000-0000000000ab",
          "assigned_to_name": "Sam Carter",
          "created_at": "2026-09-16T14:10:00.000Z",
          "updated_at": "2026-09-16T14:10:00.000Z",
          "url": "https://app.hailmate.ai/task/d4e5f6a7-5555-4000-8000-0000000000ee"
        }
      },
      "TaskCreate": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "description": {
            "type": "string"
          },
          "due_date": {
            "type": "string",
            "description": "A date or date-time. Any offset is dropped; the digits are kept."
          },
          "end_date": {
            "type": "string",
            "format": "date"
          },
          "duration_minutes": {
            "type": "integer",
            "minimum": 15,
            "maximum": 480
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "normal",
              "high"
            ]
          },
          "appointment_type": {
            "type": "string",
            "enum": [
              "inspection",
              "adjuster_meeting",
              "build_day"
            ]
          },
          "customer_reminder": {
            "type": "string",
            "enum": [
              "none",
              "sms",
              "email",
              "both"
            ],
            "description": "Remind the homeowner the day before."
          },
          "reminder_minutes": {
            "type": "integer",
            "enum": [
              -1,
              0,
              5,
              10,
              15,
              30,
              60,
              120,
              1440
            ]
          },
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "contact_id": {
            "type": "string",
            "format": "uuid"
          },
          "assigned_to": {
            "type": "string",
            "format": "uuid"
          },
          "completed": {
            "type": "boolean"
          }
        }
      },
      "TaskUpdate": {
        "type": "object",
        "description": "Any subset of TaskCreate's fields, plus `outcome`. `null` clears.",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "end_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "duration_minutes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ]
          },
          "appointment_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "outcome": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "completed",
              "no_show",
              "rescheduled",
              "canceled",
              null
            ]
          },
          "customer_reminder": {
            "type": "string"
          },
          "reminder_minutes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "job_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "contact_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "assigned_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "completed": {
            "type": "boolean"
          }
        }
      },
      "Estimate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "estimate"
          },
          "estimate_number": {
            "type": [
              "string",
              "null"
            ],
            "example": "EST-0214"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "document_type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "proposal",
              "estimate",
              null
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "draft",
              "sent",
              "viewed",
              "signed",
              "expired",
              "declined",
              null
            ]
          },
          "signature_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "type": [
              "number",
              "null"
            ],
            "description": "The selected package's price, else the base package. Null — never 0 — when it could not be computed."
          },
          "package_totals": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "number"
            },
            "description": "Every package's price, keyed by package.",
            "example": {
              "good": 18450,
              "better": 21900,
              "best": 26400
            }
          },
          "upgrades_total": {
            "type": [
              "number",
              "null"
            ],
            "description": "Optional upgrades, quoted outside the package price."
          },
          "selected_tier": {
            "type": [
              "string",
              "null"
            ],
            "description": "The package the homeowner chose."
          },
          "job_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "valid_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "first_viewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_viewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "view_count": {
            "type": "integer"
          },
          "signed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "signer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "pdf_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "signed_pdf_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "view_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The homeowner's view of the estimate."
          }
        },
        "example": {
          "id": "a41c9e20-2222-4000-8000-0000000000e1",
          "object": "estimate",
          "estimate_number": "EST-0214",
          "title": "Roof replacement — 1804 Cedar Ridge Dr",
          "document_type": "proposal",
          "status": "sent",
          "signature_status": "pending",
          "total": 21900,
          "package_totals": {
            "good": 18450,
            "better": 21900,
            "best": 26400
          },
          "upgrades_total": 1250,
          "selected_tier": null,
          "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
          "valid_until": "2026-10-28T00:00:00.000Z",
          "sent_at": "2026-09-28T15:10:00.000Z",
          "first_viewed_at": "2026-09-28T18:42:00.000Z",
          "last_viewed_at": "2026-09-28T19:05:00.000Z",
          "view_count": 3,
          "signed_at": null,
          "signer_name": null,
          "pdf_url": "https://files.example-cdn.com/estimates/EST-0214.pdf",
          "signed_pdf_url": null,
          "created_at": "2026-09-27T21:30:00.000Z",
          "updated_at": "2026-09-28T19:05:00.000Z",
          "url": "https://app.hailmate.ai/estimates/a41c9e20-2222-4000-8000-0000000000e1",
          "view_url": "https://app.hailmate.ai/estimate/6f1d3b52-9c7e-4a10-8f3b-2d4e5a6b7c8d"
        }
      },
      "Invoice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "invoice"
          },
          "invoice_number": {
            "type": [
              "string",
              "null"
            ],
            "example": "INV-0311"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "sent",
              "viewed",
              "partially_paid",
              "paid",
              "overdue",
              "void",
              "bad_debt"
            ]
          },
          "purpose": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "claim_scope",
              "deductible",
              "supplement",
              "depreciation",
              "contract",
              "other",
              null
            ]
          },
          "subtotal": {
            "type": [
              "number",
              "null"
            ]
          },
          "discount_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "tax_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "late_fee_amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "total_amount": {
            "type": "number"
          },
          "amount_paid": {
            "type": "number"
          },
          "balance_due": {
            "type": "number",
            "description": "`total_amount - amount_paid`, never below zero."
          },
          "part_number": {
            "type": [
              "integer",
              "null"
            ],
            "description": "When billed in parts, which part this is."
          },
          "part_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "issue_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "first_viewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "paid_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "job_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "contact_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "estimate_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "pdf_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "payment_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The homeowner's pay page — the same link \"Copy Pay Link\" gives."
          }
        },
        "example": {
          "id": "c90d7b31-3333-4000-8000-0000000000f2",
          "object": "invoice",
          "invoice_number": "INV-0311",
          "title": "Deductible",
          "status": "sent",
          "purpose": "deductible",
          "subtotal": 2500,
          "discount_amount": 0,
          "tax_amount": 0,
          "late_fee_amount": 0,
          "total_amount": 2500,
          "amount_paid": 0,
          "balance_due": 2500,
          "part_number": null,
          "part_count": null,
          "issue_date": "2026-09-28",
          "due_date": "2026-10-12",
          "sent_at": "2026-09-28T16:00:00.000Z",
          "first_viewed_at": null,
          "paid_at": null,
          "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
          "contact_id": "b7c2d3e4-4444-4000-8000-0000000000dd",
          "estimate_id": null,
          "pdf_url": "https://files.example-cdn.com/invoices/INV-0311.pdf",
          "created_at": "2026-09-28T15:55:00.000Z",
          "updated_at": "2026-09-28T16:00:00.000Z",
          "url": "https://app.hailmate.ai/jobs/invoice/c90d7b31-3333-4000-8000-0000000000f2",
          "payment_url": "https://app.hailmate.ai/invoice/0b7e2c19-5d4a-4f3e-9a1b-8c6d2e4f1a37"
        }
      },
      "PaymentType": {
        "type": "string",
        "enum": [
          "acv",
          "deductible",
          "supplement",
          "depreciation",
          "retail",
          "other"
        ]
      },
      "Payment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "payment"
          },
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "invoice_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "amount": {
            "type": "number",
            "description": "Negative for a refund."
          },
          "payment_type": {
            "$ref": "#/components/schemas/PaymentType"
          },
          "payer_type": {
            "type": "string",
            "enum": [
              "insurance",
              "homeowner",
              "mortgage_company",
              "other"
            ]
          },
          "payment_method": {
            "type": "string",
            "enum": [
              "check",
              "cash",
              "card",
              "ach",
              "financing",
              "other"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "expected",
              "received",
              "sent_to_mortgage",
              "endorsed",
              "deposited"
            ]
          },
          "check_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "date_received": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "date_deposited": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_refund": {
            "type": "boolean"
          },
          "refund_of_payment_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "online": {
            "type": "boolean",
            "description": "Paid through HailMate's online payment page."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        },
        "example": {
          "id": "9fa3e1d2-5555-4000-8000-0000000000c3",
          "object": "payment",
          "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
          "invoice_id": null,
          "amount": 13164,
          "payment_type": "acv",
          "payer_type": "insurance",
          "payment_method": "check",
          "status": "received",
          "check_number": "4471",
          "date_received": "2026-09-26",
          "date_deposited": null,
          "notes": "First ACV check from Example Mutual",
          "is_refund": false,
          "refund_of_payment_id": null,
          "online": false,
          "created_at": "2026-09-26T20:14:00.000Z",
          "updated_at": "2026-09-26T20:14:00.000Z",
          "url": "https://app.hailmate.ai/job/f2b1a0c4-1111-4000-8000-0000000000aa"
        }
      },
      "PaymentCreate": {
        "type": "object",
        "required": [
          "job_id",
          "amount"
        ],
        "properties": {
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "invoice_id": {
            "type": "string",
            "format": "uuid",
            "description": "An invoice ON THIS JOB to apply it to. Never guessed."
          },
          "payment_type": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentType"
              }
            ],
            "default": "other"
          },
          "payer_type": {
            "type": "string",
            "enum": [
              "insurance",
              "homeowner",
              "mortgage_company",
              "other"
            ],
            "default": "homeowner"
          },
          "payment_method": {
            "type": "string",
            "enum": [
              "check",
              "cash",
              "card",
              "ach",
              "financing",
              "other"
            ],
            "default": "check"
          },
          "status": {
            "type": "string",
            "enum": [
              "expected",
              "received",
              "sent_to_mortgage",
              "endorsed",
              "deposited"
            ],
            "default": "received"
          },
          "check_number": {
            "type": "string"
          },
          "date_received": {
            "type": "string",
            "format": "date",
            "description": "Defaults to today (US Central)."
          },
          "date_deposited": {
            "type": "string",
            "format": "date"
          },
          "notes": {
            "type": "string"
          }
        }
      },
      "Note": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "note"
          },
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "content": {
            "type": "string"
          },
          "author_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Null for a note written through the API."
          },
          "author_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "example": {
          "id": "3c8b1a07-6666-4000-8000-0000000000b4",
          "object": "note",
          "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
          "content": "Adjuster meeting moved to Thursday 10am. Homeowner will be home.",
          "author_id": "5d0e1c2b-7777-4000-8000-0000000000ab",
          "author_name": "Sam Carter",
          "created_at": "2026-09-27T14:20:00.000Z",
          "updated_at": "2026-09-27T14:20:00.000Z",
          "url": "https://app.hailmate.ai/job/f2b1a0c4-1111-4000-8000-0000000000aa"
        }
      },
      "NoteCreate": {
        "type": "object",
        "required": [
          "job_id",
          "content"
        ],
        "properties": {
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "content": {
            "type": "string",
            "maxLength": 10000
          }
        }
      },
      "File": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "file"
          },
          "job_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "photo",
              "document",
              "scope",
              "estimate",
              "contract",
              "other"
            ]
          },
          "mime_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_photo": {
            "type": "boolean"
          },
          "is_video": {
            "type": "boolean"
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "The caption."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "latitude": {
            "type": [
              "number",
              "null"
            ],
            "description": "Where a photo was taken."
          },
          "longitude": {
            "type": [
              "number",
              "null"
            ]
          },
          "taken_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "uploaded_by_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "uploaded_by_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "download_url": {
            "type": "string",
            "format": "uri"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        },
        "example": {
          "id": "7e2d9c40-8888-4000-8000-0000000000a5",
          "object": "file",
          "job_id": "f2b1a0c4-1111-4000-8000-0000000000aa",
          "name": "north-slope-hits.jpg",
          "category": "photo",
          "mime_type": "image/jpeg",
          "is_photo": true,
          "is_video": false,
          "size_bytes": 1532410,
          "description": "Hail hits on the north slope, chalked",
          "tags": [
            "damage",
            "north slope"
          ],
          "latitude": 33.0726,
          "longitude": -96.7512,
          "taken_at": "2026-09-16T15:12:08.000Z",
          "uploaded_by_id": "5d0e1c2b-7777-4000-8000-0000000000ab",
          "uploaded_by_name": "Sam Carter",
          "download_url": "https://files.example-cdn.com/jobs/f2b1a0c4/north-slope-hits.jpg",
          "created_at": "2026-09-16T15:12:40.000Z",
          "url": "https://app.hailmate.ai/job/f2b1a0c4-1111-4000-8000-0000000000aa"
        }
      },
      "FileUploadForm": {
        "type": "object",
        "required": [
          "file",
          "job_id"
        ],
        "properties": {
          "file": {
            "type": "string",
            "format": "binary"
          },
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "category": {
            "type": "string",
            "enum": [
              "photo",
              "document",
              "scope",
              "estimate",
              "contract",
              "other"
            ]
          },
          "description": {
            "type": "string"
          },
          "file_name": {
            "type": "string"
          }
        }
      },
      "FileFromUrl": {
        "type": "object",
        "required": [
          "job_id",
          "file_url"
        ],
        "properties": {
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "file_url": {
            "type": "string",
            "format": "uri",
            "description": "A public https address. Redirects are followed (up to 3)."
          },
          "category": {
            "type": "string",
            "enum": [
              "photo",
              "document",
              "scope",
              "estimate",
              "contract",
              "other"
            ]
          },
          "description": {
            "type": "string"
          },
          "file_name": {
            "type": "string"
          }
        }
      },
      "KnockResult": {
        "type": "string",
        "enum": [
          "interested",
          "not_interested",
          "contacted",
          "no_answer",
          "door_hanger",
          "follow_up",
          "appointment_scheduled",
          "dont_knock",
          "renter",
          "no_damage",
          "cash_quote",
          "lost"
        ]
      },
      "Pin": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "pin"
          },
          "pin_type": {
            "type": "string",
            "enum": [
              "knock",
              "inspection",
              "job"
            ]
          },
          "knock_result": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/KnockResult"
              },
              {
                "type": "null"
              }
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "homeowner_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "homeowner_first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "homeowner_last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "job_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Set once the pin became a job."
          },
          "created_by_id": {
            "type": "string",
            "format": "uuid"
          },
          "created_by_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "map_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "example": {
          "id": "2b6f4e18-9999-4000-8000-0000000000d6",
          "object": "pin",
          "pin_type": "knock",
          "knock_result": "interested",
          "address": "1812 Cedar Ridge Dr",
          "city": "Plano",
          "state": "TX",
          "postal_code": "75024",
          "latitude": 33.0731,
          "longitude": -96.7519,
          "homeowner_name": "Chris Lane",
          "homeowner_first_name": "Chris",
          "homeowner_last_name": "Lane",
          "phone": "(972) 555-0193",
          "email": null,
          "notes": "Wants an inspection Saturday morning",
          "job_id": null,
          "created_by_id": "5d0e1c2b-7777-4000-8000-0000000000ab",
          "created_by_name": "Sam Carter",
          "created_at": "2026-09-27T17:45:00.000Z",
          "updated_at": "2026-09-27T17:45:00.000Z",
          "map_url": "https://www.google.com/maps?q=33.0731,-96.7519",
          "url": "https://app.hailmate.ai/canvassing"
        }
      },
      "PinCreate": {
        "type": "object",
        "description": "Needs `latitude` and `longitude`, or an `address` to place it from.",
        "properties": {
          "latitude": {
            "type": "number",
            "minimum": -90,
            "maximum": 90
          },
          "longitude": {
            "type": "number",
            "minimum": -180,
            "maximum": 180
          },
          "address": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "description": "A code or a name — \"TX\" or \"Texas\"."
          },
          "postal_code": {
            "type": "string"
          },
          "knock_result": {
            "$ref": "#/components/schemas/KnockResult"
          },
          "pin_type": {
            "type": "string",
            "enum": [
              "knock",
              "inspection"
            ],
            "default": "knock"
          },
          "homeowner_name": {
            "type": "string",
            "description": "Split into first and last for you."
          },
          "homeowner_first_name": {
            "type": "string"
          },
          "homeowner_last_name": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "notes": {
            "type": "string"
          },
          "created_by_id": {
            "type": "string",
            "format": "uuid",
            "description": "The teammate who knocked. Defaults to whoever connected the integration."
          }
        }
      },
      "PinUpdate": {
        "type": "object",
        "properties": {
          "knock_result": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/KnockResult"
              },
              {
                "type": "null"
              }
            ]
          },
          "pin_type": {
            "type": "string",
            "enum": [
              "knock",
              "inspection"
            ]
          },
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "homeowner_name": {
            "type": "string"
          },
          "homeowner_first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "homeowner_last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "LineItemInput": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": "number",
            "minimum": 0,
            "default": 1
          },
          "unit": {
            "type": "string",
            "description": "sq, lf, ea…"
          },
          "unit_price": {
            "type": "number",
            "description": "Required on an invoice line. Negative on an invoice is a credit."
          },
          "taxable": {
            "type": "boolean",
            "default": true,
            "description": "Invoices only."
          }
        }
      },
      "InvoiceCreate": {
        "type": "object",
        "required": [
          "line_items"
        ],
        "description": "Needs a `job_id` or a `contact_id`.",
        "properties": {
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "contact_id": {
            "type": "string",
            "format": "uuid",
            "description": "Who it bills."
          },
          "estimate_id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "purpose": {
            "type": "string",
            "enum": [
              "claim_scope",
              "deductible",
              "supplement",
              "depreciation",
              "contract",
              "other"
            ]
          },
          "issue_date": {
            "type": "string",
            "format": "date",
            "description": "Defaults to today."
          },
          "due_date": {
            "type": "string",
            "format": "date"
          },
          "line_items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            }
          },
          "tax_rate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "A percent",
            "on the taxable lines.": null
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "amount",
              "percent"
            ]
          },
          "discount_value": {
            "type": "number",
            "minimum": 0
          },
          "notes": {
            "type": "string"
          },
          "terms": {
            "type": "string"
          },
          "allow_partial_payments": {
            "type": "boolean"
          }
        }
      },
      "InvoiceUpdate": {
        "type": "object",
        "properties": {
          "contact_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "estimate_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "purpose": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "claim_scope",
              "deductible",
              "supplement",
              "depreciation",
              "contract",
              "other",
              null
            ]
          },
          "issue_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "line_items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            },
            "description": "Replaces every line."
          },
          "tax_rate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "amount",
              "percent"
            ]
          },
          "discount_value": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "`0` or `null` takes the discount off."
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "terms": {
            "type": [
              "string",
              "null"
            ]
          },
          "allow_partial_payments": {
            "type": "boolean"
          }
        }
      },
      "EstimateCreate": {
        "type": "object",
        "required": [
          "job_id"
        ],
        "properties": {
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "template_id": {
            "type": "string",
            "format": "uuid",
            "description": "One of your templates. Leave out for your standard proposal."
          },
          "title": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "One price for the whole job."
          },
          "valid_until": {
            "type": "string",
            "format": "date",
            "description": "Defaults to your workspace's setting."
          },
          "line_items": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            },
            "description": "The Scope of Work, without a template. Leave out for the standard checklist."
          }
        }
      },
      "EstimateUpdate": {
        "type": "object",
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": [
              "number",
              "null"
            ],
            "exclusiveMinimum": 0,
            "description": "`null` takes the stated price off."
          },
          "valid_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          }
        }
      },
      "EstimateTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "estimate_template"
          },
          "name": {
            "type": "string"
          },
          "template_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "example": {
          "id": "7a1c9e02-2222-4000-8000-0000000000c3",
          "object": "estimate_template",
          "name": "Insurance roof replacement",
          "template_number": "TPL-0003",
          "created_at": "2026-08-30T15:00:00.000Z",
          "updated_at": "2026-09-20T18:30:00.000Z"
        }
      },
      "EstimateTemplateList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EstimateTemplate"
            }
          },
          "has_more": {
            "type": "boolean",
            "const": false
          },
          "next_cursor": {
            "type": "null"
          }
        },
        "example": {
          "object": "list",
          "data": [
            {
              "id": "7a1c9e02-2222-4000-8000-0000000000c3",
              "object": "estimate_template",
              "name": "Insurance roof replacement",
              "template_number": "TPL-0003",
              "created_at": "2026-08-30T15:00:00.000Z",
              "updated_at": "2026-09-20T18:30:00.000Z"
            }
          ],
          "has_more": false,
          "next_cursor": null
        }
      },
      "OAuthTokenRequest": {
        "type": "object",
        "required": [
          "grant_type",
          "client_id",
          "client_secret"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "enum": [
              "authorization_code",
              "refresh_token"
            ]
          },
          "code": {
            "type": "string",
            "description": "For `authorization_code`."
          },
          "redirect_uri": {
            "type": "string",
            "format": "uri",
            "description": "For `authorization_code` — the same address the person was sent back to."
          },
          "refresh_token": {
            "type": "string",
            "description": "For `refresh_token`."
          },
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          }
        }
      },
      "OAuthRevokeRequest": {
        "type": "object",
        "required": [
          "token",
          "client_id",
          "client_secret"
        ],
        "properties": {
          "token": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          }
        }
      },
      "OAuthToken": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "`hm_oat_…` — send as `Authorization: Bearer`."
          },
          "token_type": {
            "type": "string",
            "const": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "description": "Seconds — an hour."
          },
          "refresh_token": {
            "type": "string",
            "description": "`hm_ort_…` — keep it secret; it does not change."
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "write"
            ]
          },
          "workspace_id": {
            "type": "string",
            "format": "uuid"
          },
          "workspace": {
            "type": [
              "string",
              "null"
            ],
            "description": "The company that was connected."
          }
        },
        "example": {
          "access_token": "hm_oat_1q2w3e4r5t6y7u8i9o0p1a2s3d4f5g6h7j8k9l0z",
          "token_type": "Bearer",
          "expires_in": 3600,
          "refresh_token": "hm_ort_8d2k6m1q9x4w7c3t0r5p6n2j8h1g4f7d3s9a2l6e",
          "scope": "write",
          "workspace_id": "5f0c2a8e-1b3d-4c7a-9e21-7a3b8d4c6e10",
          "workspace": "Ridgeline Roofing"
        }
      },
      "OAuthError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "invalid_request",
              "invalid_client",
              "invalid_grant",
              "unsupported_grant_type",
              "server_error"
            ]
          },
          "error_description": {
            "type": "string"
          }
        },
        "example": {
          "error": "invalid_grant",
          "error_description": "That code has expired. Start the connection again."
        }
      },
      "StormList": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "storm_list"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "building",
              "ready",
              "partial",
              "failed",
              null
            ]
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "manual",
              "auto",
              null
            ]
          },
          "window_from": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "window_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "min_size_in": {
            "type": [
              "number",
              "null"
            ]
          },
          "area_kind": {
            "type": [
              "string",
              "null"
            ]
          },
          "territory_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "clipped_area_sq_mi": {
            "type": [
              "number",
              "null"
            ]
          },
          "property_cap": {
            "type": [
              "integer",
              "null"
            ]
          },
          "property_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "skipped_duplicates": {
            "type": [
              "integer",
              "null"
            ]
          },
          "dedupe_days": {
            "type": [
              "integer",
              "null"
            ]
          },
          "credits_spent": {
            "type": [
              "integer",
              "null"
            ]
          },
          "total_available": {
            "type": [
              "integer",
              "null"
            ]
          },
          "storm_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "built_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "example": {
          "id": "e5a1c7d9-aaaa-4000-8000-0000000000e7",
          "object": "storm_list",
          "name": "Plano 1.25″+ — Sep 12",
          "status": "ready",
          "source": "manual",
          "window_from": "2026-09-12",
          "window_to": "2026-09-12",
          "min_size_in": 1.25,
          "area_kind": "drawn",
          "territory_id": null,
          "clipped_area_sq_mi": 4.2,
          "property_cap": 1000,
          "property_count": 412,
          "skipped_duplicates": 18,
          "dedupe_days": 90,
          "credits_spent": 412,
          "total_available": 430,
          "storm_date": "2026-09-12",
          "built_at": "2026-09-13T13:04:00.000Z",
          "created_at": "2026-09-13T13:02:00.000Z",
          "updated_at": "2026-09-13T13:04:00.000Z",
          "url": "https://app.hailmate.ai/canvassing/lists/e5a1c7d9-aaaa-4000-8000-0000000000e7"
        }
      },
      "StormListProperty": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "storm_list_property"
          },
          "storm_list_id": {
            "type": "string",
            "format": "uuid"
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "zip": {
            "type": [
              "string",
              "null"
            ]
          },
          "owner_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "owner_occupied": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Null when the county record did not say."
          },
          "mailing_address": {
            "type": [
              "string",
              "null"
            ]
          },
          "mailing_city": {
            "type": [
              "string",
              "null"
            ]
          },
          "mailing_state": {
            "type": [
              "string",
              "null"
            ]
          },
          "mailing_zip": {
            "type": [
              "string",
              "null"
            ]
          },
          "mail_deliverable": {
            "type": "boolean",
            "description": "Our judgement — NOT a CASS or NCOA result."
          },
          "mail_exclude_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "hail_size_in": {
            "type": [
              "number",
              "null"
            ]
          },
          "hail_event_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "nearest_report_mi": {
            "type": [
              "number",
              "null"
            ]
          },
          "nearest_report_size_in": {
            "type": [
              "number",
              "null"
            ]
          },
          "year_built": {
            "type": [
              "integer",
              "null"
            ]
          },
          "property_use": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_residential": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "latitude": {
            "type": [
              "number",
              "null"
            ]
          },
          "longitude": {
            "type": [
              "number",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "example": {
          "id": "d4b3a2c1-bbbb-4000-8000-0000000000f8",
          "object": "storm_list_property",
          "storm_list_id": "e5a1c7d9-aaaa-4000-8000-0000000000e7",
          "address": "1804 Cedar Ridge Dr",
          "city": "Plano",
          "state": "TX",
          "zip": "75024",
          "owner_name": "Dana Reed",
          "owner_occupied": true,
          "mailing_address": "1804 Cedar Ridge Dr",
          "mailing_city": "Plano",
          "mailing_state": "TX",
          "mailing_zip": "75024",
          "mail_deliverable": true,
          "mail_exclude_reason": null,
          "hail_size_in": 1.5,
          "hail_event_date": "2026-09-12",
          "nearest_report_mi": 1.8,
          "nearest_report_size_in": 1.75,
          "year_built": 2004,
          "property_use": "Single family",
          "is_residential": true,
          "latitude": 33.0726,
          "longitude": -96.7512,
          "created_at": "2026-09-13T13:04:00.000Z"
        }
      },
      "HailEvent": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "size_in": {
            "type": [
              "number",
              "null"
            ],
            "description": "The size HailMate stands behind for this day at this address."
          },
          "confidence": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "confirmed",
              "likely",
              "radar",
              null
            ]
          },
          "confirmed_by_ground_report": {
            "type": "boolean"
          },
          "radar_estimated_size_in": {
            "type": [
              "number",
              "null"
            ]
          },
          "severe_hail_probability": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Percent."
          },
          "ground_report_size_in": {
            "type": [
              "number",
              "null"
            ]
          },
          "ground_report_distance_mi": {
            "type": [
              "number",
              "null"
            ]
          },
          "ground_report_count": {
            "type": "integer"
          }
        },
        "example": {
          "date": "2026-09-12",
          "size_in": 1.5,
          "confidence": "confirmed",
          "confirmed_by_ground_report": true,
          "radar_estimated_size_in": 1.5,
          "severe_hail_probability": 80,
          "ground_report_size_in": 1.75,
          "ground_report_distance_mi": 1.8,
          "ground_report_count": 3
        }
      },
      "HailReport": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "hail_report"
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "The address as it was found."
          },
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "since": {
            "type": "string",
            "format": "date"
          },
          "hail_day_count": {
            "type": "integer"
          },
          "largest_hail_in": {
            "type": [
              "number",
              "null"
            ]
          },
          "last_hail_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "hail_events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HailEvent"
            }
          },
          "wind_events": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "max_gust_mph": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Null when the report was damage with no measured gust."
                }
              }
            }
          }
        },
        "example": {
          "object": "hail_report",
          "address": "1804 Cedar Ridge Dr, Plano, TX 75024, USA",
          "latitude": 33.0198,
          "longitude": -96.6989,
          "since": "2021-09-28",
          "hail_day_count": 2,
          "largest_hail_in": 1.5,
          "last_hail_date": "2025-03-25",
          "hail_events": [
            {
              "date": "2025-03-25",
              "size_in": 1.5,
              "confidence": "confirmed",
              "confirmed_by_ground_report": true,
              "radar_estimated_size_in": null,
              "severe_hail_probability": null,
              "ground_report_size_in": 1.5,
              "ground_report_distance_mi": 1.4,
              "ground_report_count": 11
            },
            {
              "date": "2024-05-28",
              "size_in": 1.5,
              "confidence": "likely",
              "confirmed_by_ground_report": false,
              "radar_estimated_size_in": 1.5,
              "severe_hail_probability": null,
              "ground_report_size_in": 1.25,
              "ground_report_distance_mi": 9,
              "ground_report_count": 1
            }
          ],
          "wind_events": [
            {
              "date": "2025-03-04",
              "max_gust_mph": 90
            }
          ]
        }
      },
      "PipelineStage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "pipeline_stage"
          },
          "pipeline_id": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": "string",
            "description": "What you send and filter on.",
            "example": "claim_approved"
          },
          "label": {
            "type": "string",
            "description": "What to show a person.",
            "example": "Claim Approved"
          },
          "order": {
            "type": "integer"
          },
          "is_completed": {
            "type": "boolean"
          },
          "is_lost": {
            "type": "boolean"
          }
        },
        "example": {
          "id": "8a7b6c5d-cccc-4000-8000-000000000011",
          "object": "pipeline_stage",
          "pipeline_id": "1cf7d87b-7d99-4ba0-b277-0e88ac127e75",
          "key": "claim_approved",
          "label": "Claim Approved",
          "order": 6,
          "is_completed": false,
          "is_lost": false
        }
      },
      "Pipeline": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "pipeline"
          },
          "name": {
            "type": "string"
          },
          "is_default": {
            "type": "boolean"
          },
          "order": {
            "type": [
              "integer",
              "null"
            ]
          },
          "stages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PipelineStage"
            }
          }
        },
        "example": {
          "id": "1cf7d87b-7d99-4ba0-b277-0e88ac127e75",
          "object": "pipeline",
          "name": "Insurance",
          "is_default": true,
          "order": 0,
          "stages": [
            {
              "id": "8a7b6c5d-cccc-4000-8000-000000000010",
              "object": "pipeline_stage",
              "pipeline_id": "1cf7d87b-7d99-4ba0-b277-0e88ac127e75",
              "key": "lead",
              "label": "Lead",
              "order": 0,
              "is_completed": false,
              "is_lost": false
            },
            {
              "id": "8a7b6c5d-cccc-4000-8000-000000000011",
              "object": "pipeline_stage",
              "pipeline_id": "1cf7d87b-7d99-4ba0-b277-0e88ac127e75",
              "key": "claim_approved",
              "label": "Claim Approved",
              "order": 6,
              "is_completed": false,
              "is_lost": false
            }
          ]
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "user"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ]
          }
        },
        "example": {
          "id": "5d0e1c2b-7777-4000-8000-0000000000ab",
          "object": "user",
          "name": "Sam Carter",
          "email": "sam@ridgelineroofing.example",
          "role": "member"
        }
      },
      "EventDefinition": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "example": "job.stage_changed"
          },
          "resource": {
            "type": "string",
            "example": "job"
          },
          "label": {
            "type": "string",
            "example": "Job Stage Changed"
          },
          "description": {
            "type": "string"
          },
          "filters": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "example": "stage"
                },
                "label": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "stage",
                    "user",
                    "pipeline",
                    "enum",
                    "boolean"
                  ]
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "example": {
          "event": "job.stage_changed",
          "resource": "job",
          "label": "Job Stage Changed",
          "description": "A job moved to a different stage on the board.",
          "filters": [
            {
              "key": "stage",
              "label": "Stage",
              "kind": "stage"
            },
            {
              "key": "previous_stage",
              "label": "Previous stage",
              "kind": "stage"
            },
            {
              "key": "job_type",
              "label": "Job type",
              "kind": "enum",
              "values": [
                "insurance",
                "retail"
              ]
            },
            {
              "key": "pipeline_id",
              "label": "Pipeline",
              "kind": "pipeline"
            }
          ]
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "webhook"
          },
          "event": {
            "type": "string",
            "description": "An event name, or \"*\" for every event."
          },
          "target_url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "filters": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "disabled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "example": {
          "id": "0f8c8c1b-dddd-4000-8000-000000000022",
          "object": "webhook",
          "event": "job.stage_changed",
          "target_url": "https://hooks.zapier.com/hooks/standard/1234567/abcdef/",
          "description": "Slack: approved claims",
          "filters": {
            "stage": "claim_approved"
          },
          "created_at": "2026-09-20T18:30:00.000Z",
          "disabled_at": null
        }
      },
      "WebhookCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Webhook"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "The signing secret. Returned ONCE.",
                "example": "whsec_3a2b1c5f…"
              }
            }
          }
        ]
      },
      "WebhookCreate": {
        "type": "object",
        "required": [
          "target_url"
        ],
        "properties": {
          "event": {
            "type": "string",
            "description": "One event, or \"*\"."
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Several events for one URL."
          },
          "target_url": {
            "type": "string",
            "format": "uri",
            "description": "`targetUrl` works too (Zapier's spelling)."
          },
          "description": {
            "type": "string"
          },
          "filters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Only with a single event. See GET /events."
          }
        }
      },
      "EventEnvelope": {
        "type": "object",
        "description": "Every delivery is the record's own fields at the top level plus these. Dedupe on `event_id`: a retry or a resend carries the same one.\n",
        "properties": {
          "event_type": {
            "type": "string",
            "example": "job.stage_changed"
          },
          "event_id": {
            "type": "string",
            "format": "uuid",
            "description": "Also the X-HailMate-Delivery header."
          },
          "event_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the change happened."
          },
          "event_workspace_id": {
            "type": "string",
            "format": "uuid"
          },
          "event_test": {
            "type": "boolean",
            "description": "Present and true on a test send from Settings."
          },
          "previous_stage": {
            "type": [
              "string",
              "null"
            ],
            "description": "`job.stage_changed` only."
          },
          "previous_stage_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "`job.stage_changed` only."
          },
          "changed_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "`*.updated` only — the public field names that changed."
          },
          "previous_assigned_to_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "`job.assigned` only."
          },
          "previous_knock_result": {
            "type": [
              "string",
              "null"
            ],
            "description": "`pin.result_changed` only."
          }
        },
        "example": {
          "event_type": "job.stage_changed",
          "event_id": "2cb27b8c-4e1f-4a3b-9d2c-6f7e8a9b0c1d",
          "event_at": "2026-09-28T19:42:57.388Z",
          "event_workspace_id": "5f0c2a8e-1b3d-4c7a-9e21-7a3b8d4c6e10",
          "previous_stage": "inspection_complete",
          "previous_stage_label": "Inspection Complete"
        }
      }
    }
  }
}
