{
  "swagger": "2.0",
  "info": {
    "contact": {},
    "title": "ServiceKeel REST API",
    "version": "1.0",
    "description": "ServiceKeel field service operations and partner integrations, including Partner OAuth."
  },
  "paths": {
    "/field_service/customers": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "List customers",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_customers"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "Create customer",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_customers"
      }
    },
    "/field_service/customers/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "Get customer",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_customers_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "Update customer (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_customers_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Customers"
        ],
        "summary": "Archive customer",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_customers_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "Update customer (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_customers_id"
      }
    },
    "/field_service/customers/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "Run customer action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_customers_id_actions"
      }
    },
    "/field_service/customers/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Customers"
        ],
        "summary": "Check customer deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_customers_id_deletion_dependencies"
      }
    },
    "/field_service/customers/{id}/merge": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owners/admins only. Keeps the target identity and contact values, merges contacts/properties/tags, revokes source portal access, and moves one related record per request. Repeat while has_more is true. Source archival and the merge target are durable so retries resume. Financial records with any payment ledger, credits, invoice allocations, or pending checkout block merging. Saved payment authorizations, referral bindings, exported accounting mappings, and source-customer record-bound PDFs block merging. Image/video attachments are retained internally on the target without granting new customer visibility.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "Preview or advance a duplicate customer merge",
        "parameters": [
          {
            "type": "string",
            "description": "Duplicate source customer",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Target and reviewed record versions",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceCustomerMergeRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_customers_id_merge"
      }
    },
    "/field_service/customers/{id}/portal": {
      "post": {
        "description": "Owner/admin only. Link grants access to this customer within the authenticated tenant for 30 days. Optionally delivers the link through SES.\nEmail delivery requires a valid customer email. Missing details return requirements_not_met with edit_customer_email before any portal token is issued.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "Create a revocable customer portal link",
        "parameters": [
          {
            "type": "string",
            "description": "Customer ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Delivery options",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServicePortalIssueRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_customers_id_portal"
      }
    },
    "/field_service/customers/{id}/portal/revoke": {
      "post": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "Revoke a customer portal link",
        "parameters": [
          {
            "type": "string",
            "description": "Customer ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "boolean"
              }
            }
          }
        },
        "operationId": "post_field_service_customers_id_portal_revoke"
      }
    },
    "/field_service/customers/import": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owners/admins only. Process one row per request for bounded Lambda execution. The same import ID and row number are idempotent. Existing exact email matches are skipped; existing records and marketing consent are never overwritten.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customers"
        ],
        "summary": "Validate or import one mapped customer CSV row",
        "parameters": [
          {
            "description": "Mapped row and stable import ID",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceCustomerImportRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_customers_import"
      }
    },
    "/field_service/locations": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Lists service properties embedded in non-archived tenant customers. Returns items and has_more; paginate with offset.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Locations"
        ],
        "summary": "List tenant service locations",
        "parameters": [
          {
            "type": "string",
            "description": "Customer ID",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service location ID",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Alias for property_id; must agree when both are supplied",
            "name": "location_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Case-insensitive location address search",
            "name": "address",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Exact postal code, case-insensitive",
            "name": "postal_code",
            "in": "query"
          },
          {
            "maximum": 200,
            "minimum": 1,
            "type": "integer",
            "default": 50,
            "description": "Page size",
            "name": "limit",
            "in": "query"
          },
          {
            "maximum": 100000,
            "minimum": 0,
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "properties": {
                "has_more": {
                  "type": "boolean"
                },
                "items": {
                  "type": "array",
                  "items": {
                    "$ref": "#/definitions/models.FieldServiceLocation"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "500": {
            "description": "Internal Server Error",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_locations"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Appends a property to an existing customer in the authenticated tenant. customer_id and address are required; id and customer_name are assigned by the server. Duplicate normalized addresses for the same customer return 409.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Locations"
        ],
        "summary": "Create a customer service location",
        "parameters": [
          {
            "description": "Service location; customer_id and address are required",
            "name": "location",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceLocation"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceLocation"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "500": {
            "description": "Internal Server Error",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_locations"
      }
    },
    "/field_service/requests": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Requests"
        ],
        "summary": "List requests",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_requests"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Requests"
        ],
        "summary": "Create request",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_requests"
      }
    },
    "/field_service/requests/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Requests"
        ],
        "summary": "Get request",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_requests_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Requests"
        ],
        "summary": "Update request (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_requests_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Requests"
        ],
        "summary": "Archive request",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_requests_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Requests"
        ],
        "summary": "Update request (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_requests_id"
      }
    },
    "/field_service/requests/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Requests"
        ],
        "summary": "Run request action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_requests_id_actions"
      }
    },
    "/field_service/requests/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Requests"
        ],
        "summary": "Check request deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_requests_id_deletion_dependencies"
      }
    },
    "/field_service/estimates": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Estimates"
        ],
        "summary": "List estimates",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_estimates"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Estimates"
        ],
        "summary": "Create estimate",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_estimates"
      }
    },
    "/field_service/estimates/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Estimates"
        ],
        "summary": "Get estimate",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_estimates_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Estimates"
        ],
        "summary": "Update estimate (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_estimates_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Estimates"
        ],
        "summary": "Archive estimate",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_estimates_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Estimates"
        ],
        "summary": "Update estimate (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_estimates_id"
      }
    },
    "/field_service/estimates/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Estimates"
        ],
        "summary": "Run estimate action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_estimates_id_actions"
      }
    },
    "/field_service/estimates/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Estimates"
        ],
        "summary": "Check estimate deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_estimates_id_deletion_dependencies"
      }
    },
    "/field_service/jobs": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Jobs"
        ],
        "summary": "List jobs",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_jobs"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Jobs"
        ],
        "summary": "Create job",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_jobs"
      }
    },
    "/field_service/jobs/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Jobs"
        ],
        "summary": "Get job",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_jobs_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Jobs"
        ],
        "summary": "Update job (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_jobs_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Jobs"
        ],
        "summary": "Archive job",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_jobs_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Jobs"
        ],
        "summary": "Update job (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_jobs_id"
      }
    },
    "/field_service/jobs/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Jobs"
        ],
        "summary": "Run job action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_jobs_id_actions"
      }
    },
    "/field_service/jobs/{id}/cancel": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Scope is job_only, future_appointments or all_open_appointments. Each call cancels at most 25 visits. Repeat with the same reason and scope while complete is false; already-cancelled appointments are skipped and the original cutoff is preserved. Completed visits are retained.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Jobs"
        ],
        "summary": "Cancel a job and optionally its open appointments in resumable batches",
        "parameters": [
          {
            "type": "string",
            "description": "Job ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Reason and scope",
            "name": "cancellation",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceCancelJobRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_jobs_id_cancel"
      }
    },
    "/field_service/jobs/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Jobs"
        ],
        "summary": "Check job deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_jobs_id_deletion_dependencies"
      }
    },
    "/field_service/jobs/{id}/profitability": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owner/admin only. One-off jobs use their entire history; recurring jobs use the latest 30 days of completed visits, approved time, expenses by expense date and sent invoice allocations. Tax is excluded; billed revenue and gross profit are before credits/refunds. Recurring planned_revenue_cents is only the sum of completed visit budgets, not fixed-cycle billed or projected revenue. Enter a cost once: line-item costs and approved expenses are additive. This is job costing, not company accounting.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Jobs"
        ],
        "summary": "Read job costs and profitability",
        "parameters": [
          {
            "type": "string",
            "description": "Job ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_jobs_id_profitability"
      }
    },
    "/field_service/visits": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Visits"
        ],
        "summary": "List visits",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_visits"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Visits"
        ],
        "summary": "Create visit",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_visits"
      }
    },
    "/field_service/visits/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Visits"
        ],
        "summary": "Get visit",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_visits_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Visits"
        ],
        "summary": "Update visit (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_visits_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Visits"
        ],
        "summary": "Archive visit",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_visits_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Visits"
        ],
        "summary": "Update visit (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_visits_id"
      }
    },
    "/field_service/visits/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Visits"
        ],
        "summary": "Run visit action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_visits_id_actions"
      }
    },
    "/field_service/visits/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Visits"
        ],
        "summary": "Check visit deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_visits_id_deletion_dependencies"
      }
    },
    "/field_service/visits/{id}/tracking": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Visits"
        ],
        "summary": "Read an appointment's dispatch status, arrival window and available ETA",
        "parameters": [
          {
            "type": "string",
            "description": "Visit ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_visits_id_tracking"
      }
    },
    "/field_service/recurring_services": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Lists jobs with a recurrence rule, using the same filters and response as the job list. The recurring filter is always true. Planned next_service_at and upcoming_service_dates are derived from recurrence; booked visits remain authoritative.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Recurring services"
        ],
        "summary": "List tenant recurring services",
        "parameters": [
          {
            "type": "string",
            "description": "Customer ID",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service location ID",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Alias for property_id",
            "name": "location_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "boolean",
            "description": "Filter active jobs",
            "name": "active",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Assigned staff user ID",
            "name": "assigned_user_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service catalog ID",
            "name": "service_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Start boundary (RFC3339)",
            "name": "starts_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "End boundary (RFC3339)",
            "name": "ends_before",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          },
          {
            "maximum": 200,
            "minimum": 1,
            "type": "integer",
            "default": 50,
            "description": "Page size",
            "name": "limit",
            "in": "query"
          },
          {
            "maximum": 100000,
            "minimum": 0,
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "500": {
            "description": "Internal Server Error",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_recurring_services"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Creates a recurring job using the existing job creation workflow. recurrence and starts_at are required. Job validation, tenant-scoped references and server-calculated prices apply. Planned service dates do not replace booked visits.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Recurring services"
        ],
        "summary": "Create a tenant recurring service",
        "parameters": [
          {
            "description": "Recurring job; recurrence and starts_at are required",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "500": {
            "description": "Internal Server Error",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_recurring_services"
      }
    },
    "/field_service/availability": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Slots are advisory for five minutes and are not held. Create or update a visit to book; the existing visit write serializes conflict checks and returns 409 for conflicts. Working hours and buffers come from saved booking forms, qualifications and coverage from scheduling settings.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Scheduling"
        ],
        "summary": "Find tenant appointment availability across booking forms",
        "parameters": [
          {
            "type": "string",
            "description": "Catalog service",
            "name": "service_id",
            "in": "query",
            "required": true
          },
          {
            "type": "string",
            "description": "Range start RFC3339",
            "name": "starts_after",
            "in": "query",
            "required": true
          },
          {
            "type": "string",
            "description": "Range end RFC3339, at most 31 days",
            "name": "ends_before",
            "in": "query",
            "required": true
          },
          {
            "type": "string",
            "description": "Response timezone",
            "name": "timezone",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Restrict to one booking form",
            "name": "booking_form_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Saved service location",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Optional specific staff member",
            "name": "staff_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Override duration, 15-480 minutes",
            "name": "duration_minutes",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_availability"
      }
    },
    "/field_service/calendar_feed": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Scheduling"
        ],
        "summary": "Get the current user's Field Service calendar subscription status",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_calendar_feed"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Uses the current shared tenant and user. Mine includes assigned visits; all requires owner/admin. Default output includes appointment times only. Include details adds titles and locations, never customer contact, notes or financial fields. Issuing a new link revokes the previous link.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Scheduling"
        ],
        "summary": "Create or rotate the current user's calendar subscription",
        "parameters": [
          {
            "description": "Calendar scope",
            "name": "options",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceCalendarIssueRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_calendar_feed"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Scheduling"
        ],
        "summary": "Revoke the current user's calendar subscription",
        "responses": {
          "204": {
            "description": "No Content"
          }
        },
        "operationId": "delete_field_service_calendar_feed"
      }
    },
    "/field_service/scheduling_settings": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Scheduling"
        ],
        "summary": "Read branch coverage, staff qualifications and service scheduling rules",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "get_field_service_scheduling_settings"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Scheduling"
        ],
        "summary": "Save scheduling rules for existing tenant catalog services and shared users",
        "parameters": [
          {
            "description": "Current version and scheduling settings",
            "name": "settings",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "put_field_service_scheduling_settings"
      }
    },
    "/field_service/service_area": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Scheduling"
        ],
        "summary": "Check service coverage by postal code or saved service location",
        "parameters": [
          {
            "type": "string",
            "description": "Postal code",
            "name": "postal_code",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Exact saved service address",
            "name": "address",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Saved service location",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer owning the location",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Catalog service",
            "name": "service_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Configured service type",
            "name": "service_type",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_service_area"
      }
    },
    "/field_service/routing/apply": {
      "post": {
        "description": "Owner/admin only. Rechecks tenant record versions, crew assignments, current day availability and schedule lease. Saves progress after each changed visit; retries resume already applied work safely. Stale proposals require regeneration. Streams status and final proposal.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Routing"
        ],
        "summary": "Apply an explicitly reviewed route proposal",
        "parameters": [
          {
            "description": "Reviewed proposal ID",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceRouteApplyRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE status, proposal, done or error",
            "schema": {
              "type": "string"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_routing_apply"
      }
    },
    "/field_service/routing/geocode": {
      "post": {
        "description": "Owner/admin only. Resolves a saved customer or its property address within the authenticated tenant. Rejects ambiguous and partial address matches. Streams geocode result and done.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Routing"
        ],
        "summary": "Locate a tenant customer service address with Google Maps",
        "parameters": [
          {
            "description": "Tenant customer and optional property ID",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceGeocodeRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE geocode or error",
            "schema": {
              "type": "string"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_routing_geocode"
      }
    },
    "/field_service/routing/proposals/{id}": {
      "get": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Routing"
        ],
        "summary": "Get a saved tenant route proposal",
        "parameters": [
          {
            "type": "string",
            "description": "Proposal ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_routing_proposals_id"
      }
    },
    "/field_service/routing/propose": {
      "post": {
        "description": "Owner/admin only. Uses stored tenant service addresses with Google Routes. Up to 25 visits across seven local days; fixed, unscheduled and completed work is excluded. Groups preserve worker/crew assignments, day, service duration, and first/last stops. Streams progress, a saved proposal, and done. Does not change visits until explicit apply.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Routing"
        ],
        "summary": "Propose optimized routes for selected anytime visits",
        "parameters": [
          {
            "description": "Selected visit IDs and IANA timezone",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceRouteRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE status, proposal, done or error",
            "schema": {
              "type": "string"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_routing_propose"
      }
    },
    "/field_service/services": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Services"
        ],
        "summary": "List catalog services with approved fees, duration and required skills",
        "parameters": [
          {
            "type": "string",
            "description": "Service type",
            "name": "service_type",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Branch",
            "name": "branch_id",
            "in": "query"
          },
          {
            "type": "boolean",
            "description": "Active catalog items",
            "name": "active",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_services"
      }
    },
    "/field_service/catalog": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Service catalog"
        ],
        "summary": "List service catalog",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_catalog"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Service catalog"
        ],
        "summary": "Create catalog item",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_catalog"
      }
    },
    "/field_service/catalog/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Service catalog"
        ],
        "summary": "Get catalog item",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_catalog_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Service catalog"
        ],
        "summary": "Update catalog item (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_catalog_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Service catalog"
        ],
        "summary": "Archive catalog item",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_catalog_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Service catalog"
        ],
        "summary": "Update catalog item (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_catalog_id"
      }
    },
    "/field_service/catalog/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Service catalog"
        ],
        "summary": "Run catalog item action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_catalog_id_actions"
      }
    },
    "/field_service/catalog/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Service catalog"
        ],
        "summary": "Check catalog item deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_catalog_id_deletion_dependencies"
      }
    },
    "/field_service/equipment": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns equipment stored on customer service locations. Each item includes customer_id, property_id and location_id. Pass next_cursor as cursor for the next page; offset pagination is not supported. A page may have no matching items and still have has_more=true.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Equipment"
        ],
        "summary": "List tenant service-location equipment",
        "parameters": [
          {
            "type": "string",
            "description": "Customer ID",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service location ID",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Alias for property_id; must agree when both are supplied",
            "name": "location_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Case-insensitive location address search",
            "name": "address",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Exact postal code, case-insensitive",
            "name": "postal_code",
            "in": "query"
          },
          {
            "maximum": 200,
            "minimum": 1,
            "type": "integer",
            "default": 50,
            "description": "Page size",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Exact equipment type, case-insensitive",
            "name": "equipment_type",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Opaque next_cursor from the previous response",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "properties": {
                "has_more": {
                  "type": "boolean"
                },
                "items": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  }
                },
                "next_cursor": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Stored equipment could not be read",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "500": {
            "description": "Internal Server Error",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_equipment"
      }
    },
    "/field_service/memberships": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Memberships"
        ],
        "summary": "List memberships",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_memberships"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Memberships"
        ],
        "summary": "Create membership",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_memberships"
      }
    },
    "/field_service/memberships/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Memberships"
        ],
        "summary": "Get membership",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_memberships_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Memberships"
        ],
        "summary": "Update membership (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_memberships_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Memberships"
        ],
        "summary": "Archive membership",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_memberships_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Memberships"
        ],
        "summary": "Update membership (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_memberships_id"
      }
    },
    "/field_service/memberships/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Memberships"
        ],
        "summary": "Run membership action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_memberships_id_actions"
      }
    },
    "/field_service/memberships/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Memberships"
        ],
        "summary": "Check membership deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_memberships_id_deletion_dependencies"
      }
    },
    "/field_service/staff": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Staff"
        ],
        "summary": "List shared staff with branch, skills, coverage and on-call details",
        "parameters": [
          {
            "type": "string",
            "description": "Branch",
            "name": "branch_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Comma-separated required skills",
            "name": "skills",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Coverage postal code",
            "name": "postal_code",
            "in": "query"
          },
          {
            "type": "boolean",
            "description": "Active staff (defaults to true)",
            "name": "active",
            "in": "query"
          },
          {
            "type": "boolean",
            "description": "On-call status",
            "name": "on_call",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_staff"
      }
    },
    "/field_service/crews": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Crews"
        ],
        "summary": "List crews",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_crews"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Crews"
        ],
        "summary": "Create crew",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_crews"
      }
    },
    "/field_service/crews/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Crews"
        ],
        "summary": "Get crew",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_crews_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Crews"
        ],
        "summary": "Update crew (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_crews_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Crews"
        ],
        "summary": "Archive crew",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_crews_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Crews"
        ],
        "summary": "Update crew (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_crews_id"
      }
    },
    "/field_service/crews/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Crews"
        ],
        "summary": "Run crew action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_crews_id_actions"
      }
    },
    "/field_service/crews/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Crews"
        ],
        "summary": "Check crew deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_crews_id_deletion_dependencies"
      }
    },
    "/field_service/notes": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Notes"
        ],
        "summary": "List notes",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_notes"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Notes"
        ],
        "summary": "Create note",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_notes"
      }
    },
    "/field_service/notes/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Notes"
        ],
        "summary": "Get note",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_notes_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Notes"
        ],
        "summary": "Update note (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_notes_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Notes"
        ],
        "summary": "Archive note",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_notes_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Notes"
        ],
        "summary": "Update note (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_notes_id"
      }
    },
    "/field_service/notes/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Notes"
        ],
        "summary": "Run note action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_notes_id_actions"
      }
    },
    "/field_service/notes/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Notes"
        ],
        "summary": "Check note deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_notes_id_deletion_dependencies"
      }
    },
    "/field_service/checklists": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Checklists"
        ],
        "summary": "List checklists",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_checklists"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Checklists"
        ],
        "summary": "Create checklist",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_checklists"
      }
    },
    "/field_service/checklists/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Checklists"
        ],
        "summary": "Get checklist",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_checklists_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Checklists"
        ],
        "summary": "Update checklist (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_checklists_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Checklists"
        ],
        "summary": "Archive checklist",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_checklists_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Checklists"
        ],
        "summary": "Update checklist (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_checklists_id"
      }
    },
    "/field_service/checklists/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Checklists"
        ],
        "summary": "Run checklist action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_checklists_id_actions"
      }
    },
    "/field_service/checklists/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Checklists"
        ],
        "summary": "Check checklist deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_checklists_id_deletion_dependencies"
      }
    },
    "/field_service/chemicals": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Chemicals"
        ],
        "summary": "List chemicals",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_chemicals"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Chemicals"
        ],
        "summary": "Create chemical record",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_chemicals"
      }
    },
    "/field_service/chemicals/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Chemicals"
        ],
        "summary": "Get chemical record",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_chemicals_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Chemicals"
        ],
        "summary": "Update chemical record (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_chemicals_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Chemicals"
        ],
        "summary": "Archive chemical record",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_chemicals_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Chemicals"
        ],
        "summary": "Update chemical record (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_chemicals_id"
      }
    },
    "/field_service/chemicals/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Chemicals"
        ],
        "summary": "Run chemical record action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_chemicals_id_actions"
      }
    },
    "/field_service/chemicals/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Chemicals"
        ],
        "summary": "Check chemical record deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_chemicals_id_deletion_dependencies"
      }
    },
    "/field_service/pipeline_stages": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Pipeline stages"
        ],
        "summary": "List pipeline stages",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_pipeline_stages"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Pipeline stages"
        ],
        "summary": "Create pipeline stage",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_pipeline_stages"
      }
    },
    "/field_service/pipeline_stages/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Pipeline stages"
        ],
        "summary": "Get pipeline stage",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_pipeline_stages_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Pipeline stages"
        ],
        "summary": "Update pipeline stage (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_pipeline_stages_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Pipeline stages"
        ],
        "summary": "Archive pipeline stage",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_pipeline_stages_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Pipeline stages"
        ],
        "summary": "Update pipeline stage (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_pipeline_stages_id"
      }
    },
    "/field_service/pipeline_stages/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Pipeline stages"
        ],
        "summary": "Run pipeline stage action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_pipeline_stages_id_actions"
      }
    },
    "/field_service/pipeline_stages/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Pipeline stages"
        ],
        "summary": "Check pipeline stage deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_pipeline_stages_id_deletion_dependencies"
      }
    },
    "/field_service/invoices": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Invoices"
        ],
        "summary": "List invoices",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_invoices"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Invoices"
        ],
        "summary": "Create invoice",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_invoices"
      }
    },
    "/field_service/invoices/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Invoices"
        ],
        "summary": "Get invoice",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_invoices_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Invoices"
        ],
        "summary": "Update invoice (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_invoices_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Invoices"
        ],
        "summary": "Archive invoice",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_invoices_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Invoices"
        ],
        "summary": "Update invoice (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_invoices_id"
      }
    },
    "/field_service/invoices/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Invoices"
        ],
        "summary": "Run invoice action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_invoices_id_actions"
      }
    },
    "/field_service/invoices/{id}/attachments": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns attachments with default titles for legacy filenames. Scoped to the authenticated tenant.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Invoices"
        ],
        "summary": "List invoice attachment titles",
        "parameters": [
          {
            "type": "string",
            "description": "Invoice ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/models.FieldServiceAttachment"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_invoices_id_attachments"
      }
    },
    "/field_service/invoices/{id}/attachments/{asset_id}/title": {
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Changes only attachment display metadata, including on issued invoices. Preserves the original filename and financial fields; checks the invoice version and records an audit entry. Archived invoices and invoices awaiting email delivery cannot be changed.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Invoices"
        ],
        "summary": "Update an invoice attachment title",
        "parameters": [
          {
            "type": "string",
            "description": "Invoice ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Attached asset ID",
            "name": "asset_id",
            "in": "path",
            "required": true
          },
          {
            "description": "Display title and current invoice version",
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceAttachmentTitleRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "put_field_service_invoices_id_attachments_asset_id_title"
      }
    },
    "/field_service/invoices/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Invoices"
        ],
        "summary": "Check invoice deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_invoices_id_deletion_dependencies"
      }
    },
    "/field_service/payments/customers/{id}/wallet": {
      "post": {
        "description": "Owner/admin only. Methods require prior verified customer-hub consent. Returns masked details.",
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Payments"
        ],
        "summary": "View saved methods for an authorized business customer",
        "parameters": [
          {
            "type": "string",
            "description": "Tenant customer ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "SSE wallet or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_payments_customers_id_wallet"
      }
    },
    "/field_service/payments/estimates/{id}/cancel-checkout": {
      "post": {
        "description": "Verifies Checkout expiry or off-session PaymentIntent cancellation with Stripe before clearing the tenant document lock. Completed or processing payments must reconcile first.",
        "tags": [
          "Payments"
        ],
        "summary": "Expire a pending checkout and unlock a financial document",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_payments_estimates_id_cancel_checkout"
      }
    },
    "/field_service/payments/estimates/{id}/charge": {
      "post": {
        "description": "Owner/admin confirms an existing invoice balance or approved estimate deposit. Verifies connected customer, recorded consent, method, mandate and bank limits. Persists a document lock before an idempotent off-session PaymentIntent. No recurring schedule is created. Requires-action and pending bank debits remain unpaid until signed webhook reconciliation.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Payments"
        ],
        "summary": "Charge a customer-consented saved method",
        "parameters": [
          {
            "type": "string",
            "description": "Document ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "payment_method_id, confirmed:true, expected_amount_cents, version",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE payment or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_payments_estimates_id_charge"
      }
    },
    "/field_service/payments/estimates/{id}/checkout": {
      "post": {
        "description": "Computes the USD balance from tenant records and creates a direct charge on the tenant's existing Stripe Connect account. Owners/admins only. A checkout locks financial edits until payment or explicit cancellation. Application fee is zero.\nOptional amount_cents supports partial payments up to the collectible balance. Optional expires_at is 30 minutes to 24 hours from creation. The response includes the actual amount and expiration.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Payments"
        ],
        "summary": "Create invoice or approved estimate deposit checkout",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Payment amount, expiration and method",
            "name": "options",
            "in": "body",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceCheckoutOptions"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_payments_estimates_id_checkout"
      }
    },
    "/field_service/payments/invoices/{id}/cancel-checkout": {
      "post": {
        "description": "Verifies Checkout expiry or off-session PaymentIntent cancellation with Stripe before clearing the tenant document lock. Completed or processing payments must reconcile first.",
        "tags": [
          "Payments"
        ],
        "summary": "Expire a pending checkout and unlock a financial document",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_payments_invoices_id_cancel_checkout"
      }
    },
    "/field_service/payments/invoices/{id}/charge": {
      "post": {
        "description": "Owner/admin confirms an existing invoice balance or approved estimate deposit. Verifies connected customer, recorded consent, method, mandate and bank limits. Persists a document lock before an idempotent off-session PaymentIntent. No recurring schedule is created. Requires-action and pending bank debits remain unpaid until signed webhook reconciliation.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Payments"
        ],
        "summary": "Charge a customer-consented saved method",
        "parameters": [
          {
            "type": "string",
            "description": "Document ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "payment_method_id, confirmed:true, expected_amount_cents, version",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE payment or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_payments_invoices_id_charge"
      }
    },
    "/field_service/payments/invoices/{id}/checkout": {
      "post": {
        "description": "Computes the USD balance from tenant records and creates a direct charge on the tenant's existing Stripe Connect account. Owners/admins only. A checkout locks financial edits until payment or explicit cancellation. Application fee is zero.\nOptional amount_cents supports partial payments up to the collectible balance. Optional expires_at is 30 minutes to 24 hours from creation. The response includes the actual amount and expiration.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Payments"
        ],
        "summary": "Create invoice or approved estimate deposit checkout",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Payment amount, expiration and method",
            "name": "options",
            "in": "body",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceCheckoutOptions"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_payments_invoices_id_checkout"
      }
    },
    "/field_service/payments/webhook": {
      "post": {
        "description": "Verifies FIELD_SERVICE_STRIPE_WEBHOOK_SECRET, resolves the tenant from signed metadata and validates its existing StripeAccountID. Retrieves current Stripe state and reconciles ledger allocations with bounded compare-and-swap retries; redirects never mark payments paid.",
        "tags": [
          "Payments"
        ],
        "summary": "Stripe connected-account payment webhook",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_payments_webhook"
      }
    },
    "/field_service/expenses": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Expenses"
        ],
        "summary": "List expenses",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_expenses"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Expenses"
        ],
        "summary": "Create expense",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_expenses"
      }
    },
    "/field_service/expenses/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Expenses"
        ],
        "summary": "Get expense",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_expenses_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Expenses"
        ],
        "summary": "Update expense (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_expenses_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Expenses"
        ],
        "summary": "Archive expense",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_expenses_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Expenses"
        ],
        "summary": "Update expense (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_expenses_id"
      }
    },
    "/field_service/expenses/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Expenses"
        ],
        "summary": "Run expense action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_expenses_id_actions"
      }
    },
    "/field_service/expenses/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Expenses"
        ],
        "summary": "Check expense deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_expenses_id_deletion_dependencies"
      }
    },
    "/field_service/time_entries": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Time entries"
        ],
        "summary": "List time entries",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_time_entries"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Time entries"
        ],
        "summary": "Create time entry",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_time_entries"
      }
    },
    "/field_service/time_entries/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Time entries"
        ],
        "summary": "Get time entry",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_time_entries_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Time entries"
        ],
        "summary": "Update time entry (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_time_entries_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Time entries"
        ],
        "summary": "Archive time entry",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_time_entries_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Time entries"
        ],
        "summary": "Update time entry (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_time_entries_id"
      }
    },
    "/field_service/time_entries/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Time entries"
        ],
        "summary": "Run time entry action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_time_entries_id_actions"
      }
    },
    "/field_service/time_entries/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Time entries"
        ],
        "summary": "Check time entry deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_time_entries_id_deletion_dependencies"
      }
    },
    "/field_service/calls": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Call records"
        ],
        "summary": "List call records",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_calls"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Call records"
        ],
        "summary": "Create call record",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_calls"
      }
    },
    "/field_service/calls/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Call records"
        ],
        "summary": "Get call record",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_calls_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Call records"
        ],
        "summary": "Update call record (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_calls_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Call records"
        ],
        "summary": "Archive call record",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_calls_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Call records"
        ],
        "summary": "Update call record (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_calls_id"
      }
    },
    "/field_service/calls/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Call records"
        ],
        "summary": "Run call record action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_calls_id_actions"
      }
    },
    "/field_service/calls/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Call records"
        ],
        "summary": "Check call record deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_calls_id_deletion_dependencies"
      }
    },
    "/field_service/phone/{provider}/connect": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "consumes": [
          "application/json"
        ],
        "tags": [
          "Phone"
        ],
        "summary": "Connect and validate an SMS account",
        "parameters": [
          {
            "type": "string",
            "description": "Provider",
            "name": "provider",
            "in": "path",
            "required": true
          },
          {
            "description": "Credentials",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.PhoneConnectInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PhoneConnection"
            }
          }
        },
        "operationId": "post_field_service_phone_provider_connect"
      }
    },
    "/field_service/phone/{provider}/oauth/authorize": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "Start secure phone provider sign-in",
        "parameters": [
          {
            "type": "string",
            "description": "aircall, ringcentral, dialpad",
            "name": "provider",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_phone_provider_oauth_authorize"
      }
    },
    "/field_service/phone/{provider}/oauth/exchange": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "Finish single-use tenant-bound phone provider sign-in",
        "parameters": [
          {
            "type": "string",
            "description": "aircall, ringcentral, dialpad",
            "name": "provider",
            "in": "path",
            "required": true
          },
          {
            "description": "Authorization response",
            "name": "input",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PhoneOAuthExchangeInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PhoneConnection"
            }
          }
        },
        "operationId": "post_field_service_phone_provider_oauth_exchange"
      }
    },
    "/field_service/phone/connections": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "List connected SMS providers",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_phone_connections"
      }
    },
    "/field_service/phone/connections/{id}": {
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "Disconnect an SMS provider and erase stored credentials",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          }
        },
        "operationId": "delete_field_service_phone_connections_id"
      }
    },
    "/field_service/phone/connections/{id}/booking": {
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "Enable customer-confirmed SMS booking using a ServiceKeel booking form",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Form ID, or blank to disable",
            "name": "input",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PhoneBookingSettings"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "put_field_service_phone_connections_id_booking"
      }
    },
    "/field_service/phone/connections/{id}/calls": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "Read RingCentral call history",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "integer",
            "description": "Page number",
            "name": "page",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_phone_connections_id_calls"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Rings the explicitly supplied callback phone before connecting the recipient. The callback is separate from business caller ID and must differ from the recipient. Ambiguous attempts are never automatically retried.",
        "tags": [
          "Phone"
        ],
        "summary": "Call a customer through the connected RingCentral phone",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Callback number, recipient and unique request key",
            "name": "input",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PhoneCallInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_phone_connections_id_calls"
      }
    },
    "/field_service/phone/connections/{id}/calls/{call}/action": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Verifies the session belongs to the authenticated extension before hold, resume, transfer or hangup.",
        "tags": [
          "Phone"
        ],
        "summary": "Control an active RingCentral call",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Telephony session ID",
            "name": "call",
            "in": "path",
            "required": true
          },
          {
            "description": "Action",
            "name": "input",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PhoneCallActionInput"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          }
        },
        "operationId": "post_field_service_phone_connections_id_calls_call_action"
      }
    },
    "/field_service/phone/connections/{id}/incoming": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Registers authenticated Aircall, Quo or Dialpad notifications for this connection. Reconciles existing registrations and never returns provider secrets. Select and save a business number first.",
        "tags": [
          "Phone"
        ],
        "summary": "Set up or repair incoming SMS notifications",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PhoneConnection"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_phone_connections_id_incoming"
      }
    },
    "/field_service/phone/connections/{id}/numbers": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "Discover accessible provider phone numbers",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_phone_connections_id_numbers"
      }
    },
    "/field_service/phone/connections/{id}/scheduled": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "List scheduled SMS and delivery outcomes",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_phone_connections_id_scheduled"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Requires explicit text permission, enabled automation, and a send time between 8 AM and 8 PM in the recipient timezone. Aircall, Quo and Dialpad support one recipient per follow-up; RingCentral High Volume supports up to 100. Every recipient receives a durable, idempotent job.",
        "tags": [
          "Phone"
        ],
        "summary": "Schedule a customer SMS follow-up or RingCentral recipient campaign",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Reviewed recipients, text and send time",
            "name": "input",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PhoneScheduleInput"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_phone_connections_id_scheduled"
      }
    },
    "/field_service/phone/connections/{id}/scheduled/{message}": {
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "Cancel an unsent scheduled SMS",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Scheduled message ID",
            "name": "message",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          }
        },
        "operationId": "delete_field_service_phone_connections_id_scheduled_message"
      }
    },
    "/field_service/phone/connections/{id}/settings": {
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "Choose an owned SMS number and enable agent eligibility",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Settings",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PhoneSettingsInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PhoneConnection"
            }
          }
        },
        "operationId": "put_field_service_phone_connections_id_settings"
      }
    },
    "/field_service/phone/connections/{id}/sync": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Queues durable maintenance. History import never triggers AI replies.",
        "tags": [
          "Phone"
        ],
        "summary": "Check SMS connection health, repair notifications and recover supported message history",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_phone_connections_id_sync"
      }
    },
    "/field_service/phone/connections/{id}/voice": {
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Uses an existing OtherPhone device on the authenticated extension. SIP credentials are encrypted with the integration and are never returned to the browser.",
        "tags": [
          "Phone"
        ],
        "summary": "Configure the RingCentral AI voice agent",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Device and voice agent settings",
            "name": "input",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PhoneVoiceInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PhoneConnection"
            }
          }
        },
        "operationId": "put_field_service_phone_connections_id_voice"
      }
    },
    "/field_service/phone/connections/{id}/voice/devices": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "List eligible RingCentral devices for the voice agent",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_phone_connections_id_voice_devices"
      }
    },
    "/field_service/phone/connections/{id}/voice/runtime": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "The file is a secret for one tenant and connection. It grants access to its SIP configuration; keep it on the private voice worker only.",
        "tags": [
          "Phone"
        ],
        "summary": "Download a scoped voice runtime connection file",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_phone_connections_id_voice_runtime"
      }
    },
    "/field_service/phone/conversations": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "List SMS conversations",
        "parameters": [
          {
            "type": "string",
            "description": "Connection",
            "name": "integration_id",
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_phone_conversations"
      }
    },
    "/field_service/phone/conversations/{id}/handoff": {
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Expected version required. Resuming does not override STOP or activate a marketing agent.",
        "tags": [
          "Phone"
        ],
        "summary": "Take over or resume an SMS conversation",
        "parameters": [
          {
            "type": "string",
            "description": "Conversation ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Reviewed handoff state",
            "name": "input",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PhoneHandoffInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.PhoneConversation"
            }
          }
        },
        "operationId": "put_field_service_phone_conversations_id_handoff"
      }
    },
    "/field_service/phone/messages": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "List persisted SMS messages",
        "parameters": [
          {
            "type": "string",
            "description": "Connection ID",
            "name": "integration_id",
            "in": "query",
            "required": true
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_phone_messages"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Requires recorded consent, enforces STOP suppression, persists an idempotency claim before any provider request, and pauses AI for a human reply. Ambiguous outcomes are not retried automatically.",
        "tags": [
          "Phone"
        ],
        "summary": "Send an SMS through the selected provider",
        "parameters": [
          {
            "description": "Message and idempotency key",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.PhoneSendInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PhoneMessage"
            }
          }
        },
        "operationId": "post_field_service_phone_messages"
      }
    },
    "/field_service/phone/messages/{id}/attachments/{attachment}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Checks tenant, connection and attachment membership. Provider access tokens never reach the browser.",
        "tags": [
          "Phone"
        ],
        "summary": "Download a message attachment",
        "parameters": [
          {
            "type": "string",
            "description": "Message ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Attachment ID",
            "name": "attachment",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "file"
            }
          }
        },
        "operationId": "get_field_service_phone_messages_id_attachments_attachment"
      }
    },
    "/field_service/phone/providers": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Phone"
        ],
        "summary": "List supported SMS integrations",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_phone_providers"
      }
    },
    "/field_service/phone/voice/{token}/config": {
      "get": {
        "description": "Requires a connection-scoped worker secret and signed tenant route, never ordinary public access.",
        "tags": [
          "Phone"
        ],
        "summary": "Read private voice worker configuration",
        "parameters": [
          {
            "type": "string",
            "description": "Signed connection route",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_phone_voice_token_config"
      }
    },
    "/field_service/phone/voice/{token}/events": {
      "post": {
        "tags": [
          "Phone"
        ],
        "summary": "Receive scoped voice worker health and call events",
        "parameters": [
          {
            "type": "string",
            "description": "Signed connection route",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "boolean"
              }
            }
          }
        },
        "operationId": "post_field_service_phone_voice_token_events"
      }
    },
    "/field_service/phone/webhooks/{provider}/{token}": {
      "post": {
        "description": "Tenant is derived from a signed route token. The provider signature is verified before any message is persisted. Duplicate events never resend an SMS. Only selected sender numbers are accepted.",
        "tags": [
          "Phone"
        ],
        "summary": "Receive authenticated phone provider events",
        "parameters": [
          {
            "type": "string",
            "description": "Provider",
            "name": "provider",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Signed tenant connection routing token",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "boolean"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_phone_webhooks_provider_token"
      }
    },
    "/field_service/email_activity": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owners/admins can inspect tenant-scoped delivery and distinct-email engagement totals from the existing signed SES event relay. Reply counts and related document references are included; message bodies are available through the separately authorized replies endpoint. No provider errors, signing tokens or internal correlation identifiers are exposed. Open/click events depend on the shared engagement configuration set and may be affected by email privacy software.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Email"
        ],
        "summary": "View Field Service customer and campaign email activity",
        "parameters": [
          {
            "type": "string",
            "description": "Filter to an existing Field Service customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Filter to an existing Field Service campaign",
            "name": "campaign_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset, 0–100000; pages contain 50 rows",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceEmailActivityResponse"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_email_activity"
      }
    },
    "/field_service/email_activity/{id}/replies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owners/admins can read plain-text replies within their authenticated tenant. Attachments are counted but never exposed or executed. Pagination is newest first.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Email"
        ],
        "summary": "Read customer replies to a ServiceKeel email",
        "parameters": [
          {
            "type": "string",
            "description": "Outgoing email ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "integer",
            "description": "Reply offset",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_email_activity_id_replies"
      }
    },
    "/field_service/email_activity/{id}/reply": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Explicit owner/admin send action. Sends plain-text content to the original tenant customer and creates a new random reply route tied to the same conversation. No automatic resend after an ambiguous failure.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Email"
        ],
        "summary": "Respond to a customer email conversation",
        "parameters": [
          {
            "type": "string",
            "description": "Outgoing email ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Response",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceEmailReplyRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_email_activity_id_reply"
      }
    },
    "/field_service/campaigns": {
      "post": {
        "description": "Saves authenticated actor, timestamp and before/after field changes in activity. Owner/admin only. Campaigns require opted-in customers. Optional growth.audience supports live unsold-estimate, lapsed-customer, recent-job, lead, city/postal, property, service and equipment filters. Audience evaluation is bounded and rejects truncation or more than 2,000 recipients; scheduling freezes the audience and sending rechecks consent. Rule triggers: estimate_followup, invoice_reminder, request_followup, job_completed. job_completed runs once per job after delay_hours (1-8760); other triggers use delay_days (1-365). Actions: email, tag (customer), archive (open requests or unpaid/unapproved estimates only). Customer status/tag and source status/catalog-item conditions are enforced. Sending processes one recipient per request and persists progress.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Campaigns"
        ],
        "summary": "Create a Field Service email campaign or follow-up rule",
        "parameters": [
          {
            "description": "Email content and segmentation/trigger",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceGrowthInput"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "post_field_service_campaigns"
      }
    },
    "/field_service/campaigns/{id}": {
      "put": {
        "description": "Saves authenticated actor, timestamp and before/after field changes in activity. Owner/admin only. Campaigns require opted-in customers. Optional growth.audience supports live unsold-estimate, lapsed-customer, recent-job, lead, city/postal, property, service and equipment filters. Audience evaluation is bounded and rejects truncation or more than 2,000 recipients; scheduling freezes the audience and sending rechecks consent. Rule triggers: estimate_followup, invoice_reminder, request_followup, job_completed. job_completed runs once per job after delay_hours (1-8760); other triggers use delay_days (1-365). Actions: email, tag (customer), archive (open requests or unpaid/unapproved estimates only). Customer status/tag and source status/catalog-item conditions are enforced. Sending processes one recipient per request and persists progress.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Campaigns"
        ],
        "summary": "Create a Field Service email campaign or follow-up rule",
        "parameters": [
          {
            "description": "Email content and segmentation/trigger",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceGrowthInput"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "put_field_service_campaigns_id"
      }
    },
    "/field_service/campaigns/{id}/cancel_schedule": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Cancels only a still-scheduled campaign with no attempted recipients; queued workers recheck the status before sending. Returns the campaign to an editable draft.",
        "consumes": [
          "application/json"
        ],
        "tags": [
          "Campaigns"
        ],
        "summary": "Cancel a campaign before scheduled sending starts",
        "parameters": [
          {
            "type": "string",
            "description": "Campaign ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Expected version; send_at is ignored",
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceCampaignScheduleInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_campaigns_id_cancel_schedule"
      }
    },
    "/field_service/campaigns/{id}/preview": {
      "get": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Campaigns"
        ],
        "summary": "Preview opted-in campaign recipients",
        "parameters": [
          {
            "type": "string",
            "description": "Campaign ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_campaigns_id_preview"
      }
    },
    "/field_service/campaigns/{id}/schedule": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owner/admin explicitly approves a future send time and current opted-in recipient snapshot. Content and audience are frozen until cancellation. Current email consent and segmentation are rechecked before each recipient; batch workers attempt one recipient per invocation. Actual delivery follows scheduler cadence.",
        "consumes": [
          "application/json"
        ],
        "tags": [
          "Campaigns"
        ],
        "summary": "Schedule a one-off Field Service email campaign",
        "parameters": [
          {
            "type": "string",
            "description": "Campaign ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Expected version and future RFC3339 send_at",
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceCampaignScheduleInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_campaigns_id_schedule"
      }
    },
    "/field_service/campaigns/{id}/send": {
      "post": {
        "description": "Explicit send action. One SES email is attempted per invocation. Progress is durable. Attempted deliveries are never automatically retried after an ambiguous failure, avoiding duplicate mail. Repeat while has_more is true.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Campaigns"
        ],
        "summary": "Send the next recipient in an email campaign",
        "parameters": [
          {
            "type": "string",
            "description": "Campaign ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_campaigns_id_send"
      }
    },
    "/field_service/automations": {
      "post": {
        "description": "Saves authenticated actor, timestamp and before/after field changes in activity. Owner/admin only. Campaigns require opted-in customers. Optional growth.audience supports live unsold-estimate, lapsed-customer, recent-job, lead, city/postal, property, service and equipment filters. Audience evaluation is bounded and rejects truncation or more than 2,000 recipients; scheduling freezes the audience and sending rechecks consent. Rule triggers: estimate_followup, invoice_reminder, request_followup, job_completed. job_completed runs once per job after delay_hours (1-8760); other triggers use delay_days (1-365). Actions: email, tag (customer), archive (open requests or unpaid/unapproved estimates only). Customer status/tag and source status/catalog-item conditions are enforced. Sending processes one recipient per request and persists progress.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Automations"
        ],
        "summary": "Create a Field Service email campaign or follow-up rule",
        "parameters": [
          {
            "description": "Email content and segmentation/trigger",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceGrowthInput"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "post_field_service_automations"
      }
    },
    "/field_service/automations/{id}": {
      "put": {
        "description": "Saves authenticated actor, timestamp and before/after field changes in activity. Owner/admin only. Campaigns require opted-in customers. Optional growth.audience supports live unsold-estimate, lapsed-customer, recent-job, lead, city/postal, property, service and equipment filters. Audience evaluation is bounded and rejects truncation or more than 2,000 recipients; scheduling freezes the audience and sending rechecks consent. Rule triggers: estimate_followup, invoice_reminder, request_followup, job_completed. job_completed runs once per job after delay_hours (1-8760); other triggers use delay_days (1-365). Actions: email, tag (customer), archive (open requests or unpaid/unapproved estimates only). Customer status/tag and source status/catalog-item conditions are enforced. Sending processes one recipient per request and persists progress.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Automations"
        ],
        "summary": "Create a Field Service email campaign or follow-up rule",
        "parameters": [
          {
            "description": "Email content and segmentation/trigger",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceGrowthInput"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "put_field_service_automations_id"
      }
    },
    "/field_service/automations/{id}/run": {
      "post": {
        "description": "Processes one candidate per invocation with durable cursor and action claim. Enforces customer status/tag and source status/catalog conditions. Email sends never replay ambiguous outcomes; customer tag and safe request/estimate archive actions resume idempotently. Invoices cannot be archived by automation.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Automations"
        ],
        "summary": "Run the next customer follow-up in an automation",
        "parameters": [
          {
            "type": "string",
            "description": "Automation rule ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceAutomationRunResult"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_automations_id_run"
      }
    },
    "/field_service/review_requests": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "A stable idempotency key prevents duplicate messages. Email requires marketing consent; link creates a link for manual sharing. Uncertain delivery is never automatically resent.",
        "consumes": [
          "application/json"
        ],
        "tags": [
          "Review requests"
        ],
        "summary": "Prepare or email a completed-job review request",
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "post_field_service_review_requests"
      }
    },
    "/field_service/referral_rewards/{id}/apply": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Core owner/admin only. Applies the full reward once to a sent ordinary invoice with sufficient balance. Recurring-cycle invoices, pending payments, and invoices already exported to accounting are excluded. Credit lowers balance without increasing cash payments. A durable reservation and deterministic credit ID make retries safe.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Referrals"
        ],
        "summary": "Apply an approved referral credit to the referrer's invoice",
        "parameters": [
          {
            "type": "string",
            "description": "Reward ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Referrer invoice",
            "name": "target",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceReferralApplyRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_referral_rewards_id_apply"
      }
    },
    "/field_service/referral_rewards/{id}/cancel_application": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Only releases a reward whose target invoice never received its deterministic credit. Applied credits cannot be silently removed or reused.",
        "tags": [
          "Referrals"
        ],
        "summary": "Cancel an interrupted referral-credit reservation",
        "parameters": [
          {
            "type": "string",
            "description": "Reward ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "post_field_service_referral_rewards_id_cancel_application"
      }
    },
    "/field_service/referrals/{id}/reward": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Core owner/admin only. Requires a completed job attributed to this referral and a fully cash-paid source invoice. For a combined source invoice, the percentage uses only this job's gross allocation. Fixed rewards cannot exceed that value. A referral earns one immutable reward; retries with the same terms return it.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Referrals"
        ],
        "summary": "Approve a single referral reward after completed, paid work",
        "parameters": [
          {
            "type": "string",
            "description": "Referral ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Reviewed reward terms",
            "name": "reward",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceReferralRewardRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_referrals_id_reward"
      }
    },
    "/field_service/website": {
      "get": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Website"
        ],
        "summary": "Get the business website draft and publication state",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_website"
      },
      "put": {
        "description": "Records authenticated actor, timestamp and draft field changes in activity history.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Website"
        ],
        "summary": "Save the business website draft without changing its published snapshot",
        "parameters": [
          {
            "description": "Draft content and current version",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceWebsiteInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_website"
      }
    },
    "/field_service/website/preview": {
      "get": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Website"
        ],
        "summary": "Preview the saved website draft with the existing private media signer",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_website_preview"
      }
    },
    "/field_service/website/publication": {
      "delete": {
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Website"
        ],
        "summary": "Unpublish the website and revoke its public link",
        "parameters": [
          {
            "description": "Current version",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceWebsitePublishInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_website_publication"
      }
    },
    "/field_service/website/publish": {
      "post": {
        "description": "Requires Field Service Core and explicit confirmation. Publishes an immutable snapshot; later draft edits remain private until republished.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Website"
        ],
        "summary": "Publish the reviewed website draft",
        "parameters": [
          {
            "description": "Reviewed version and confirmation",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceWebsitePublishInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_website_publish"
      }
    },
    "/field_service/marketing/workspace/{module}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owner/admin and Core entitlement required. All records are tenant scoped. Source metrics are labelled as manual imports or provider syncs.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Marketing workspace"
        ],
        "summary": "List marketing workspace records",
        "parameters": [
          {
            "type": "string",
            "description": "agents, direct-mail, reputation, or ads",
            "name": "module",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Name search",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status filter",
            "name": "status",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          }
        },
        "operationId": "get_field_service_marketing_workspace_module"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Saves tenant configuration for agent workflows, postcard campaigns, reviews, or advertising tracking. No external action is performed. Supplied provider state is discarded.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Create a marketing workspace draft",
        "parameters": [
          {
            "type": "string",
            "description": "Marketing module",
            "name": "module",
            "in": "path",
            "required": true
          },
          {
            "description": "Marketing configuration",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceMarketingInput"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_marketing_workspace_module"
      }
    },
    "/field_service/marketing/workspace/{module}/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Get a marketing workspace record",
        "parameters": [
          {
            "type": "string",
            "description": "Marketing module",
            "name": "module",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_marketing_workspace_module_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Expected version is mandatory. Provider outcome fields cannot be supplied by the caller; synchronized source data cannot be overwritten. A pending publication must be reconciled before editing.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Update marketing configuration",
        "parameters": [
          {
            "type": "string",
            "description": "Marketing module",
            "name": "module",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Configuration and expected version",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceMarketingInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "put_field_service_marketing_workspace_module_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Archive a marketing record",
        "parameters": [
          {
            "type": "string",
            "description": "Marketing module",
            "name": "module",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "integer",
            "description": "Expected record version",
            "name": "version",
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "delete_field_service_marketing_workspace_module_id"
      }
    },
    "/field_service/marketing/workspace/{module}/{id}/action": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Requires expected version. Provider actions fail closed when a verified tenant provider is unavailable. Email agent activate creates a linked scheduler workflow from exact saved content and scope; the action itself sends nothing, but consent-eligible email can start after one minute. Live audience, quiet hours, delay and bounded follow-up attempts are rechecked by the worker. Active agents must pause before editing. Draft generation, preview, duplicate, pause, and mark_reviewed do not send messages. mark_reviewed never means a response was published.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Apply a reviewed marketing action",
        "parameters": [
          {
            "type": "string",
            "description": "Marketing module",
            "name": "module",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Action and expected version",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceMarketingActionInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_marketing_workspace_module_id_action"
      }
    },
    "/field_service/marketing/workspace/{module}/{id}/export": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Exports only authenticated tenant data. Direct-mail export includes eligible postal recipients and does not place orders. Formula cells are escaped. Mail export records its timestamp with optimistic concurrency; use version query parameter.",
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Export a direct-mail recipient list or advertising report",
        "parameters": [
          {
            "type": "string",
            "description": "direct-mail or ads",
            "name": "module",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "integer",
            "description": "Expected version for mail export",
            "name": "version",
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "CSV",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "get_field_service_marketing_workspace_module_id_export"
      }
    },
    "/field_service/marketing/workspace/{module}/{id}/generate": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Streams status/delta/draft/done or error events with Gemini. Uses only the authenticated tenant record. Does not save content, send messages, publish responses, or book jobs.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Generate an editable AI marketing draft",
        "parameters": [
          {
            "type": "string",
            "description": "Marketing module",
            "name": "module",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Draft instructions",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceMarketingGenerateInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream with draft {text,requires_review}",
            "schema": {
              "type": "string"
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_marketing_workspace_module_id_generate"
      }
    },
    "/field_service/marketing/workspace/{module}/{id}/preview": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Evaluates live tenant customers, requests, estimates, completed jobs, property metadata, suppression and channel-specific consent. Nothing is sent. SMS and voice require custom_fields.marketing_sms_opt_in or marketing_voice_opt_in=true; generic email consent is not reused. Postal export requires a complete property address. Metadata filters exclude missing values. Lists exceeding 5,000 source records report truncated=true. Ads preview returns source-matched job attribution and does not claim causal revenue.",
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Preview live marketing audiences, agent opportunities, or ad attribution",
        "parameters": [
          {
            "type": "string",
            "description": "Marketing module",
            "name": "module",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceMarketingPreview"
            }
          }
        },
        "operationId": "post_field_service_marketing_workspace_module_id_preview"
      }
    },
    "/field_service/marketing/workspace/overview": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns actual persisted campaign counts, tenant review ratings and entered or synchronized advertising metrics. No demo KPIs. Connections mean configured, not proof of successful provider delivery. Scans are bounded and disclose truncated results.",
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Read marketing overview and provider readiness",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_marketing_workspace_overview"
      }
    },
    "/field_service/marketing/workspace/reputation/sync": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Starts an initial sync without creating a placeholder review. Emits SSE result with synced_count then done, or error. No replies are published.",
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Marketing workspace"
        ],
        "summary": "Import reviews from a connected Google Business Profile",
        "responses": {
          "200": {
            "description": "SSE status, result, done, error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_marketing_workspace_reputation_sync"
      }
    },
    "/field_service/marketing/real-estate/{path}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Delegates to the corresponding /real-estate/{path} operation with the same payload and response, but requires an active ServiceKeel seven-day trial or paid AI and Marketing (Core) subscription. All project, role and tenant checks remain in force. Free Forever receives 402.",
        "tags": [
          "Marketing API"
        ],
        "summary": "ServiceKeel marketing API",
        "parameters": [
          {
            "type": "string",
            "description": "Shared marketing operation, for example projects or brand-kits",
            "name": "path",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_marketing_real_estate_path"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Delegates to the corresponding /real-estate/{path} operation with the same payload and response, but requires an active ServiceKeel seven-day trial or paid AI and Marketing (Core) subscription. All project, role and tenant checks remain in force. Free Forever receives 402.",
        "tags": [
          "Marketing API"
        ],
        "summary": "ServiceKeel marketing API",
        "parameters": [
          {
            "type": "string",
            "description": "Shared marketing operation, for example projects or brand-kits",
            "name": "path",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "put_field_service_marketing_real_estate_path"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Delegates to the corresponding /real-estate/{path} operation with the same payload and response, but requires an active ServiceKeel seven-day trial or paid AI and Marketing (Core) subscription. All project, role and tenant checks remain in force. Free Forever receives 402.",
        "tags": [
          "Marketing API"
        ],
        "summary": "ServiceKeel marketing API",
        "parameters": [
          {
            "type": "string",
            "description": "Shared marketing operation, for example projects or brand-kits",
            "name": "path",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_marketing_real_estate_path"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Delegates to the corresponding /real-estate/{path} operation with the same payload and response, but requires an active ServiceKeel seven-day trial or paid AI and Marketing (Core) subscription. All project, role and tenant checks remain in force. Free Forever receives 402.",
        "tags": [
          "Marketing API"
        ],
        "summary": "ServiceKeel marketing API",
        "parameters": [
          {
            "type": "string",
            "description": "Shared marketing operation, for example projects or brand-kits",
            "name": "path",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "delete_field_service_marketing_real_estate_path"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Delegates to the corresponding /real-estate/{path} operation with the same payload and response, but requires an active ServiceKeel seven-day trial or paid AI and Marketing (Core) subscription. All project, role and tenant checks remain in force. Free Forever receives 402.",
        "tags": [
          "Marketing API"
        ],
        "summary": "ServiceKeel marketing API",
        "parameters": [
          {
            "type": "string",
            "description": "Shared marketing operation, for example projects or brand-kits",
            "name": "path",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "patch_field_service_marketing_real_estate_path"
      }
    },
    "/partner/oauth/authorize": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Requires a verified account owner/admin bearer session. Only authorization-code flow with S256 PKCE is accepted. Explicit customer consent freezes the requested registered Field Service read/write scopes. Draft/submitted apps may be tested only in the partner's own account. Display this response to the customer before POSTing their explicit decision.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner OAuth"
        ],
        "summary": "Prepare ServiceKeel partner app consent",
        "parameters": [
          {
            "type": "string",
            "description": "Partner app OAuth client ID",
            "name": "client_id",
            "in": "query",
            "required": true
          },
          {
            "type": "string",
            "description": "Must be code",
            "name": "response_type",
            "in": "query",
            "required": true
          },
          {
            "type": "string",
            "description": "Exact registered redirect URI",
            "name": "redirect_uri",
            "in": "query",
            "required": true
          },
          {
            "type": "string",
            "description": "Base64url SHA256 PKCE challenge",
            "name": "code_challenge",
            "in": "query",
            "required": true
          },
          {
            "type": "string",
            "description": "Must be S256",
            "name": "code_challenge_method",
            "in": "query",
            "required": true
          },
          {
            "type": "string",
            "description": "Opaque application CSRF state, returned unchanged",
            "name": "state",
            "in": "query",
            "required": true
          },
          {
            "type": "string",
            "description": "Space-delimited registered scopes; omitted defaults to the app scopes, explicit empty grants none",
            "name": "scope",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.PartnerOAuthConsentResponse"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_partner_oauth_authorize"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Requires explicit customer consent. The one-use consent token is bound to the authenticated tenant/user, exact redirect, state, and PKCE challenge. Navigate the browser to redirect_to after this call; denied requests receive error=access_denied at that URI.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner OAuth"
        ],
        "summary": "Approve or deny a prepared partner OAuth request",
        "parameters": [
          {
            "description": "Consent decision",
            "name": "request",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PartnerOAuthConsentDecision"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_oauth_authorize"
      }
    },
    "/partner/oauth/connections": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 200 unexpired, non-disconnected grant records. An app review/status/version change can invalidate a stored grant; authenticated token introspection is authoritative for its current activity. This list does not grant business API access.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner OAuth"
        ],
        "summary": "List this account's stored partner OAuth connections",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/models.PartnerOAuthGrant"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_partner_oauth_connections"
      }
    },
    "/partner/oauth/connections/{grantID}": {
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Partner OAuth"
        ],
        "summary": "Disconnect a partner app from this account",
        "parameters": [
          {
            "type": "string",
            "description": "Connection grant ID",
            "name": "grantID",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "delete_partner_oauth_connections_grantID"
      }
    },
    "/partner/oauth/introspect": {
      "post": {
        "description": "Authenticate the owning app with HTTP Basic or body credentials. Introspection rechecks the live app approval/version, current token hash and expiry, and consenting account owner/admin. Inactive, expired, rotated, revoked, and another app's tokens return only active=false. Returns the effective consented scope and no customer identity or business data.",
        "consumes": [
          "application/x-www-form-urlencoded"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner OAuth"
        ],
        "summary": "Check a partner OAuth token's current status",
        "parameters": [
          {
            "type": "string",
            "description": "OAuth client ID when not using HTTP Basic",
            "name": "client_id",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "Client secret when not using HTTP Basic",
            "name": "client_secret",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "Access or refresh token",
            "name": "token",
            "in": "formData",
            "required": true
          },
          {
            "type": "string",
            "description": "access_token or refresh_token",
            "name": "token_type_hint",
            "in": "formData"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.PartnerOAuthIntrospectionResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_oauth_introspect"
      }
    },
    "/partner/oauth/revoke": {
      "post": {
        "description": "Client authentication is required. Revokes both access and refresh tokens in the token's grant. Unknown tokens return success without disclosing token validity.",
        "consumes": [
          "application/x-www-form-urlencoded"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner OAuth"
        ],
        "summary": "Revoke a partner OAuth grant",
        "parameters": [
          {
            "type": "string",
            "description": "OAuth client ID when not using HTTP Basic",
            "name": "client_id",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "Client secret when not using HTTP Basic",
            "name": "client_secret",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "Access or refresh token",
            "name": "token",
            "in": "formData",
            "required": true
          },
          {
            "type": "string",
            "description": "access_token or refresh_token",
            "name": "token_type_hint",
            "in": "formData"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_oauth_revoke"
      }
    },
    "/partner/oauth/token": {
      "post": {
        "description": "Authenticate with HTTP Basic (preferred) or body client_id/client_secret. Authorization codes require their original redirect_uri and S256 code_verifier. Refresh tokens rotate on every use; replay revokes the entire grant. Credentials are scoped to the consenting tenant. Tokens carry only explicitly consented scopes. Refresh may narrow effective scopes but cannot expand them; added app permissions require fresh customer consent.",
        "consumes": [
          "application/x-www-form-urlencoded"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner OAuth"
        ],
        "summary": "Exchange a partner authorization code or rotate a refresh token",
        "parameters": [
          {
            "type": "string",
            "description": "authorization_code or refresh_token",
            "name": "grant_type",
            "in": "formData",
            "required": true
          },
          {
            "type": "string",
            "description": "OAuth client ID when not using HTTP Basic",
            "name": "client_id",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "Client secret when not using HTTP Basic",
            "name": "client_secret",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "One-use authorization code",
            "name": "code",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "Exact original redirect URI for code exchange",
            "name": "redirect_uri",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "Original PKCE verifier",
            "name": "code_verifier",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "Current refresh token",
            "name": "refresh_token",
            "in": "formData"
          },
          {
            "type": "string",
            "description": "Refresh-only downscope; omitted preserves current permissions, explicit empty removes them",
            "name": "scope",
            "in": "formData"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.PartnerOAuthTokenResponse"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_oauth_token"
      }
    },
    "/partner/apps": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "List your partner apps",
        "parameters": [
          {
            "type": "integer",
            "description": "Page size, 1–100",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Offset, 0–10000",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_partner_apps"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "Create a partner app and credentials",
        "parameters": [
          {
            "description": "App details and selected partner API scopes",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PartnerAppRequest"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/gin.PartnerAppCredentialsResponse"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_apps"
      }
    },
    "/partner/apps/{app_id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "Get your partner app",
        "parameters": [
          {
            "type": "string",
            "description": "App ID",
            "name": "app_id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PartnerApp"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_partner_apps_app_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Full replacement of editable fields; omitted test_credentials preserves the encrypted test account. Withdraw submitted/approved apps before editing. Requires the last fetched revision.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "Replace editable app details",
        "parameters": [
          {
            "type": "string",
            "description": "App ID",
            "name": "app_id",
            "in": "path",
            "required": true
          },
          {
            "description": "App details and revision",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PartnerAppRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PartnerApp"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "put_partner_apps_app_id"
      }
    },
    "/partner/apps/{app_id}/client-secret": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Verified tenant owners/admins only. Decrypts the current secret within the authenticated tenant. Legacy hash-only secrets require explicit rotation. Responses must not be cached.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "Reveal a partner app client secret",
        "parameters": [
          {
            "type": "string",
            "description": "App ID",
            "name": "app_id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_apps_app_id_client_secret"
      }
    },
    "/partner/apps/{app_id}/logo/upload-url": {
      "post": {
        "description": "Owner/admin only. Creates a 15-minute, create-only upload URL for a PNG/JPEG logo up to 5 MiB. PUT the exact size using upload_headers, then save logo_url with the app update endpoint. Submitted and approved apps must be withdrawn first. Each issued URL consumes the shared daily logo upload budget.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "Create a partner app logo upload URL",
        "parameters": [
          {
            "type": "string",
            "description": "Partner app ID",
            "name": "app_id",
            "in": "path",
            "required": true
          },
          {
            "description": "Logo upload parameters",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PartnerLogoUploadRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.PartnerLogoUploadResponse"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_apps_app_id_logo_upload_url"
      }
    },
    "/partner/apps/{app_id}/rotate-secret": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Shows the replacement secret once and invalidates all existing OAuth grants.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "Rotate app client secret",
        "parameters": [
          {
            "type": "string",
            "description": "App ID",
            "name": "app_id",
            "in": "path",
            "required": true
          },
          {
            "description": "Current revision",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PartnerRevisionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.PartnerAppCredentialsResponse"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_apps_app_id_rotate_secret"
      }
    },
    "/partner/apps/{app_id}/submit": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Requires review URLs, ServiceKeel reference confirmation and test account. Persists the review queue before notifying support. A submitted app may retry notification with its current revision.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "Submit app for review or retry its failed notification",
        "parameters": [
          {
            "type": "string",
            "description": "App ID",
            "name": "app_id",
            "in": "path",
            "required": true
          },
          {
            "description": "Current revision",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PartnerRevisionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_apps_app_id_submit"
      }
    },
    "/partner/apps/{app_id}/withdraw": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns the app to draft and invalidates existing OAuth grants. Edit and submit again for a fresh review.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "Withdraw or unpublish your app",
        "parameters": [
          {
            "type": "string",
            "description": "App ID",
            "name": "app_id",
            "in": "path",
            "required": true
          },
          {
            "description": "Current revision",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PartnerRevisionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PartnerApp"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_apps_app_id_withdraw"
      }
    },
    "/partner/integrations": {
      "get": {
        "description": "Public, deliberately published metadata only. Excludes applicant identity, tenant IDs, test credentials and OAuth secrets. Combine this feed with first-party integrations in the frontend.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "List approved partner integration tiles",
        "parameters": [
          {
            "type": "integer",
            "description": "Page size, 1–100",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Offset",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_partner_integrations"
      }
    },
    "/partner/scopes": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns the explicitly selectable Field Service read and write scopes. Scopes require customer consent and remain limited to the approving tenant.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner apps"
        ],
        "summary": "List available partner API scopes",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_partner_scopes"
      }
    },
    "/partner/profile": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner account"
        ],
        "summary": "Get partner applicant profile",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PartnerProfile"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_partner_profile"
      }
    },
    "/partner/signup": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "After shared account signup/login, a verified owner/admin provides applicant details. No subscription is required. Email must match the authenticated applicant. Enrollment is idempotent per tenant.\nThe company website may be a public domain or HTTPS URL; omitted schemes are saved with https://.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Partner account"
        ],
        "summary": "Enroll as a free ServiceKeel partner",
        "parameters": [
          {
            "description": "Applicant information",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.PartnerSignupRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.PartnerProfile"
            }
          },
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.PartnerProfile"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_partner_signup"
      }
    },
    "/field_service/oauth/integrations/exchange": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Routes phone or general integration OAuth using cryptographically verified state. Phone connections require owner/admin Core access and a single-use tenant/user/provider/callback-bound state. Returns the safe ServiceKeel integrations destination.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Integration connections"
        ],
        "summary": "Complete a ServiceKeel integration connection",
        "parameters": [
          {
            "description": "OAuth authorization code and signed state",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.IntegrationOAuthExchangeRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "502": {
            "description": "Bad Gateway",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_oauth_integrations_exchange"
      }
    },
    "/field_service/events": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Cursor feed of changes committed after event capture was enabled. Contains identifiers only. Owners/admins only.",
        "tags": [
          "Events"
        ],
        "summary": "Read tenant record change events",
        "parameters": [
          {
            "type": "string",
            "description": "Event type",
            "name": "event_type",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "record_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "RFC3339 start time",
            "name": "after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Continuation cursor",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_events"
      }
    },
    "/field_service/webhooks": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "List tenant event subscriptions",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          }
        },
        "operationId": "get_field_service_webhooks"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns the signing secret once. Events use at-least-once delivery; deduplicate the event ID. OAuth requires field_service:read and field_service:write and binds delivery to the current app authorization. Portal users retain normal role-based access.",
        "consumes": [
          "application/json"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Subscribe to tenant record events",
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_webhooks"
      }
    },
    "/field_service/webhooks/{id}": {
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Revoke a tenant event subscription",
        "responses": {
          "204": {
            "description": "No Content"
          }
        },
        "operationId": "delete_field_service_webhooks_id"
      }
    },
    "/field_service/webhooks/{id}/retry": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Resume a failed event subscription from its last confirmed event",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "post_field_service_webhooks_id_retry"
      }
    },
    "/field_service/developer/webhook_events": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Developer webhooks"
        ],
        "summary": "Inspect durable request-created webhook delivery progress",
        "parameters": [
          {
            "type": "string",
            "description": "Existing shared OAuth application ID",
            "name": "app_id",
            "in": "query",
            "required": true
          },
          {
            "type": "integer",
            "description": "Page offset (200 events per page; max10000)",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_developer_webhook_events"
      }
    },
    "/field_service/developer/webhook_events/{id}/retry": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Reuses the original event ID for receiver deduplication. Succeeded/revoked deliveries are never resent. Retry is available for seven days after the original operation.",
        "tags": [
          "Developer webhooks"
        ],
        "summary": "Explicitly retry failed request-created webhook deliveries",
        "parameters": [
          {
            "type": "string",
            "description": "Event ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceWebhookEvent"
            }
          }
        },
        "operationId": "post_field_service_developer_webhook_events_id_retry"
      }
    },
    "/field_service/developer/webhooks": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Only request.created events produced by the app's GraphQL createRequest mutation are supported. Signing secrets are never returned by listing.",
        "tags": [
          "Developer webhooks"
        ],
        "summary": "List app-owned request-created webhook subscriptions",
        "parameters": [
          {
            "type": "string",
            "description": "Existing shared OAuth application ID",
            "name": "app_id",
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          }
        },
        "operationId": "get_field_service_developer_webhooks"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owner/admin only. Public HTTPS destinations on port443; DNS/IP rechecked at connection time and redirects denied. Returns signing_secret once. Receivers must deduplicate X-SiteStack-Event-ID and verify v1 HMAC-SHA256 over timestamp + dot + raw body, allowing at most five minutes clock skew. Delivery is at least once with five bounded attempts; only the creating OAuth app's GraphQL requests trigger this event.",
        "consumes": [
          "application/json"
        ],
        "tags": [
          "Developer webhooks"
        ],
        "summary": "Create an app-owned signed request-created webhook",
        "parameters": [
          {
            "description": "Destination",
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceWebhookInput"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_developer_webhooks"
      }
    },
    "/field_service/developer/webhooks/{id}": {
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "tags": [
          "Developer webhooks"
        ],
        "summary": "Revoke a Field Service webhook subscription",
        "parameters": [
          {
            "type": "string",
            "description": "Subscription ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          }
        },
        "operationId": "delete_field_service_developer_webhooks_id"
      }
    },
    "/field_service/developer/webhooks/{id}/rotate": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Old signatures become invalid for subsequent deliveries. Returns the new signing_secret once.",
        "tags": [
          "Developer webhooks"
        ],
        "summary": "Rotate a webhook delivery signing secret",
        "parameters": [
          {
            "type": "string",
            "description": "Subscription ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_developer_webhooks_id_rotate"
      }
    },
    "/field_service/quickbooks/export": {
      "post": {
        "description": "Writes exactly the saved preview after revalidating local data and remote references. Persists a claim before POST and uses stable provider requestid. Ambiguous writes require explicit remote reconciliation, never a blind retry. Does not email, charge, or submit payroll.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "QuickBooks"
        ],
        "summary": "Confirm a reviewed QuickBooks export",
        "parameters": [
          {
            "description": "preview_id",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE exported or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_quickbooks_export"
      }
    },
    "/field_service/quickbooks/exports": {
      "get": {
        "description": "Tenant owner/admin only. Results contain reviewed provider payloads and external IDs, never credentials. Bounded to 200 recent exports.",
        "tags": [
          "QuickBooks"
        ],
        "summary": "List QuickBooks export previews and mappings",
        "parameters": [
          {
            "type": "string",
            "description": "Shared integration ID",
            "name": "integration_id",
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_quickbooks_exports"
      }
    },
    "/field_service/quickbooks/options": {
      "post": {
        "description": "Streams current accounts and tax configuration from the tenant's connected company. Initial export supports US companies and USD. No remote changes.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "QuickBooks"
        ],
        "summary": "Read QuickBooks accounting options",
        "parameters": [
          {
            "description": "integration_id",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE options or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_quickbooks_options"
      }
    },
    "/field_service/quickbooks/preview": {
      "post": {
        "description": "Builds the provider payload from tenant records and existing verified mappings. Does not write QuickBooks. US/USD, simple single-rate tax, no discounts. Dependency exports are customer, catalog, invoice, then invoice payment. Never imports or overwrites remote data.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "QuickBooks"
        ],
        "summary": "Preview a reviewed one-way QuickBooks export",
        "parameters": [
          {
            "description": "Source and reviewed accounting references",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceQuickBooksPreviewRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE preview or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_quickbooks_preview"
      }
    },
    "/field_service/quickbooks/reconcile": {
      "post": {
        "description": "Reads an explicitly confirmed remote ID from the connected company and verifies it matches the immutable reviewed payload before saving its mapping. Never writes QuickBooks.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "QuickBooks"
        ],
        "summary": "Reconcile an interrupted QuickBooks export",
        "parameters": [
          {
            "description": "preview_id, remote_id, confirmed:true",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE exported or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_quickbooks_reconcile"
      }
    },
    "/field_service/gusto/export": {
      "post": {
        "description": "Requires reviewed employee/job mapping, timezone, payroll period and regular/overtime classifications. Durable per-shift export journal prevents duplicate writes. Ambiguous submissions reconcile by provider metadata; they are never blindly resent. Does not run or submit payroll.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Gusto"
        ],
        "summary": "Export one approved shift to Gusto Time Tracking",
        "parameters": [
          {
            "description": "Reviewed shift export",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceGustoExportRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE export or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_gusto_export"
      }
    },
    "/field_service/gusto/mapping": {
      "post": {
        "description": "Requires manual confirmation of matching name and email. Validates employee/job in the tenant's authorized company. No employee creation.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Gusto"
        ],
        "summary": "Map a shared team member to a Gusto employee and job",
        "parameters": [
          {
            "description": "integration_id, user_id, employee_uuid, job_uuid, confirm_name, confirm_email, version",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE mapping or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_gusto_mapping"
      }
    },
    "/field_service/gusto/payrolls": {
      "post": {
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Gusto"
        ],
        "summary": "List unprocessed Gusto payroll periods for reviewed reimbursement exports",
        "parameters": [
          {
            "description": "integration_id and page",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE payrolls or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_gusto_payrolls"
      }
    },
    "/field_service/gusto/records": {
      "get": {
        "description": "Existing platform managers only. Uses tenant-owned records and exposes no credentials.",
        "tags": [
          "Gusto"
        ],
        "summary": "Read payroll mappings and export history",
        "parameters": [
          {
            "type": "string",
            "description": "Gusto integration ID",
            "name": "integration_id",
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_gusto_records"
      }
    },
    "/field_service/gusto/reimbursements/export": {
      "post": {
        "description": "Freezes the approved expense, revalidates provider version and existing reimbursements, then updates only the mapped employee's reimbursements once. Ambiguous submissions are reconciled using the unique expense marker and never repeated. Payroll processing remains in Gusto.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Gusto"
        ],
        "summary": "Export exactly a reviewed Gusto reimbursement preview",
        "parameters": [
          {
            "description": "Confirmed preview and current version",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fsGustoExpenseExportRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE export or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_gusto_reimbursements_export"
      }
    },
    "/field_service/gusto/reimbursements/preview": {
      "post": {
        "description": "Explicitly prepares an unprocessed regular payroll to obtain versioned employee data. Does not change amounts or submit payroll. Preserves existing named nonrecurring reimbursements; recurring or unnamed reimbursements are rejected.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Gusto"
        ],
        "summary": "Prepare a Gusto reimbursement export for review",
        "parameters": [
          {
            "description": "Reviewed expense, payroll and prepare confirmation",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fsGustoExpenseRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE preview or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_gusto_reimbursements_preview"
      }
    },
    "/field_service/gusto/roster": {
      "post": {
        "description": "Streams provider data for one page, max 100 employees. Does not create platform users.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Gusto"
        ],
        "summary": "Fetch Gusto employee roster for explicit mapping",
        "parameters": [
          {
            "description": "integration_id and page",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE roster or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_gusto_roster"
      }
    },
    "/field_service/booking_forms": {
      "get": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Booking forms"
        ],
        "summary": "List booking and request forms",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          }
        },
        "operationId": "get_field_service_booking_forms"
      },
      "post": {
        "description": "Saves authenticated actor, timestamp and changed fields in activity history.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Booking forms"
        ],
        "summary": "Create an intake or online booking form",
        "parameters": [
          {
            "description": "Form with booking configuration",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "post_field_service_booking_forms"
      }
    },
    "/field_service/booking_forms/{id}": {
      "put": {
        "description": "Saves authenticated actor, timestamp and changed fields in activity history.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Booking forms"
        ],
        "summary": "Create an intake or online booking form",
        "parameters": [
          {
            "description": "Form with booking configuration",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        },
        "operationId": "put_field_service_booking_forms_id"
      }
    },
    "/field_service/booking_forms/{id}/publish": {
      "post": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Booking forms"
        ],
        "summary": "Publish a revocable booking form link",
        "parameters": [
          {
            "type": "string",
            "description": "Form ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_booking_forms_id_publish"
      }
    },
    "/field_service/public/booking/{token}": {
      "get": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Public booking"
        ],
        "summary": "Read a published service form and available appointment times",
        "parameters": [
          {
            "type": "string",
            "description": "Published form capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Local date YYYY-MM-DD",
            "name": "date",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Bookable service ID",
            "name": "service_id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_public_booking_token"
      },
      "post": {
        "description": "A signed form capability fixes tenant scope. Confirmed visits are rechecked under a tenant scheduling lease. A caller request_key prevents duplicate submissions.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Public booking"
        ],
        "summary": "Submit a request or availability-checked booking",
        "parameters": [
          {
            "type": "string",
            "description": "Published form capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "description": "Customer and requested appointment",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceBookingSubmission"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_public_booking_token"
      }
    },
    "/field_service/public/hub/{token}": {
      "get": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Read authorized customer portal records",
        "parameters": [
          {
            "type": "string",
            "description": "Revocable customer capability",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_public_hub_token"
      }
    },
    "/field_service/public/hub/{token}/autopay/{job_id}": {
      "post": {
        "description": "Full customer-hub capability only. Core and explicit per-job consent required for enable. Fixed schedule and exact maximum charge are fingerprinted; changes invalidate consent. Applies only to subsequently issued, due recurring invoices. Revocation does not cancel already initiated payments.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Review, authorize or revoke recurring customer payments",
        "parameters": [
          {
            "type": "string",
            "description": "Full customer-hub capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Recurring job ID",
            "name": "job_id",
            "in": "path",
            "required": true
          },
          {
            "description": "review, enable or disable and explicit reviewed consent",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceAutopayRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE autopay, done or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_public_hub_token_autopay_job_id"
      }
    },
    "/field_service/public/hub/{token}/checkout/estimates/{id}": {
      "post": {
        "description": "Reuses the scoped customer hub capability; cannot select another tenant or customer. Amounts, currency, account, and document state are verified on the server. Redirects do not confirm payment.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Pay an authorized customer invoice or estimate deposit",
        "parameters": [
          {
            "type": "string",
            "description": "Customer hub capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_public_hub_token_checkout_estimates_id"
      }
    },
    "/field_service/public/hub/{token}/checkout/invoices/{id}": {
      "post": {
        "description": "Reuses the scoped customer hub capability; cannot select another tenant or customer. Amounts, currency, account, and document state are verified on the server. Redirects do not confirm payment.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Pay an authorized customer invoice or estimate deposit",
        "parameters": [
          {
            "type": "string",
            "description": "Customer hub capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_public_hub_token_checkout_invoices_id"
      }
    },
    "/field_service/public/hub/{token}/estimates/{id}/response": {
      "post": {
        "description": "Approval is bound to the current estimate version and records the customer's typed signature.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Respond to a sent estimate",
        "parameters": [
          {
            "type": "string",
            "description": "Customer capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Estimate ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Approval or requested changes",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServicePortalEstimateInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_public_hub_token_estimates_id_response"
      }
    },
    "/field_service/public/hub/{token}/media": {
      "get": {
        "description": "Returns attachment metadata only. Internal notes, asset storage keys, and unshared files are never returned.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "List files explicitly shared with the authorized customer",
        "parameters": [
          {
            "type": "string",
            "description": "Revocable customer capability",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_public_hub_token_media"
      }
    },
    "/field_service/public/hub/{token}/media/{kind}/{id}/{asset_id}": {
      "get": {
        "description": "Revalidates the customer, source record, shared flag, and tenant-owned asset on every request before using the existing private storage signer.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Open a file explicitly shared with the authorized customer",
        "parameters": [
          {
            "type": "string",
            "description": "Revocable customer capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Source kind",
            "name": "kind",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Source ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Asset ID",
            "name": "asset_id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_public_hub_token_media_kind_id_asset_id"
      }
    },
    "/field_service/public/hub/{token}/referral_link": {
      "post": {
        "description": "The existing hub capability authorizes the referrer. The separate referral capability only permits new referral intake and expires in 90 days. It never grants hub access or changes shared account identity.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Create or rotate the customer's referral invitation",
        "parameters": [
          {
            "type": "string",
            "description": "Customer hub capability",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_public_hub_token_referral_link"
      },
      "delete": {
        "tags": [
          "Customer portal"
        ],
        "summary": "Revoke the customer's referral invitation",
        "parameters": [
          {
            "type": "string",
            "description": "Customer hub capability",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          }
        },
        "operationId": "delete_field_service_public_hub_token_referral_link"
      }
    },
    "/field_service/public/hub/{token}/requests": {
      "post": {
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Request service from the customer portal",
        "parameters": [
          {
            "type": "string",
            "description": "Customer capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "description": "Request details",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServicePortalRequestInput"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_public_hub_token_requests"
      }
    },
    "/field_service/public/hub/{token}/wallet": {
      "post": {
        "description": "Customer capability authorizes only its own wallet. Streams masked methods and explicit consent terms; never exposes card numbers or bank details.",
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "View a customer hub wallet",
        "parameters": [
          {
            "type": "string",
            "description": "Customer hub capability",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "SSE wallet or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_public_hub_token_wallet"
      }
    },
    "/field_service/public/hub/{token}/wallet/remove": {
      "post": {
        "description": "Customer hub only. Records revocation before attempting Stripe detach. Repeating after an interrupted detach is safe. Pending payments are unaffected.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Revoke consent and remove a saved payment method",
        "parameters": [
          {
            "type": "string",
            "description": "Customer hub capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "description": "payment_method_id",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE removed or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_public_hub_token_wallet_remove"
      }
    },
    "/field_service/public/hub/{token}/wallet/setup": {
      "post": {
        "description": "Customer hub only. Records consent text, version, timestamp and capability digest before creating a connected-account Stripe Checkout setup session. Stripe collects sensitive payment data and bank mandates. This does not authorize an automatic recurring schedule.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Customer portal"
        ],
        "summary": "Save a payment method with explicit customer consent",
        "parameters": [
          {
            "type": "string",
            "description": "Customer hub capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "description": "consent:true, consent_version, method_type",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE checkout or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_public_hub_token_wallet_setup"
      }
    },
    "/field_service/public/calendar/{token}": {
      "get": {
        "description": "One-way iCalendar feed, 30 days of history and 180 days ahead. The signed capability is tied to an active shared tenant member and rechecks current role and product access. Returns up to 1000 visits; larger feeds require a narrower assigned-user scope.",
        "produces": [
          "text/calendar"
        ],
        "tags": [
          "Public calendar"
        ],
        "summary": "Read a revocable personal Field Service calendar",
        "parameters": [
          {
            "type": "string",
            "description": "Revocable calendar capability",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "iCalendar feed",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "413": {
            "description": "Request Entity Too Large",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_public_calendar_token"
      }
    },
    "/field_service/public/referral/{token}": {
      "get": {
        "produces": [
          "application/json"
        ],
        "tags": [
          "Public referrals"
        ],
        "summary": "View a customer referral invitation",
        "parameters": [
          {
            "type": "string",
            "description": "Referral capability",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_public_referral_token"
      },
      "post": {
        "description": "Creates domain customer and request records, never a platform user or tenant. A stable 16-100 character idempotency key resumes interrupted writes. Referrer and tenant come exclusively from the signed referral capability.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Public referrals"
        ],
        "summary": "Submit an attributed service request through a referral invitation",
        "parameters": [
          {
            "type": "string",
            "description": "Referral capability",
            "name": "token",
            "in": "path",
            "required": true
          },
          {
            "description": "Contact and requested service",
            "name": "request",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceReferralSubmission"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_public_referral_token"
      }
    },
    "/field_service/public/unsubscribe/{token}": {
      "get": {
        "produces": [
          "text/html"
        ],
        "tags": [
          "Unsubscribe"
        ],
        "summary": "Display email unsubscribe confirmation",
        "parameters": [
          {
            "type": "string",
            "description": "Signed customer capability",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "get_field_service_public_unsubscribe_token"
      },
      "post": {
        "produces": [
          "text/html"
        ],
        "tags": [
          "Unsubscribe"
        ],
        "summary": "Unsubscribe a customer from tenant marketing emails",
        "parameters": [
          {
            "type": "string",
            "description": "Signed customer capability",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_public_unsubscribe_token"
      }
    },
    "/field_service/public/website/{token}": {
      "get": {
        "description": "Tenant scope is derived from a validated signed publication token. Returns only the explicitly published website snapshot.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Public website"
        ],
        "summary": "Read a published Field Service business website",
        "parameters": [
          {
            "type": "string",
            "description": "Publication token",
            "name": "token",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_public_website_token"
      }
    },
    "/field_service/assistant/cancel": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Support impersonation can only list and read the viewed user's existing active and archived client chats, under the same tenant, user and role checks. Support-session chats are excluded. The contracts response includes read_only. All assistant POST endpoints are unavailable (403); viewing history never saves a chat or invokes the model.\nInvalidates pending choices/review; completed business changes and activity history remain. An operation already running cannot be recalled.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "Cancel the pending Field Service assistant task",
        "parameters": [
          {
            "description": "Conversation and current version",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceAssistantRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceAssistantConversation"
            }
          }
        },
        "operationId": "post_field_service_assistant_cancel"
      }
    },
    "/field_service/assistant/confirm": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Support impersonation can only list and read the viewed user's existing active and archived client chats, under the same tenant, user and role checks. Support-session chats are excluded. The contracts response includes read_only. All assistant POST endpoints are unavailable (403); viewing history never saves a chat or invokes the model.\nHuman review required. The approval ID expires and is bound to the current conversation, command, record versions and recipient/dependencies. Replayed/stale approval never authorizes a changed action.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "Confirm an exact reviewed Field Service assistant operation",
        "parameters": [
          {
            "description": "Conversation/version/request ID and server-issued approval ID",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceAssistantRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_assistant_confirm"
      }
    },
    "/field_service/assistant/contracts": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Support impersonation can only list and read the viewed user's existing active and archived client chats, under the same tenant, user and role checks. Support-session chats are excluded. The contracts response includes read_only. All assistant POST endpoints are unavailable (403); viewing history never saves a chat or invokes the model.\nCore only. Versioned resource/action definitions use existing tenant/account authorization. Field Service domain mutations are staged for human review before execution; marketing reel drafts save conversationally, generation credits require exact approval, and publishing stays in the project UI.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "Discover Field Service assistant contracts",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_assistant_contracts"
      }
    },
    "/field_service/assistant/conversations": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Support impersonation can only list and read the viewed user's existing active and archived client chats, under the same tenant, user and role checks. Support-session chats are excluded. The contracts response includes read_only. All assistant POST endpoints are unavailable (403); viewing history never saves a chat or invokes the model.\nPaginated active or archived conversations (default 20, maximum 50), scoped to the authenticated shared tenant and user. Conversations created under a different role are omitted. Returns compact sidebar metadata and the first two messages; use the conversation GET endpoint for full history and pending work.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "List your recent Field Service assistant conversations",
        "parameters": [
          {
            "type": "boolean",
            "description": "List archived conversations instead of active conversations",
            "name": "archived",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Zero-based page offset (maximum 100000)",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Page size (default 20, maximum 50)",
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_assistant_conversations"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Support impersonation can only list and read the viewed user's existing active and archived client chats, under the same tenant, user and role checks. Support-session chats are excluded. The contracts response includes read_only. All assistant POST endpoints are unavailable (403); viewing history never saves a chat or invokes the model.\nShared tenant and user; conversations and archived drafts persist until shared account deletion. No business record changes.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "Start a private Field Service assistant conversation",
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceAssistantConversation"
            }
          }
        },
        "operationId": "post_field_service_assistant_conversations"
      }
    },
    "/field_service/assistant/conversations/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Support impersonation can only list and read the viewed user's existing active and archived client chats, under the same tenant, user and role checks. Support-session chats are excluded. The contracts response includes read_only. All assistant POST endpoints are unavailable (403); viewing history never saves a chat or invokes the model.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "Resume your private Field Service assistant conversation",
        "parameters": [
          {
            "type": "string",
            "description": "Conversation ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceAssistantConversation"
            }
          }
        },
        "operationId": "get_field_service_assistant_conversations_id"
      }
    },
    "/field_service/assistant/conversations/{id}/archive": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Support impersonation can only list and read the viewed user's existing active and archived client chats, under the same tenant, user and role checks. Support-session chats are excluded. The contracts response includes read_only. All assistant POST endpoints are unavailable (403); viewing history never saves a chat or invokes the model.\nUses the authenticated shared tenant and user with a conversation version check. Busy conversations cannot be archived. Archiving preserves conversation history and unsaved drafts; an archived conversation cannot execute actions until restored. History and drafts are retained after archiving.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "Archive or restore a private Field Service assistant conversation",
        "parameters": [
          {
            "type": "string",
            "description": "Conversation ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Current version and archived true or false",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceAssistantArchiveRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceAssistantConversation"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_assistant_conversations_id_archive"
      }
    },
    "/field_service/assistant/message": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Support impersonation can only list and read the viewed user's existing active and archived client chats, under the same tenant, user and role checks. Support-session chats are excluded. The contracts response includes read_only. All assistant POST endpoints are unavailable (403); viewing history never saves a chat or invokes the model.\nOn the first user message in an empty or welcome-only chat, Create a customer or Create a new customer replies Please provide me with the customer details; Create an estimate replies Which customer should I create an estimate for?; Schedule a job replies Which customer should I create a job for?; Send an invoice replies Would you like to create a new invoice, or look for an existing invoice to send? These plain-message shortcuts ignore casing and surrounding whitespace, skip model analysis, and reply after a simulated one-second delay. All other messages use the normal flow.\nWhen sending finds no matching invoice or known job/estimate source, the assistant offers to create a new invoice or search again. Accepting creation retains the send request and its delivery parameters, collects missing invoice details, and requires separate approvals to create and send. Existing customer matches are tenant-scoped; ambiguous customers require selection.\nInformation questions use contract-declared read capabilities for tenant/role-scoped searches, exact counts and numeric totals. Chat lists contain at most 10 records with links and resumable pagination; counts/totals aggregate the entire matching set. Named filters define business terms such as outstanding invoices. Information replies preserve the current task, draft and pending approval, mark their message answer_type=information, and do not navigate or mutate resources. Explicit open-page requests navigate separately. Unsupported filters/metrics require clarification instead of fabricated results.\nEdits to declared linked records preserve the current task and remaining workflow. The assistant prepares the linked record update using the supplied values, requires its own review, and resumes the original task with fresh validation afterward. Invoice/estimate recipient email belongs to the linked customer; changing it refreshes the send review and never sends automatically.\nrefresh_review=true with the current pending_id renews a resource review without invoking a model or executing the action. It retains the draft, reloads authorized dependencies, reports changed field labels, and requires a new explicit confirmation. Failed renewal preserves the pending draft with refresh_error.\nMarketing reels reuse shared projects, brand kits and durable photo-grounded narration/render jobs. Image choices require current pending_id, edits.asset_ids and explicit edits.rights_confirmed=true. Poll script/render stages with edits.refresh=true. Credit-consuming renders require an unexpired marketing_review: reply exactly Generate this reel with its current pending_id and conversation version, or explicitly confirm its approval_id through /confirm. Generic acknowledgements and model output never approve generation; publishing is never performed by the assistant.\nStreams status, assistant_state, resource_changed, assistant_navigation and done events. Explicit open-page requests emit assistant_navigation using resource contract routes and preserve the pending task. Persists up to eight contract-validated workflow steps with typed result bindings and separately collected future drafts. Confirming each prerequisite resumes the next step and requires its own approval; explicit cancellation discards only unfinished work. Missing details and ambiguous records are clarified through chat; plain replies select only currently offered choices, and a missing customer can be created before resuming the original task. Shared Core auth required. Saved conversation references are reloaded within the tenant; ambiguous references require a choice. Selected estimates supply invoice customer, priced items, service address and provenance; existing invoices are reused and job billing retains allocation checks. Ineffective revisions preserve the current draft. Field Service domain mutations return a preflight review and form proposal without business writes. Marketing drafts are saved as part of their explicit conversational workflow. Full chat history is preserved; new message turns return conversation_full at 500 messages or approximately 3 MiB. Confirm/cancel and history/archive access remain available. Model output cannot approve operations. Field Service domain mutations use the separate version-bound confirm endpoint after human review; marketing render reviews additionally accept the exact bound confirmation phrase described above. Linked steps inherit unambiguous resolved relationship context through resource contracts, and every planned step can bind tenant-verified saved conversation references. Explicit different records and ambiguous associations still require lookup/clarification.\nExplicit new tasks pause the active workflow. Up to eight unfinished steps persist across reloads; suspended contains compact IDs and labels. Say resume, name a paused task, or send resume/ID to return to it with a fresh review. Declared missing prerequisites bind tenant-verified saved result IDs after approval; unrelated work does not bind or automatically resume. Cancel affects the active workflow, not other paused work or queued generation.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "Converse with the contract-driven Field Service assistant",
        "parameters": [
          {
            "description": "Message, selected choice, or edited draft; current conversation version and stable request ID",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceAssistantRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream",
            "schema": {
              "type": "string"
            }
          },
          "409": {
            "description": "Conversation changed, busy, archived, or full",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_assistant_message"
      }
    },
    "/field_service/assistant/voice/message": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Unavailable during support impersonation (403). Support can only view saved client chat history.\nSame authentication, versioning, idempotency and SSE results as assistant/message. Accepts only a plain message and optional page context. Rejects approval IDs, choices, edits and review refreshes. Voice never confirms actions or approves marketing generation; use the existing on-screen exact review controls.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "Delegate a spoken request to the existing Field Service assistant",
        "parameters": [
          {
            "description": "conversation_id, version, request_id, message, optional context",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceAssistantRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_assistant_voice_message"
      }
    },
    "/field_service/assistant/voice/session": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Unavailable during support impersonation (403). Support can only view saved client chat history.\nShared tenant/user, paid assistant access and matching conversation role/version required. Streams status, voice_session (sdp and conversation_id), done or error. Uses existing server-side OpenAI credentials and fixed scope instructions; no saved history, business records or internal capability metadata is included in setup. Audio flows over WebRTC directly between the browser and OpenAI. No business mutation or approval is performed.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "Assistant"
        ],
        "summary": "Start a Field Service assistant voice conversation",
        "parameters": [
          {
            "description": "Current conversation/version and WebRTC SDP offer",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceVoiceSessionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream",
            "schema": {
              "type": "string"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_assistant_voice_session"
      }
    },
    "/field_service/ai/draft": {
      "post": {
        "description": "Field Service Core only. Uses tenant-authorized customer, record and catalog context, returns validated draft with server-computed totals and clarification questions. Never saves, schedules, sends or charges automatically.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "AI tools"
        ],
        "summary": "Prepare an editable Field Service record draft from reviewed text",
        "parameters": [
          {
            "description": "Reviewed transcript and target kind",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceDraftPayload"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE status, delta, draft, done or error events",
            "schema": {
              "type": "string"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_ai_draft"
      }
    },
    "/field_service/ai/expenses/extract": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owner/admin and Core only. Processes one document per bounded stream so a batch of up to25 can resume sequentially. Reuses Gemini with the exact tenant-owned PDF; its content is untrusted input. Emits expense_draft without creating or approving an expense. Repeated extraction of the same completed import returns its persisted result.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "AI tools"
        ],
        "summary": "Extract one private supplier PDF into a review draft",
        "parameters": [
          {
            "description": "import_id and asset_id from completed private upload",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE status, delta, expense_draft, done or error",
            "schema": {
              "type": "string"
            }
          }
        },
        "operationId": "post_field_service_ai_expenses_extract"
      }
    },
    "/field_service/ai/expenses/imports": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "AI tools"
        ],
        "summary": "List resumable supplier invoice imports",
        "parameters": [
          {
            "type": "integer",
            "description": "Pagination offset; page size 50",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_ai_expenses_imports"
      }
    },
    "/field_service/ai/expenses/imports/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "AI tools"
        ],
        "summary": "Read one supplier invoice import for review or recovery",
        "parameters": [
          {
            "type": "string",
            "description": "Import ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_ai_expenses_imports_id"
      }
    },
    "/field_service/ai/expenses/intake": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owner/admin and Core only. Creates private intake records without creating expenses or assigning an amount. Upload each PDF with the existing private file upload flow using kind expense_imports and its returned intake ID.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "AI tools"
        ],
        "summary": "Prepare up to 25 supplier PDF invoice imports",
        "parameters": [
          {
            "description": "filenames array, 1 to 25 PDF names",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_ai_expenses_intake"
      }
    },
    "/field_service/ai/expenses/save": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owner/admin and Core only. Validates edited totals/date/currency and tenant job. Freezes the reviewed payload before creating one deterministic expense per source PDF. Retries resume the same saved payload and attachment transfer. Approval and reimbursement remain separate existing workflows.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "AI tools"
        ],
        "summary": "Save an explicitly reviewed supplier invoice as a pending expense",
        "parameters": [
          {
            "description": "import_id, version, confirmed:true, draft, and optional job_id",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_ai_expenses_save"
      }
    },
    "/field_service/ai/transcribe": {
      "post": {
        "description": "Field Service Core only. Streams progress immediately, then editable transcript. Does not create or change any business records. Accepts up to 8 MB audio.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "text/event-stream"
        ],
        "tags": [
          "AI tools"
        ],
        "summary": "Transcribe dictated Field Service instructions with Gemini",
        "parameters": [
          {
            "description": "Base64 audio and MIME type",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.fieldServiceTranscribePayload"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE status, delta, transcript, done or error events",
            "schema": {
              "type": "string"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_ai_transcribe"
      }
    },
    "/field_service/files": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns tenant-scoped attachment metadata with source/customer/property/type and updated-date filters. Internal expense sources require an owner/admin. Every download rechecks source access and tenant ownership.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Files"
        ],
        "summary": "Browse the Field Service files and media library",
        "parameters": [
          {
            "type": "string",
            "description": "Customer ID",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property ID",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Source record kind",
            "name": "kind",
            "in": "query"
          },
          {
            "type": "string",
            "description": "image, video or document",
            "name": "media_type",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Inclusive updated_at RFC3339 timestamp",
            "name": "after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Exclusive updated_at RFC3339 timestamp",
            "name": "before",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset; page size 50",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_files"
      }
    },
    "/field_service/files/{asset_id}/complete": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Validates server-selected source, S3 metadata, exact byte length and PDF signature before making it attachable. Upload URL is create-only; repeated completion returns the same document.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Files"
        ],
        "summary": "Complete and validate a private PDF upload",
        "parameters": [
          {
            "type": "string",
            "description": "Upload ID",
            "name": "asset_id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_files_asset_id_complete"
      }
    },
    "/field_service/files/{kind}/{id}/{asset_id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Files"
        ],
        "summary": "Prepare an authorized source attachment download",
        "parameters": [
          {
            "type": "string",
            "description": "Source record kind",
            "name": "kind",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Source ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "type": "string",
            "description": "Attachment asset ID",
            "name": "asset_id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_files_kind_id_asset_id"
      }
    },
    "/field_service/files/assets/{asset_id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Files"
        ],
        "summary": "Get an uploaded Field Service PDF",
        "parameters": [
          {
            "type": "string",
            "description": "Completed upload ID",
            "name": "asset_id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_files_assets_asset_id"
      }
    },
    "/field_service/files/upload": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Uses the existing create-only S3 signer, bound to the exact byte length, content type, authenticated tenant and existing source record. PDF is not attached or visible until completion and a versioned record update. Existing image/video uploads continue using the shared customer-assets API.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Files"
        ],
        "summary": "Prepare a private Field Service PDF upload",
        "parameters": [
          {
            "description": "PDF and source record",
            "name": "payload",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceFileUploadInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_files_upload"
      }
    },
    "/field_service/billing": {
      "get": {
        "description": "Returns app-managed seven-day trial or Field Service Core entitlement without modifying the existing portal. The trial includes Core features; expiration returns to Free Forever with AI and Marketing locked until Core is paid ($29/month). Trial start and expiry are never managed by Stripe. Administrative complimentary grants keep Core access enabled without payment or a trial deadline.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Account billing"
        ],
        "summary": "Field Service plans and shared tenant entitlement",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_billing"
      }
    },
    "/field_service/billing/cancel": {
      "post": {
        "description": "Cancels only Field Service; other product subscriptions and the shared account remain unchanged.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Account billing"
        ],
        "summary": "Cancel Field Service Core at the end of the billing period",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceBilling"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_billing_cancel"
      }
    },
    "/field_service/billing/checkout": {
      "post": {
        "description": "Creates a custom Checkout session for the in-app Stripe Payment Element on the existing platform customer. Returns its client_secret only to the account owner; never logs or persists that secret. Only the shared account owner may subscribe. Price and return URL are verified server-side; Stripe webhooks grant entitlement.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Account billing"
        ],
        "summary": "Subscribe to Field Service Core",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_billing_checkout"
      }
    },
    "/field_service/billing/confirm": {
      "post": {
        "description": "Retrieves the owner's stored checkout from Stripe, verifies tenant and shared customer ownership, and reconciles the subscription. Never grants access from browser payment claims. Safe to retry; webhooks also reconcile later renewals. Includes purchase {transaction_id, value, currency} only for a completed, paid, positive-value live USD subscription checkout; transaction_id is the stable Checkout Session ID, value is its actual total in dollars. Repeated confirmations return the same receipt; clients must deduplicate. Ordinary billing reads never include a purchase.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Account billing"
        ],
        "summary": "Verify Field Service checkout completion",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "502": {
            "description": "Bad Gateway",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_billing_confirm"
      }
    },
    "/field_service/billing/trial": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Uses the existing authenticated shared account (including its guest owner). No email, card, Stripe customer, or Stripe subscription is created. Server time controls the immutable trial. Repeated requests never extend or restart it; existing subscribers are ineligible.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Account billing"
        ],
        "summary": "Start the tenant's one-time seven-day Field Service trial",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_billing_trial"
      }
    },
    "/field_service/settings": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Supported kinds: customers, requests, estimates, jobs, visits, invoices, catalog, expenses, time_entries, checklists, chemicals, notes, settings, crews, pipeline_stages. All identity and tenant context comes from shared authentication. Crews and pipeline stages have manager-only writes and shared member reads.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Settings"
        ],
        "summary": "List settings",
        "parameters": [
          {
            "type": "string",
            "description": "Search name, title, company or email",
            "name": "search",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Customer",
            "name": "customer_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Service property",
            "name": "property_id",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Job",
            "name": "job_id",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "1-200",
            "name": "limit",
            "in": "query"
          },
          {
            "type": "integer",
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceListResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_settings"
      },
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Time entries default to the acting user; owners/admins may specify an active same-tenant user_id. Requests/jobs may reference a completed earlier job for the same customer/property through related_job_id.\nJob-linked records inherit the job service property when property_id is omitted.\nPrices are integer cents. Totals are calculated by the server. New visits snapshot their job's budget; callers cannot supply visit financial values. Referenced customers, jobs, visits and users must belong to the authenticated tenant.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Settings"
        ],
        "summary": "Create setting",
        "parameters": [
          {
            "description": "Record",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_settings"
      }
    },
    "/field_service/settings/{id}": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Settings"
        ],
        "summary": "Get setting",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_settings_id"
      },
      "put": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Settings"
        ],
        "summary": "Update setting (PUT)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "put_field_service_settings_id"
      },
      "delete": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Soft archives a record only when no active downstream records remain. Conflicts include dependencies (id, kind, name) and has_more. Financial restrictions also apply.",
        "tags": [
          "Settings"
        ],
        "summary": "Archive setting",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "delete_field_service_settings_id"
      },
      "patch": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Job-linked records inherit the job service property when property_id is empty.\nActivity records authenticated actor, timestamp, and before/after changes to editable fields. Caller-supplied history is ignored. Send the current version to reject stale edits. Sent invoices are immutable; record payments through actions. Editing an accepted estimate requires fresh acceptance. The address field is the complete service-location snapshot, independent of the customer billing address; selecting a property fills its complete address only when no explicit address is supplied. Visit job bindings and saved financial snapshots are immutable through this endpoint.\nMissing or invalid form details return code=requirements_not_met and issues targeting request fields. Keep the pending form values and focus the identified field; saving still requires explicit submission.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Settings"
        ],
        "summary": "Update setting (PATCH)",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Record fields and current version",
            "name": "record",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "patch_field_service_settings_id"
      }
    },
    "/field_service/settings/{id}/actions": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "For create_invoice, repeating the source service address preserves its saved property link; changing or clearing the address clears that link on the new invoice only.\ncopy_attachment accepts source_kind, an exact source record name/ID in source, and an exact attachment filename in name. Owners/admins can append one existing tenant-owned file as internal only, retaining existing attachments and source sharing settings. Duplicate copies are idempotent. Document send accepts recipient_email to deliver this document to a reviewed recipient without changing the customer primary email. Jobs: apply_change_estimate appends an approved same-customer/property estimate to an active non-recurring job, including after progress invoicing. Existing prices, invoices and original estimate remain unchanged. Requires matching tax rates and no discounts; repeated estimate application is idempotent. Customers: add_equipment appends equipment to an exact property name/address/ID with a stable idempotency_key. Catalog: set_supply_list changes only the supply_list flag. set_stock sets quantity, reorder_point, location and supply_list; use_stock records a positive available quantity against an exact job name/ID with a stable idempotency_key. Jobs: apply_estimate copies authoritative prices from an exact approved estimate name/ID into an empty active job of the same customer/property; deposited or converted estimates are rejected. Customers: add_property appends a service property with required address and optional name/city/region/postal_code/country; existing properties and billing address are preserved. Repeated idempotency_key returns the same property; duplicate addresses require choosing the saved property. Requests and estimates: set_pipeline_stage (pipeline_stage_id or empty to clear). Requests: convert_to_estimate/convert_to_job. Estimates: send/approve/request_changes/decline/convert_to_job/duplicate. approve accepts draft or sent estimates with priced items and a customer signature, including in-person approval without email. Jobs: start/complete/reopen/cancel/create_invoice/generate_visits/cancel_invoice_preparation. create_invoice supports a gross amount_cents for progress or up to five same-customer job_ids for combining remaining balances. An optional address supplies the complete service-address snapshot for the new invoice only; it does not change source jobs or customer properties. A stable idempotency_key is required for partial/combined requests and retries. Recurring jobs require a completed visit_id; per-visit or fixed calendar-cycle invoices are created as drafts once per cycle. Visits: en_route/arrive/start/complete/reopen/cancel. en_route and arrive record server timestamps in custom_fields; en_route optionally accepts an ETA in reason and queues opted-in appointment email. Invoices: send/issue/email_receipt/void/record_payment (cash/check/external). issue records in-person delivery without sending email. Fully paid manual payments queue an email receipt when a valid customer email is available; email_receipt queues the same idempotent receipt on demand. resolve_delivery requires manager-reviewed evidence and sent/not_sent reference to clear an uncertain delivery attempt. Time: submit/approve. Checklists: complete/reopen. Conversion is idempotent; all money is server calculated.\nMissing workflow details return code=requirements_not_met and issues with stable code, message, target (kind/id/field/scope), and resolution. Supported resolutions are edit_pricing, enter_signature, enter_recipient, edit_customer_email, complete_checklist, edit_schedule, and focus_field. Pricing issues include minimum_total_cents (zero for estimate send; one for invoice send/issue and estimate approval). Checklist errors retain missing_required for older clients. Preserve the original action draft while resolving issues, then explicitly submit again. Optional version guards the reviewed record against concurrent changes; a mismatch returns 409. Legacy error text remains available.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "Settings"
        ],
        "summary": "Run setting action",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          },
          {
            "description": "Workflow action",
            "name": "action",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceActionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceRecord"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceProblem"
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "post_field_service_settings_id_actions"
      }
    },
    "/field_service/settings/{id}/deletion_dependencies": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Returns up to 201 active downstream records in the authenticated tenant. When has_more is true, remove listed dependencies and check again. DELETE always rechecks dependencies.",
        "tags": [
          "Settings"
        ],
        "summary": "Check setting deletion dependencies",
        "parameters": [
          {
            "type": "string",
            "description": "Record ID",
            "name": "id",
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_settings_id_deletion_dependencies"
      }
    },
    "/field_service/dashboard": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Tenant totals over active work plus archived committed financial records. Revenue excludes draft and void invoices; archiving paid work does not erase financial history. Costs require owner/admin access.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Reporting"
        ],
        "summary": "Field Service operational and financial totals",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/models.FieldServiceDashboard"
            }
          }
        },
        "operationId": "get_field_service_dashboard"
      }
    },
    "/field_service/reports/export": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Tenant-scoped, permission checked CSV export. Up to 2,000 records per export with the X-Export-Truncated header indicating additional matching records. Use offset/date/status filters to export further pages. Spreadsheet formula cells are escaped.",
        "produces": [
          "text/csv"
        ],
        "tags": [
          "Reporting"
        ],
        "summary": "Export Field Service operational records as CSV",
        "parameters": [
          {
            "type": "string",
            "description": "customers, requests, estimates, jobs, visits, receivables, invoices, catalog, expenses, time_entries, checklists, chemicals",
            "name": "report",
            "in": "query",
            "required": true
          },
          {
            "type": "integer",
            "description": "Starting record offset",
            "name": "offset",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created at or after (RFC3339)",
            "name": "created_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Created before, exclusive (RFC3339)",
            "name": "created_before",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Status",
            "name": "status",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Start time filter (RFC3339)",
            "name": "starts_after",
            "in": "query"
          },
          {
            "type": "string",
            "description": "End time filter (RFC3339)",
            "name": "ends_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "file"
            }
          }
        },
        "operationId": "get_field_service_reports_export"
      }
    },
    "/field_service/revenue": {
      "get": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Owner/admin financial report. Revenue is sent invoice totals after discounts and before tax, including unpaid and archived issued invoices; draft/void invoices are excluded. Buckets use the configured business timezone. The current annual target is used as a reference for the selected year, not historical target snapshots.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Reporting"
        ],
        "summary": "Read issued revenue for an annual or explicit date range",
        "parameters": [
          {
            "type": "integer",
            "description": "Year, 2000 through 2100; defaults current business year",
            "name": "year",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Range start inclusive (RFC3339); requires ends_at and excludes year; maximum 367 days",
            "name": "starts_at",
            "in": "query"
          },
          {
            "type": "string",
            "description": "Range end exclusive (RFC3339); requires starts_at",
            "name": "ends_at",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "operationId": "get_field_service_revenue"
      }
    },
    "/field_service/setup": {
      "get": {
        "description": "Owner/admin checklist derived from shared identity, tenant records and live Stripe Connect readiness. Optional features do not affect the percentage. Unavailable checks never count as complete. Does not create accounts, submit verification, send messages or purchase subscriptions.",
        "produces": [
          "application/json"
        ],
        "tags": [
          "Setup"
        ],
        "summary": "Get ServiceKeel portal setup progress",
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceSetupResponse"
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "402": {
            "description": "Payment Required",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "get_field_service_setup"
      }
    },
    "/field_service/graphql": {
      "post": {
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Uses existing OAuth bearer tokens with explicit field_service:read (queries) or field_service:write (createRequest). Same-account active applications and current shared owners/admins only. Queries expose customers/jobs/visits/invoices with first 1-100 and offset 0-10000. Money is decimal-string integer cents. At most 200 requested records, 128 expanded fields, 10 aliases, depth 6, one mutation, and 8 KiB query per call. createRequest requires a stable idempotencyKey (16-100 characters). No payment mutations or webhook dispatch.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "tags": [
          "GraphQL"
        ],
        "summary": "Tenant Field Service GraphQL developer API",
        "parameters": [
          {
            "description": "GraphQL operation",
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/gin.FieldServiceGraphQLRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "401": {
            "description": "Unauthorized",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        },
        "operationId": "post_field_service_graphql"
      }
    }
  },
  "definitions": {
    "gin.FieldServiceActionRequest": {
      "type": "object",
      "required": [
        "action"
      ],
      "properties": {
        "action": {
          "type": "string"
        },
        "address": {
          "type": "string"
        },
        "amount_cents": {
          "type": "integer"
        },
        "city": {
          "type": "string"
        },
        "confirm_missing_required": {
          "type": "boolean"
        },
        "country": {
          "type": "string"
        },
        "due_at": {
          "type": "string"
        },
        "equipment": {
          "$ref": "#/definitions/gin.FieldServiceEquipmentInput"
        },
        "estimate": {
          "type": "string"
        },
        "idempotency_key": {
          "type": "string"
        },
        "job": {
          "type": "string"
        },
        "job_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "location": {
          "type": "string"
        },
        "logo_data_url": {
          "type": "string"
        },
        "method": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "pipeline_stage_id": {
          "type": "string"
        },
        "postal_code": {
          "type": "string"
        },
        "property": {
          "type": "string"
        },
        "quantity": {
          "type": "integer"
        },
        "reason": {
          "type": "string"
        },
        "recipient_email": {
          "type": "string"
        },
        "reference": {
          "type": "string"
        },
        "region": {
          "type": "string"
        },
        "reorder_point": {
          "type": "integer"
        },
        "signature": {
          "type": "string"
        },
        "source": {
          "type": "string"
        },
        "source_kind": {
          "type": "string"
        },
        "supply_list": {
          "type": "boolean"
        },
        "title": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        },
        "visit_id": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceAssistantArchiveRequest": {
      "type": "object",
      "properties": {
        "archived": {
          "type": "boolean"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceAssistantContext": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "timezone": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceAssistantRequest": {
      "type": "object",
      "properties": {
        "approval_id": {
          "type": "string"
        },
        "choice_id": {
          "type": "string"
        },
        "context": {
          "$ref": "#/definitions/gin.FieldServiceAssistantContext"
        },
        "conversation_id": {
          "type": "string"
        },
        "edits": {
          "type": "object",
          "additionalProperties": true
        },
        "message": {
          "type": "string"
        },
        "pending_id": {
          "type": "string"
        },
        "refresh_review": {
          "type": "boolean"
        },
        "request_id": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceAttachmentTitleRequest": {
      "type": "object",
      "properties": {
        "title": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceAutomationRunResult": {
      "type": "object",
      "properties": {
        "action_applied": {
          "type": "boolean"
        },
        "delivered": {
          "type": "boolean"
        },
        "delivery_failed": {
          "type": "boolean"
        },
        "has_more": {
          "type": "boolean"
        },
        "record": {
          "$ref": "#/definitions/models.FieldServiceRecord"
        },
        "skipped": {
          "type": "boolean"
        }
      }
    },
    "gin.FieldServiceCampaignScheduleInput": {
      "type": "object",
      "properties": {
        "send_at": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceCheckoutOptions": {
      "type": "object",
      "properties": {
        "amount_cents": {
          "type": "integer"
        },
        "expires_at": {
          "type": "string"
        },
        "payment_method_type": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceCustomerImportRequest": {
      "type": "object",
      "properties": {
        "import_id": {
          "type": "string"
        },
        "preview": {
          "type": "boolean"
        },
        "row": {
          "$ref": "#/definitions/gin.FieldServiceCustomerImportRow"
        }
      }
    },
    "gin.FieldServiceCustomerImportRow": {
      "type": "object",
      "properties": {
        "address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "company": {
          "type": "string"
        },
        "country": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "lead_source": {
          "type": "string"
        },
        "marketing_opt_in": {
          "type": "boolean"
        },
        "name": {
          "type": "string"
        },
        "notes": {
          "type": "string"
        },
        "phone": {
          "type": "string"
        },
        "postal_code": {
          "type": "string"
        },
        "region": {
          "type": "string"
        },
        "row_number": {
          "type": "integer"
        },
        "service_address": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "tags": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "gin.FieldServiceCustomerMergeRequest": {
      "type": "object",
      "properties": {
        "preview": {
          "type": "boolean"
        },
        "source_version": {
          "type": "integer"
        },
        "target_id": {
          "type": "string"
        },
        "target_version": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceEmailActivityItem": {
      "type": "object",
      "properties": {
        "campaign_id": {
          "type": "string"
        },
        "created_at": {
          "type": "string"
        },
        "customer_id": {
          "type": "string"
        },
        "delivered_at": {
          "type": "string"
        },
        "first_clicked_at": {
          "type": "string"
        },
        "first_opened_at": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "recipient_email": {
          "type": "string"
        },
        "record_id": {
          "type": "string"
        },
        "record_kind": {
          "type": "string"
        },
        "replied_at": {
          "type": "string"
        },
        "reply_count": {
          "type": "integer"
        },
        "status": {
          "$ref": "#/definitions/models.EmailLogStatus"
        },
        "subject": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceEmailActivityResponse": {
      "type": "object",
      "properties": {
        "has_more": {
          "type": "boolean"
        },
        "items": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/gin.FieldServiceEmailActivityItem"
          }
        },
        "stats": {
          "$ref": "#/definitions/models.FieldServiceEmailStats"
        }
      }
    },
    "gin.FieldServiceEmailReplyRequest": {
      "type": "object",
      "required": [
        "text"
      ],
      "properties": {
        "text": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceEquipmentInput": {
      "type": "object",
      "properties": {
        "capacity": {
          "type": "string"
        },
        "equipment_type": {
          "type": "string"
        },
        "installed": {
          "type": "string"
        },
        "location": {
          "type": "string"
        },
        "manufacturer": {
          "type": "string"
        },
        "model": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "serial": {
          "type": "string"
        },
        "service_history": {
          "type": "string"
        },
        "warranty": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceFileUploadInput": {
      "type": "object",
      "properties": {
        "filename": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "record_id": {
          "type": "string"
        },
        "size_bytes": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceGraphQLRequest": {
      "type": "object",
      "properties": {
        "operationName": {
          "type": "string"
        },
        "query": {
          "type": "string"
        },
        "variables": {
          "type": "object",
          "additionalProperties": true
        }
      }
    },
    "gin.FieldServiceGrowthInput": {
      "type": "object",
      "properties": {
        "growth": {
          "$ref": "#/definitions/models.FieldServiceGrowth"
        },
        "name": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceMarketingActionInput": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceMarketingAttribution": {
      "type": "object",
      "properties": {
        "click_through_rate": {
          "type": "number"
        },
        "completed_jobs": {
          "type": "integer"
        },
        "cost_per_job_cents": {
          "type": "integer"
        },
        "cost_per_lead_cents": {
          "type": "integer"
        },
        "jobs": {
          "type": "integer"
        },
        "method": {
          "type": "string"
        },
        "paid_revenue_cents": {
          "type": "integer"
        },
        "revenue_cents": {
          "type": "integer"
        },
        "roas": {
          "type": "number"
        },
        "spend_cents": {
          "type": "integer"
        },
        "truncated": {
          "type": "boolean"
        },
        "window": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceMarketingGenerateInput": {
      "type": "object",
      "properties": {
        "prompt": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceMarketingInput": {
      "type": "object",
      "properties": {
        "marketing": {
          "$ref": "#/definitions/models.FieldServiceMarketing"
        },
        "name": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceMarketingPreview": {
      "type": "object",
      "properties": {
        "activation_blockers": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "activation_supported": {
          "type": "boolean"
        },
        "attribution": {
          "$ref": "#/definitions/gin.FieldServiceMarketingAttribution"
        },
        "blockers": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "count": {
          "type": "integer"
        },
        "effective_timing": {
          "type": "string"
        },
        "eligible_count": {
          "type": "integer"
        },
        "estimated_cost_cents": {
          "type": "integer"
        },
        "evaluated_at": {
          "type": "string"
        },
        "excluded_count": {
          "type": "integer"
        },
        "items": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/gin.FieldServiceMarketingRecipient"
          }
        },
        "opportunities": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/gin.FieldServiceMarketingRecipient"
          }
        },
        "quiet_hours": {
          "type": "boolean"
        },
        "recipients": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/gin.FieldServiceMarketingRecipient"
          }
        },
        "truncated": {
          "type": "boolean"
        },
        "warnings": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "gin.FieldServiceMarketingRecipient": {
      "type": "object",
      "properties": {
        "address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "country": {
          "type": "string"
        },
        "customer_id": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "message": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "phone": {
          "type": "string"
        },
        "postal_code": {
          "type": "string"
        },
        "reason": {
          "type": "string"
        },
        "region": {
          "type": "string"
        },
        "source_id": {
          "type": "string"
        },
        "source_kind": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceProblem": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string"
        },
        "error": {
          "type": "string"
        },
        "issues": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/gin.FieldServiceRequirementIssue"
          }
        },
        "missing_required": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "gin.FieldServiceRequirementIssue": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string"
        },
        "message": {
          "type": "string"
        },
        "minimum_total_cents": {
          "type": "integer"
        },
        "resolution": {
          "type": "string"
        },
        "target": {
          "$ref": "#/definitions/gin.FieldServiceRequirementTarget"
        }
      }
    },
    "gin.FieldServiceRequirementTarget": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "scope": {
          "type": "string",
          "enum": [
            "record",
            "request"
          ]
        }
      }
    },
    "gin.FieldServiceSetupResponse": {
      "type": "object",
      "properties": {
        "checked_at": {
          "type": "string"
        },
        "completed": {
          "type": "integer"
        },
        "percent": {
          "type": "integer"
        },
        "steps": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/gin.FieldServiceSetupStep"
          }
        },
        "total": {
          "type": "integer"
        },
        "verified": {
          "type": "boolean"
        }
      }
    },
    "gin.FieldServiceSetupStep": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "key": {
          "type": "string"
        },
        "path": {
          "type": "string"
        },
        "required": {
          "type": "boolean"
        },
        "state": {
          "description": "complete, todo, attention, unavailable",
          "type": "string"
        },
        "title": {
          "type": "string"
        }
      }
    },
    "gin.FieldServiceVoiceSessionRequest": {
      "type": "object",
      "properties": {
        "conversation_id": {
          "type": "string"
        },
        "sdp": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.FieldServiceWebhookInput": {
      "type": "object",
      "properties": {
        "application_id": {
          "type": "string"
        },
        "url": {
          "type": "string"
        }
      }
    },
    "gin.PartnerAppCredentialsResponse": {
      "type": "object",
      "properties": {
        "app": {
          "$ref": "#/definitions/models.PartnerApp"
        },
        "client_id": {
          "type": "string"
        },
        "client_secret": {
          "type": "string"
        }
      }
    },
    "gin.PartnerAppRequest": {
      "type": "object",
      "required": [
        "name"
      ],
      "properties": {
        "description": {
          "type": "string",
          "maxLength": 4000
        },
        "instructions_url": {
          "type": "string",
          "maxLength": 2048
        },
        "logo_url": {
          "type": "string",
          "maxLength": 2048
        },
        "marketing_url": {
          "type": "string",
          "maxLength": 2048
        },
        "name": {
          "type": "string",
          "maxLength": 120
        },
        "privacy_policy_url": {
          "type": "string",
          "maxLength": 2048
        },
        "redirect_uris": {
          "type": "array",
          "maxItems": 10,
          "items": {
            "type": "string"
          }
        },
        "revision": {
          "type": "integer"
        },
        "scopes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "servicekeel_reference_confirmed": {
          "type": "boolean"
        },
        "support_url": {
          "type": "string",
          "maxLength": 2048
        },
        "terms_url": {
          "type": "string",
          "maxLength": 2048
        },
        "test_credentials": {
          "$ref": "#/definitions/models.PartnerTestCredentials"
        },
        "website_url": {
          "type": "string",
          "maxLength": 2048
        }
      }
    },
    "gin.PartnerLogoUploadRequest": {
      "type": "object",
      "required": [
        "content_type",
        "filename",
        "size_bytes"
      ],
      "properties": {
        "content_type": {
          "type": "string"
        },
        "filename": {
          "type": "string"
        },
        "size_bytes": {
          "type": "integer"
        }
      }
    },
    "gin.PartnerLogoUploadResponse": {
      "type": "object",
      "properties": {
        "content_type": {
          "type": "string"
        },
        "expires_in": {
          "type": "integer"
        },
        "logo_key": {
          "type": "string"
        },
        "logo_url": {
          "type": "string"
        },
        "size_bytes": {
          "type": "integer"
        },
        "upload_headers": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        },
        "upload_url": {
          "type": "string"
        }
      }
    },
    "gin.PartnerOAuthConsentDecision": {
      "type": "object",
      "required": [
        "approve",
        "consent_token"
      ],
      "properties": {
        "approve": {
          "type": "boolean"
        },
        "consent_token": {
          "type": "string"
        }
      }
    },
    "gin.PartnerOAuthConsentResponse": {
      "type": "object",
      "properties": {
        "app_id": {
          "type": "string"
        },
        "app_name": {
          "type": "string"
        },
        "consent_token": {
          "type": "string"
        },
        "expires_in": {
          "type": "integer"
        },
        "logo_url": {
          "type": "string"
        },
        "redirect_uri": {
          "type": "string"
        },
        "scopes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "testing": {
          "type": "boolean"
        }
      }
    },
    "gin.PartnerOAuthIntrospectionResponse": {
      "type": "object",
      "properties": {
        "active": {
          "type": "boolean"
        },
        "client_id": {
          "type": "string"
        },
        "exp": {
          "type": "integer"
        },
        "scope": {
          "type": "string"
        },
        "token_type": {
          "type": "string"
        }
      }
    },
    "gin.PartnerOAuthTokenResponse": {
      "type": "object",
      "properties": {
        "access_token": {
          "type": "string"
        },
        "expires_in": {
          "type": "integer"
        },
        "refresh_token": {
          "type": "string"
        },
        "scope": {
          "type": "string"
        },
        "token_type": {
          "type": "string"
        }
      }
    },
    "gin.PartnerRevisionRequest": {
      "type": "object",
      "required": [
        "revision"
      ],
      "properties": {
        "revision": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "gin.PartnerSignupRequest": {
      "type": "object",
      "required": [
        "company_name",
        "company_website",
        "email",
        "first_name",
        "last_name"
      ],
      "properties": {
        "company_name": {
          "type": "string",
          "maxLength": 200
        },
        "company_website": {
          "description": "CompanyWebsite accepts a public domain or HTTPS URL. Omitted schemes default to HTTPS.",
          "type": "string",
          "maxLength": 2048
        },
        "email": {
          "type": "string",
          "maxLength": 254
        },
        "first_name": {
          "type": "string",
          "maxLength": 100
        },
        "last_name": {
          "type": "string",
          "maxLength": 100
        }
      }
    },
    "gin.PhoneBookingSettings": {
      "type": "object",
      "properties": {
        "form_id": {
          "type": "string"
        },
        "reminder_hours": {
          "type": "integer"
        }
      }
    },
    "gin.PhoneCallActionInput": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string"
        },
        "party_id": {
          "type": "string"
        },
        "to": {
          "type": "string"
        }
      }
    },
    "gin.PhoneCallInput": {
      "type": "object",
      "properties": {
        "callback_number": {
          "type": "string"
        },
        "idempotency_key": {
          "type": "string"
        },
        "to": {
          "type": "string"
        }
      }
    },
    "gin.PhoneConversation": {
      "type": "object",
      "properties": {
        "handoff": {
          "type": "boolean"
        },
        "handoff_reason": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "integration_id": {
          "type": "string"
        },
        "opted_out": {
          "type": "boolean"
        },
        "phone": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.PhoneHandoffInput": {
      "type": "object",
      "properties": {
        "handoff": {
          "type": "boolean"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.PhoneOAuthExchangeInput": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string"
        },
        "state": {
          "type": "string"
        }
      }
    },
    "gin.PhoneScheduleInput": {
      "type": "object",
      "properties": {
        "body": {
          "type": "string"
        },
        "consent_confirmed": {
          "type": "boolean"
        },
        "idempotency_key": {
          "type": "string"
        },
        "recipients": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "send_at": {
          "type": "string"
        },
        "timezone": {
          "type": "string"
        }
      }
    },
    "gin.PhoneSettingsInput": {
      "type": "object",
      "properties": {
        "automation_enabled": {
          "type": "boolean"
        },
        "number_id": {
          "type": "string"
        },
        "webhook_secret": {
          "type": "string"
        }
      }
    },
    "gin.PhoneVoiceInput": {
      "type": "object",
      "properties": {
        "device_id": {
          "type": "string"
        },
        "enabled": {
          "type": "boolean"
        },
        "handoff_number": {
          "type": "string"
        },
        "instructions": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceAutopayRequest": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string"
        },
        "consent": {
          "type": "boolean"
        },
        "consent_version": {
          "type": "string"
        },
        "expected_job_version": {
          "type": "integer"
        },
        "max_amount_cents": {
          "type": "integer"
        },
        "payment_method_id": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceBookingSubmission": {
      "type": "object",
      "properties": {
        "address": {
          "type": "string"
        },
        "answers": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        },
        "description": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "phone": {
          "type": "string"
        },
        "request_key": {
          "type": "string"
        },
        "service_id": {
          "type": "string"
        },
        "starts_at": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceCalendarIssueRequest": {
      "type": "object",
      "properties": {
        "include_details": {
          "type": "boolean"
        },
        "scope": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceCancelJobRequest": {
      "type": "object",
      "properties": {
        "reason": {
          "type": "string"
        },
        "scope": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.fieldServiceDraftPayload": {
      "type": "object",
      "required": [
        "kind",
        "transcript"
      ],
      "properties": {
        "customer_id": {
          "type": "string"
        },
        "instructions": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "record_id": {
          "type": "string"
        },
        "transcript": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceGeocodeRequest": {
      "type": "object",
      "required": [
        "customer_id"
      ],
      "properties": {
        "customer_id": {
          "type": "string"
        },
        "property_id": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceGustoExportRequest": {
      "type": "object",
      "properties": {
        "double_overtime_minutes": {
          "type": "integer"
        },
        "integration_id": {
          "type": "string"
        },
        "overtime_minutes": {
          "type": "integer"
        },
        "period_end": {
          "type": "string"
        },
        "period_start": {
          "type": "string"
        },
        "regular_minutes": {
          "type": "integer"
        },
        "time_entry_id": {
          "type": "string"
        },
        "timezone": {
          "type": "string"
        }
      }
    },
    "gin.fieldServicePortalEstimateInput": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string"
        },
        "message": {
          "type": "string"
        },
        "selected_optional_item_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "signature": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.fieldServicePortalIssueRequest": {
      "type": "object",
      "properties": {
        "send_email": {
          "type": "boolean"
        }
      }
    },
    "gin.fieldServicePortalRequestInput": {
      "type": "object",
      "properties": {
        "description": {
          "type": "string"
        },
        "title": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceQuickBooksPreviewRequest": {
      "type": "object",
      "properties": {
        "deposit_account_id": {
          "type": "string"
        },
        "fallback_catalog_id": {
          "type": "string"
        },
        "income_account_id": {
          "type": "string"
        },
        "integration_id": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "payment_id": {
          "type": "string"
        },
        "record_id": {
          "type": "string"
        },
        "tax_code_id": {
          "type": "string"
        },
        "tax_rate_id": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceReferralApplyRequest": {
      "type": "object",
      "properties": {
        "invoice_id": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceReferralRewardRequest": {
      "type": "object",
      "properties": {
        "amount_cents": {
          "type": "integer"
        },
        "job_id": {
          "type": "string"
        },
        "percent_bps": {
          "type": "integer"
        },
        "source_invoice_id": {
          "type": "string"
        },
        "type": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceReferralSubmission": {
      "type": "object",
      "properties": {
        "description": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "idempotency_key": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "phone": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceRouteApplyRequest": {
      "type": "object",
      "required": [
        "proposal_id"
      ],
      "properties": {
        "proposal_id": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceRouteRequest": {
      "type": "object",
      "required": [
        "visit_ids"
      ],
      "properties": {
        "timezone": {
          "type": "string"
        },
        "visit_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "gin.fieldServiceTranscribePayload": {
      "type": "object",
      "required": [
        "audio_base64",
        "mime_type"
      ],
      "properties": {
        "audio_base64": {
          "type": "string"
        },
        "mime_type": {
          "type": "string"
        }
      }
    },
    "gin.fieldServiceWebsiteInput": {
      "type": "object",
      "properties": {
        "content": {
          "$ref": "#/definitions/models.FieldServiceWebsiteContent"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.fieldServiceWebsitePublishInput": {
      "type": "object",
      "properties": {
        "confirmed": {
          "type": "boolean"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.fsGustoExpenseExportRequest": {
      "type": "object",
      "properties": {
        "confirmed": {
          "type": "boolean"
        },
        "integration_id": {
          "type": "string"
        },
        "preview_id": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "gin.fsGustoExpenseRequest": {
      "type": "object",
      "properties": {
        "confirm_prepare": {
          "type": "boolean"
        },
        "expense_id": {
          "type": "string"
        },
        "integration_id": {
          "type": "string"
        },
        "payroll_id": {
          "type": "string"
        }
      }
    },
    "models.EmailLogStatus": {
      "type": "string",
      "enum": [
        "pending",
        "sent",
        "delivered",
        "bounced",
        "complained",
        "rejected",
        "delivery_delayed",
        "failed",
        "skipped_unsubscribed",
        "skipped_no_recipient"
      ],
      "x-enum-varnames": [
        "EmailLogStatusPending",
        "EmailLogStatusSent",
        "EmailLogStatusDelivered",
        "EmailLogStatusBounced",
        "EmailLogStatusComplained",
        "EmailLogStatusRejected",
        "EmailLogStatusDeliveryDelayed",
        "EmailLogStatusFailed",
        "EmailLogStatusSkippedUnsubscribed",
        "EmailLogStatusSkippedNoRecipient"
      ]
    },
    "models.FieldServiceAccounting": {
      "type": "object",
      "properties": {
        "attempted_at": {
          "type": "string"
        },
        "dependencies": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceAccountingDependency"
          }
        },
        "deposit_account_id": {
          "type": "string"
        },
        "error": {
          "type": "string"
        },
        "expected_total_cents": {
          "type": "integer"
        },
        "exported_at": {
          "type": "string"
        },
        "fallback_catalog_id": {
          "type": "string"
        },
        "income_account_id": {
          "type": "string"
        },
        "integration_id": {
          "type": "string"
        },
        "payload_json": {
          "type": "string"
        },
        "payment_id": {
          "type": "string"
        },
        "provider_account_id": {
          "type": "string"
        },
        "remote_id": {
          "type": "string"
        },
        "remote_kind": {
          "type": "string"
        },
        "remote_sync_token": {
          "type": "string"
        },
        "request_id": {
          "type": "string"
        },
        "source_id": {
          "type": "string"
        },
        "source_kind": {
          "type": "string"
        },
        "tax_code_id": {
          "type": "string"
        },
        "tax_rate_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAccountingDependency": {
      "type": "object",
      "properties": {
        "export_id": {
          "type": "string"
        },
        "remote_id": {
          "type": "string"
        },
        "remote_kind": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceActivity": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string"
        },
        "actor_name": {
          "type": "string"
        },
        "at": {
          "type": "string"
        },
        "changes": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceChange"
          }
        },
        "detail": {
          "type": "string"
        },
        "user_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantChange": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "operation": {
          "type": "string"
        },
        "path": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceAssistantChoice": {
      "type": "object",
      "properties": {
        "description": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "label": {
          "type": "string"
        },
        "path": {
          "type": "string"
        },
        "record_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantConversation": {
      "type": "object",
      "properties": {
        "archived": {
          "type": "boolean"
        },
        "archived_at": {
          "type": "string"
        },
        "changes": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceAssistantChange"
          }
        },
        "created_at": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "marketing": {
          "$ref": "#/definitions/models.FieldServiceAssistantMarketing"
        },
        "messages": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceAssistantMessage"
          }
        },
        "pending": {
          "$ref": "#/definitions/models.FieldServiceAssistantPending"
        },
        "status": {
          "type": "string"
        },
        "support_session_id": {
          "type": "string"
        },
        "suspended": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceAssistantSuspended"
          }
        },
        "task": {
          "$ref": "#/definitions/models.FieldServiceAssistantTask"
        },
        "updated_at": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceAssistantForm": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string"
        },
        "operation": {
          "type": "string"
        },
        "path": {
          "type": "string"
        },
        "record_id": {
          "type": "string"
        },
        "submit_label": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantLink": {
      "type": "object",
      "properties": {
        "label": {
          "type": "string"
        },
        "path": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantMarketing": {
      "type": "object",
      "properties": {
        "asset_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "brand_kit_id": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "job_id": {
          "type": "string"
        },
        "job_title": {
          "type": "string"
        },
        "manifest_id": {
          "type": "string"
        },
        "project_id": {
          "type": "string"
        },
        "render_id": {
          "type": "string"
        },
        "revision": {
          "type": "integer"
        },
        "script": {
          "type": "string"
        },
        "script_job_id": {
          "type": "string"
        },
        "stage": {
          "type": "string"
        },
        "voice_id": {
          "type": "string"
        },
        "voice_name": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantMediaItem": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantMediaSelection": {
      "type": "object",
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceAssistantMediaItem"
          }
        },
        "job_id": {
          "type": "string"
        },
        "job_title": {
          "type": "string"
        },
        "max": {
          "type": "integer"
        },
        "min": {
          "type": "integer"
        },
        "project_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantMessage": {
      "type": "object",
      "properties": {
        "answer_type": {
          "type": "string"
        },
        "at": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "job_id": {
          "type": "string"
        },
        "links": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceAssistantLink"
          }
        },
        "photo_upload": {
          "type": "boolean"
        },
        "role": {
          "type": "string"
        },
        "text": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantPending": {
      "type": "object",
      "properties": {
        "approval_id": {
          "type": "string"
        },
        "choices": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceAssistantChoice"
          }
        },
        "expires_at": {
          "type": "string"
        },
        "field": {
          "type": "string"
        },
        "fields": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "form": {
          "$ref": "#/definitions/models.FieldServiceAssistantForm"
        },
        "id": {
          "type": "string"
        },
        "job_id": {
          "type": "string"
        },
        "media": {
          "$ref": "#/definitions/models.FieldServiceAssistantMediaSelection"
        },
        "message": {
          "type": "string"
        },
        "photo_upload": {
          "type": "boolean"
        },
        "record": {
          "$ref": "#/definitions/models.FieldServiceRecord"
        },
        "refresh_error": {
          "type": "string"
        },
        "refreshed_from": {
          "type": "string"
        },
        "review_changes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "summary": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceAssistantSummary"
          }
        },
        "type": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantSummary": {
      "type": "object",
      "properties": {
        "label": {
          "type": "string"
        },
        "value": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantSuspended": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "label": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAssistantTask": {
      "type": "object",
      "properties": {
        "action_params": {
          "type": "object",
          "additionalProperties": true
        },
        "execution_state": {
          "type": "string"
        },
        "expected_version": {
          "type": "integer"
        },
        "fields": {
          "type": "object",
          "additionalProperties": true
        },
        "id": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "operation": {
          "type": "string"
        },
        "queries": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        },
        "record_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAttachment": {
      "type": "object",
      "properties": {
        "asset_id": {
          "type": "string"
        },
        "media_type": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "shared_with_customer": {
          "type": "boolean"
        },
        "title": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceAutopayState": {
      "type": "object",
      "properties": {
        "attempt_id": {
          "type": "string"
        },
        "authorization_id": {
          "type": "string"
        },
        "completed_at": {
          "type": "string"
        },
        "consent_event_id": {
          "type": "string"
        },
        "error": {
          "type": "string"
        },
        "expected_amount_cents": {
          "type": "integer"
        },
        "next_attempt_at": {
          "type": "string"
        },
        "payment_method_id": {
          "type": "string"
        },
        "receipt_claimed_at": {
          "type": "string"
        },
        "receipt_sent_at": {
          "type": "string"
        },
        "receipt_status": {
          "type": "string"
        },
        "review_version": {
          "type": "integer"
        },
        "started_at": {
          "type": "string"
        },
        "status": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceBilling": {
      "type": "object",
      "properties": {
        "cancel_at_period_end": {
          "type": "boolean"
        },
        "checkout_expires_at": {
          "type": "integer"
        },
        "checkout_session_id": {
          "type": "string"
        },
        "current_period_end": {
          "type": "integer"
        },
        "price_id": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "subscription_id": {
          "type": "string"
        },
        "updated_at": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceBookingConfig": {
      "type": "object",
      "properties": {
        "buffer_minutes": {
          "type": "integer"
        },
        "end_hour": {
          "type": "integer"
        },
        "interval_minutes": {
          "type": "integer"
        },
        "min_notice_hours": {
          "type": "integer"
        },
        "mode": {
          "type": "string"
        },
        "questions": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceChecklistItem"
          }
        },
        "send_confirmations": {
          "type": "boolean"
        },
        "service_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "start_hour": {
          "type": "integer"
        },
        "timezone": {
          "type": "string"
        },
        "user_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "weekdays": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        }
      }
    },
    "models.FieldServiceBranch": {
      "type": "object",
      "properties": {
        "active": {
          "type": "boolean"
        },
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "postal_codes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "restrictions": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "service_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "models.FieldServiceCampaignRecipient": {
      "type": "object",
      "properties": {
        "attempted_at": {
          "type": "string"
        },
        "customer_id": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "error": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "sent_at": {
          "type": "string"
        },
        "status": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceChange": {
      "type": "object",
      "properties": {
        "after": {
          "type": "string"
        },
        "before": {
          "type": "string"
        },
        "field": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceChecklistItem": {
      "type": "object",
      "properties": {
        "answer": {
          "type": "string"
        },
        "completed": {
          "type": "boolean"
        },
        "id": {
          "type": "string"
        },
        "label": {
          "type": "string"
        },
        "options": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "required": {
          "type": "boolean"
        },
        "type": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceContact": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "phone": {
          "type": "string"
        },
        "role": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceCredit": {
      "type": "object",
      "properties": {
        "amount_cents": {
          "type": "integer"
        },
        "id": {
          "type": "string"
        },
        "method": {
          "type": "string"
        },
        "recorded_at": {
          "type": "string"
        },
        "recorded_by": {
          "type": "string"
        },
        "reference": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceDashboard": {
      "type": "object",
      "properties": {
        "active_jobs": {
          "type": "integer"
        },
        "credit_cents": {
          "type": "integer"
        },
        "expenses_cents": {
          "type": "integer"
        },
        "invoiced_cents": {
          "type": "integer"
        },
        "labor_cost_cents": {
          "type": "integer"
        },
        "open_estimates": {
          "type": "integer"
        },
        "open_requests": {
          "type": "integer"
        },
        "outstanding_cents": {
          "type": "integer"
        },
        "outstanding_invoices": {
          "type": "integer"
        },
        "paid_cents": {
          "type": "integer"
        },
        "scheduled_visits": {
          "type": "integer"
        },
        "status_counts": {
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "format": "int64"
            }
          }
        },
        "total_customers": {
          "type": "integer"
        },
        "total_minutes": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceEmailBlock": {
      "type": "object",
      "properties": {
        "alt": {
          "type": "string"
        },
        "text": {
          "type": "string"
        },
        "type": {
          "type": "string"
        },
        "url": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceEmailStats": {
      "type": "object",
      "properties": {
        "bounced": {
          "type": "integer"
        },
        "clicked": {
          "type": "integer"
        },
        "complained": {
          "type": "integer"
        },
        "delivered": {
          "type": "integer"
        },
        "failed": {
          "type": "integer"
        },
        "opened": {
          "type": "integer"
        },
        "sent": {
          "type": "integer"
        },
        "total": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceExpenseDraft": {
      "type": "object",
      "properties": {
        "amount_cents": {
          "type": "integer"
        },
        "currency": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "expense_date": {
          "type": "string"
        },
        "invoice_number": {
          "type": "string"
        },
        "job_reference": {
          "type": "string"
        },
        "questions": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "subtotal_cents": {
          "type": "integer"
        },
        "supplier": {
          "type": "string"
        },
        "tax_cents": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceExpenseImport": {
      "type": "object",
      "properties": {
        "asset_id": {
          "type": "string"
        },
        "draft": {
          "$ref": "#/definitions/models.FieldServiceExpenseDraft"
        },
        "error": {
          "type": "string"
        },
        "expense_id": {
          "type": "string"
        },
        "suggested_job_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceGrowth": {
      "type": "object",
      "properties": {
        "accent_color": {
          "type": "string"
        },
        "action": {
          "type": "string"
        },
        "action_count": {
          "type": "integer"
        },
        "action_tag": {
          "type": "string"
        },
        "attempted_count": {
          "type": "integer"
        },
        "audience": {
          "$ref": "#/definitions/models.FieldServiceMarketingAudience"
        },
        "body": {
          "type": "string"
        },
        "condition_catalog_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "condition_status": {
          "type": "string"
        },
        "delay_days": {
          "type": "integer"
        },
        "delay_hours": {
          "type": "integer"
        },
        "email_blocks": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceEmailBlock"
          }
        },
        "failed_count": {
          "type": "integer"
        },
        "last_run_at": {
          "type": "string"
        },
        "marketing_agent_config_hash": {
          "type": "string"
        },
        "marketing_agent_followup_days": {
          "type": "integer"
        },
        "marketing_agent_id": {
          "type": "string"
        },
        "marketing_agent_max_attempts": {
          "type": "integer"
        },
        "next_run_at": {
          "type": "string"
        },
        "preheader": {
          "type": "string"
        },
        "recipient_count": {
          "type": "integer"
        },
        "recipients": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceCampaignRecipient"
          }
        },
        "scan_offset": {
          "type": "integer"
        },
        "scheduled_by": {
          "type": "string"
        },
        "segment_status": {
          "type": "string"
        },
        "segment_tag": {
          "type": "string"
        },
        "sent_count": {
          "type": "integer"
        },
        "skipped_count": {
          "type": "integer"
        },
        "subject": {
          "type": "string"
        },
        "trigger": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceInteraction": {
      "type": "object",
      "properties": {
        "caller_number": {
          "type": "string"
        },
        "ended_at": {
          "type": "string"
        },
        "outcome": {
          "type": "string"
        },
        "recording_url": {
          "type": "string"
        },
        "started_at": {
          "type": "string"
        },
        "summary": {
          "type": "string"
        },
        "transcript": {
          "type": "string"
        },
        "urgency": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceInvoiceAllocation": {
      "type": "object",
      "properties": {
        "amount_cents": {
          "type": "integer"
        },
        "cycle_id": {
          "type": "string"
        },
        "deposit_cents": {
          "type": "integer"
        },
        "invoice_id": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "subtotal_cents": {
          "type": "integer"
        },
        "tax_cents": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceInvoiceSource": {
      "type": "object",
      "properties": {
        "amount_cents": {
          "type": "integer"
        },
        "cost_cents": {
          "type": "integer"
        },
        "cycle_id": {
          "type": "string"
        },
        "deposit_cents": {
          "type": "integer"
        },
        "estimate_id": {
          "type": "string"
        },
        "job_id": {
          "type": "string"
        },
        "job_name": {
          "type": "string"
        },
        "subtotal_cents": {
          "type": "integer"
        },
        "tax_cents": {
          "type": "integer"
        },
        "tax_rate_bps": {
          "type": "integer"
        },
        "taxable_subtotal_cents": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceLineItem": {
      "type": "object",
      "properties": {
        "catalog_id": {
          "type": "string"
        },
        "cost_cents": {
          "type": "integer"
        },
        "description": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "optional": {
          "type": "boolean"
        },
        "quantity": {
          "type": "number"
        },
        "selected": {
          "type": "boolean"
        },
        "taxable": {
          "type": "boolean"
        },
        "total_cents": {
          "type": "integer"
        },
        "unit_price_cents": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceListResponse": {
      "type": "object",
      "properties": {
        "has_more": {
          "type": "boolean"
        },
        "items": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceRecord"
          }
        }
      }
    },
    "models.FieldServiceLocation": {
      "type": "object",
      "properties": {
        "access_instructions": {
          "type": "string"
        },
        "address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "country": {
          "type": "string"
        },
        "custom_fields": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        },
        "customer_id": {
          "type": "string"
        },
        "customer_name": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "postal_code": {
          "type": "string"
        },
        "region": {
          "type": "string"
        },
        "tax_rate_bps": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceMarketing": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string"
        },
        "audience": {
          "$ref": "#/definitions/models.FieldServiceMarketingAudience"
        },
        "automation_id": {
          "type": "string"
        },
        "back_url": {
          "type": "string"
        },
        "body": {
          "type": "string"
        },
        "booked_jobs": {
          "type": "integer"
        },
        "budget_cents": {
          "type": "integer"
        },
        "call_to_action": {
          "type": "string"
        },
        "channel": {
          "type": "string"
        },
        "clicks": {
          "type": "integer"
        },
        "delay_hours": {
          "type": "integer"
        },
        "exported_at": {
          "type": "string"
        },
        "external_campaign_id": {
          "type": "string"
        },
        "followup_days": {
          "type": "integer"
        },
        "front_url": {
          "type": "string"
        },
        "goal": {
          "type": "string"
        },
        "handoff": {
          "type": "string"
        },
        "headline": {
          "type": "string"
        },
        "impressions": {
          "type": "integer"
        },
        "instructions": {
          "type": "string"
        },
        "job_id": {
          "type": "string"
        },
        "landing_url": {
          "type": "string"
        },
        "last_synced_at": {
          "type": "string"
        },
        "leads": {
          "type": "integer"
        },
        "location": {
          "type": "string"
        },
        "max_attempts": {
          "type": "integer"
        },
        "metrics_source": {
          "type": "string"
        },
        "offer": {
          "type": "string"
        },
        "optimizer_enabled": {
          "type": "boolean"
        },
        "phone_integration_id": {
          "type": "string"
        },
        "platform": {
          "type": "string"
        },
        "postcard_size": {
          "type": "string"
        },
        "provider_id": {
          "description": "These fields are not accepted as caller-authored provider outcomes.",
          "type": "string"
        },
        "provider_status": {
          "type": "string"
        },
        "published_response": {
          "type": "string"
        },
        "quiet_hours_end": {
          "type": "string"
        },
        "quiet_hours_start": {
          "type": "string"
        },
        "rating": {
          "type": "integer"
        },
        "response": {
          "type": "string"
        },
        "return_address": {
          "type": "string"
        },
        "revenue_cents": {
          "type": "integer"
        },
        "review_text": {
          "type": "string"
        },
        "review_url": {
          "type": "string"
        },
        "reviewer": {
          "type": "string"
        },
        "scheduled_at": {
          "type": "string"
        },
        "spend_cents": {
          "type": "integer"
        },
        "subject": {
          "type": "string"
        },
        "technician_name": {
          "type": "string"
        },
        "timezone": {
          "type": "string"
        },
        "tracking_phone": {
          "type": "string"
        },
        "unit_cost_cents": {
          "type": "integer"
        },
        "utm_campaign": {
          "type": "string"
        },
        "utm_medium": {
          "type": "string"
        },
        "utm_source": {
          "type": "string"
        },
        "workflow": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceMarketingAudience": {
      "type": "object",
      "properties": {
        "city": {
          "type": "string"
        },
        "customer_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "inactive_days": {
          "type": "integer"
        },
        "min_equipment_age": {
          "type": "integer"
        },
        "min_home_age": {
          "type": "integer"
        },
        "postal_codes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "property_type": {
          "type": "string"
        },
        "segment": {
          "type": "string"
        },
        "service_type": {
          "type": "string"
        },
        "source": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "tag": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceMembership": {
      "type": "object",
      "properties": {
        "benefits": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "plan": {
          "type": "string"
        },
        "renews_at": {
          "type": "string"
        },
        "service_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "models.FieldServiceNotificationRecipient": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "status": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceNotificationState": {
      "type": "object",
      "properties": {
        "body": {
          "type": "string"
        },
        "cursor": {
          "type": "integer"
        },
        "event_id": {
          "type": "string"
        },
        "pending": {
          "type": "boolean"
        },
        "queued_at": {
          "type": "string"
        },
        "recipients": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceNotificationRecipient"
          }
        },
        "subject": {
          "type": "string"
        }
      }
    },
    "models.FieldServicePayment": {
      "type": "object",
      "properties": {
        "amount_cents": {
          "type": "integer"
        },
        "id": {
          "type": "string"
        },
        "method": {
          "type": "string"
        },
        "recorded_at": {
          "type": "string"
        },
        "recorded_by": {
          "type": "string"
        },
        "reference": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceProperty": {
      "type": "object",
      "properties": {
        "access_instructions": {
          "type": "string"
        },
        "address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "country": {
          "type": "string"
        },
        "custom_fields": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        },
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "postal_code": {
          "type": "string"
        },
        "region": {
          "type": "string"
        },
        "tax_rate_bps": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceRecord": {
      "type": "object",
      "properties": {
        "accounting": {
          "$ref": "#/definitions/models.FieldServiceAccounting"
        },
        "address": {
          "type": "string"
        },
        "amount_cents": {
          "type": "integer"
        },
        "annual_revenue_target_cents": {
          "type": "integer"
        },
        "application_method": {
          "type": "string"
        },
        "application_rate": {
          "type": "string"
        },
        "applicator_license": {
          "type": "string"
        },
        "approved_at": {
          "type": "string"
        },
        "archived": {
          "type": "boolean"
        },
        "assigned_user_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "attachments": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceAttachment"
          }
        },
        "autopay": {
          "$ref": "#/definitions/models.FieldServiceAutopayState"
        },
        "balance_cents": {
          "type": "integer"
        },
        "billed_cents": {
          "type": "integer"
        },
        "booking": {
          "$ref": "#/definitions/models.FieldServiceBookingConfig"
        },
        "business_license": {
          "type": "string"
        },
        "category": {
          "type": "string"
        },
        "change_estimate_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "checklist_items": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceChecklistItem"
          }
        },
        "checklist_warnings": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "chemical": {
          "type": "string"
        },
        "color": {
          "type": "string"
        },
        "company": {
          "type": "string"
        },
        "completed_at": {
          "type": "string"
        },
        "concentration": {
          "type": "string"
        },
        "contacts": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceContact"
          }
        },
        "converted_id": {
          "type": "string"
        },
        "converted_kind": {
          "type": "string"
        },
        "cost_cents": {
          "type": "integer"
        },
        "created_at": {
          "type": "string"
        },
        "created_by": {
          "type": "string"
        },
        "credit_cents": {
          "type": "integer"
        },
        "credits": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceCredit"
          }
        },
        "crew_id": {
          "type": "string"
        },
        "crew_name": {
          "type": "string"
        },
        "currency": {
          "type": "string"
        },
        "custom_fields": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        },
        "customer_id": {
          "type": "string"
        },
        "deposit_cents": {
          "type": "integer"
        },
        "deposit_invoice_id": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "discount_cents": {
          "type": "integer"
        },
        "due_at": {
          "type": "string"
        },
        "duration_minutes": {
          "type": "integer"
        },
        "email": {
          "type": "string"
        },
        "email_delivery_id": {
          "type": "string"
        },
        "ends_at": {
          "type": "string"
        },
        "estimate_id": {
          "type": "string"
        },
        "expense_import": {
          "$ref": "#/definitions/models.FieldServiceExpenseImport"
        },
        "growth": {
          "$ref": "#/definitions/models.FieldServiceGrowth"
        },
        "history": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceActivity"
          }
        },
        "hourly_cost_cents": {
          "type": "integer"
        },
        "id": {
          "type": "string"
        },
        "interaction": {
          "$ref": "#/definitions/models.FieldServiceInteraction"
        },
        "invoice_allocations": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceInvoiceAllocation"
          }
        },
        "invoice_sources": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceInvoiceSource"
          }
        },
        "job_id": {
          "type": "string"
        },
        "job_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "kind": {
          "type": "string"
        },
        "lead_source": {
          "type": "string"
        },
        "line_items": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceLineItem"
          }
        },
        "logo_data_url": {
          "type": "string"
        },
        "marketing": {
          "$ref": "#/definitions/models.FieldServiceMarketing"
        },
        "marketing_opt_in": {
          "type": "boolean"
        },
        "marketing_unsubscribed": {
          "type": "boolean"
        },
        "membership": {
          "$ref": "#/definitions/models.FieldServiceMembership"
        },
        "mixture": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "next_service_at": {
          "type": "string"
        },
        "notes": {
          "type": "string"
        },
        "notifications": {
          "$ref": "#/definitions/models.FieldServiceNotificationState"
        },
        "notify_assigned_users": {
          "type": "boolean"
        },
        "notify_customer": {
          "type": "boolean"
        },
        "paid_cents": {
          "type": "integer"
        },
        "payment_attempt_id": {
          "type": "string"
        },
        "payments": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServicePayment"
          }
        },
        "payroll_export_id": {
          "type": "string"
        },
        "phone": {
          "type": "string"
        },
        "pipeline_stage_id": {
          "type": "string"
        },
        "properties": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceProperty"
          }
        },
        "property_id": {
          "type": "string"
        },
        "recipient_email": {
          "type": "string"
        },
        "recurrence": {
          "$ref": "#/definitions/models.FieldServiceRecurrence"
        },
        "referral_id": {
          "type": "string"
        },
        "referral_reward": {
          "$ref": "#/definitions/models.FieldServiceReferralReward"
        },
        "reimbursable": {
          "type": "boolean"
        },
        "related_job_id": {
          "type": "string"
        },
        "request_id": {
          "type": "string"
        },
        "routing": {
          "$ref": "#/definitions/models.FieldServiceRoutingProposal"
        },
        "schedule_type": {
          "type": "string"
        },
        "scheduling": {
          "$ref": "#/definitions/models.FieldServiceSchedulingConfig"
        },
        "sent_at": {
          "type": "string"
        },
        "signature": {
          "type": "string"
        },
        "sort_order": {
          "type": "integer"
        },
        "source_id": {
          "type": "string"
        },
        "starts_at": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "subtotal_cents": {
          "type": "integer"
        },
        "tags": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "tax_cents": {
          "type": "integer"
        },
        "tax_rate_bps": {
          "type": "integer"
        },
        "terms": {
          "type": "string"
        },
        "timezone": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "total_cents": {
          "type": "integer"
        },
        "unbilled_cents": {
          "type": "integer"
        },
        "unit_price_cents": {
          "type": "integer"
        },
        "upcoming_service_dates": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "updated_at": {
          "type": "string"
        },
        "user_id": {
          "type": "string"
        },
        "vendor": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        },
        "visit_id": {
          "type": "string"
        },
        "weather": {
          "type": "string"
        },
        "webhook": {
          "$ref": "#/definitions/models.FieldServiceWebhook"
        },
        "webhook_event": {
          "$ref": "#/definitions/models.FieldServiceWebhookEvent"
        }
      }
    },
    "models.FieldServiceRecurrence": {
      "type": "object",
      "properties": {
        "billing_frequency": {
          "type": "string"
        },
        "billing_mode": {
          "type": "string"
        },
        "count": {
          "type": "integer"
        },
        "frequency": {
          "type": "string"
        },
        "interval": {
          "type": "integer"
        }
      }
    },
    "models.FieldServiceReferralReward": {
      "type": "object",
      "properties": {
        "applied_invoice_id": {
          "type": "string"
        },
        "percent_bps": {
          "type": "integer"
        },
        "source_invoice_id": {
          "type": "string"
        },
        "source_job_value_cents": {
          "type": "integer"
        },
        "type": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceRouteExclusion": {
      "type": "object",
      "properties": {
        "reason": {
          "type": "string"
        },
        "visit_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceRouteGroup": {
      "type": "object",
      "properties": {
        "date": {
          "type": "string"
        },
        "distance_meters": {
          "type": "integer"
        },
        "stops": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceRouteStop"
          }
        },
        "travel_seconds": {
          "type": "integer"
        },
        "user_id": {
          "type": "string"
        },
        "user_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "models.FieldServiceRouteStop": {
      "type": "object",
      "properties": {
        "address": {
          "type": "string"
        },
        "applied": {
          "type": "boolean"
        },
        "ends_at": {
          "type": "string"
        },
        "original_ends_at": {
          "type": "string"
        },
        "original_starts_at": {
          "type": "string"
        },
        "original_version": {
          "type": "integer"
        },
        "starts_at": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "travel_seconds": {
          "type": "integer"
        },
        "visit_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceRoutingProposal": {
      "type": "object",
      "properties": {
        "applied_at": {
          "type": "string"
        },
        "excluded": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceRouteExclusion"
          }
        },
        "expires_at": {
          "type": "string"
        },
        "groups": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceRouteGroup"
          }
        },
        "timezone": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceSchedulingConfig": {
      "type": "object",
      "properties": {
        "branches": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceBranch"
          }
        },
        "services": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceServiceProfile"
          }
        },
        "staff": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceStaffProfile"
          }
        }
      }
    },
    "models.FieldServiceServiceProfile": {
      "type": "object",
      "properties": {
        "branch_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "required_skills": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "service_id": {
          "type": "string"
        },
        "service_type": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceStaffProfile": {
      "type": "object",
      "properties": {
        "branch_ids": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "on_call": {
          "type": "boolean"
        },
        "on_call_contact": {
          "type": "string"
        },
        "postal_codes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "skills": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "user_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceWebhook": {
      "type": "object",
      "properties": {
        "application_id": {
          "type": "string"
        },
        "events": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "url": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceWebhookEvent": {
      "type": "object",
      "properties": {
        "application_id": {
          "type": "string"
        },
        "event": {
          "type": "string"
        },
        "event_id": {
          "type": "string"
        },
        "next_attempt_at": {
          "type": "string"
        },
        "pending": {
          "type": "boolean"
        },
        "resource_id": {
          "type": "string"
        },
        "targets": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceWebhookTarget"
          }
        }
      }
    },
    "models.FieldServiceWebhookTarget": {
      "type": "object",
      "properties": {
        "attempts": {
          "type": "integer"
        },
        "last_error": {
          "type": "string"
        },
        "last_status_code": {
          "type": "integer"
        },
        "next_attempt_at": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "subscription_id": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceWebsiteContent": {
      "type": "object",
      "properties": {
        "accent_color": {
          "type": "string"
        },
        "booking_url": {
          "type": "string"
        },
        "business_name": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "headline": {
          "type": "string"
        },
        "introduction": {
          "type": "string"
        },
        "pages": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceWebsitePage"
          }
        },
        "phone": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceWebsitePage": {
      "type": "object",
      "properties": {
        "sections": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.FieldServiceWebsiteSection"
          }
        },
        "seo_description": {
          "type": "string"
        },
        "seo_title": {
          "type": "string"
        },
        "slug": {
          "type": "string"
        },
        "title": {
          "type": "string"
        }
      }
    },
    "models.FieldServiceWebsiteSection": {
      "type": "object",
      "properties": {
        "asset_id": {
          "type": "string"
        },
        "body": {
          "type": "string"
        },
        "image_url": {
          "type": "string"
        },
        "job_id": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "type": {
          "type": "string"
        }
      }
    },
    "models.IntegrationOAuthExchangeRequest": {
      "type": "object",
      "required": [
        "code",
        "state"
      ],
      "properties": {
        "code": {
          "type": "string"
        },
        "provider_callback": {
          "$ref": "#/definitions/models.IntegrationOAuthProviderCallback"
        },
        "state": {
          "type": "string"
        }
      }
    },
    "models.IntegrationOAuthProviderCallback": {
      "type": "object",
      "properties": {
        "domain": {
          "type": "string"
        },
        "hmac": {
          "type": "string"
        },
        "host": {
          "type": "string"
        },
        "realm_id": {
          "type": "string"
        },
        "shop": {
          "type": "string"
        },
        "state": {
          "type": "string"
        },
        "timestamp": {
          "type": "string"
        },
        "user": {
          "type": "string"
        }
      }
    },
    "models.PartnerApp": {
      "type": "object",
      "properties": {
        "app_id": {
          "type": "string"
        },
        "authorization_version": {
          "type": "integer"
        },
        "client_id": {
          "type": "string"
        },
        "created_at": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "has_test_credentials": {
          "type": "boolean"
        },
        "instructions_url": {
          "type": "string"
        },
        "logo_url": {
          "type": "string"
        },
        "marketing_url": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "oauth_client_id": {
          "type": "string"
        },
        "owner_user_id": {
          "type": "string"
        },
        "privacy_policy_url": {
          "type": "string"
        },
        "redirect_uris": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "review_checks": {
          "$ref": "#/definitions/models.PartnerReviewChecks"
        },
        "review_notes": {
          "type": "string"
        },
        "review_notification_sent_at": {
          "type": "string"
        },
        "reviewed_at": {
          "type": "string"
        },
        "reviewed_by": {
          "type": "string"
        },
        "revision": {
          "type": "integer"
        },
        "scopes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "servicekeel_reference_confirmed": {
          "type": "boolean"
        },
        "status": {
          "$ref": "#/definitions/models.PartnerAppStatus"
        },
        "submitted_at": {
          "type": "string"
        },
        "support_url": {
          "type": "string"
        },
        "terms_url": {
          "type": "string"
        },
        "updated_at": {
          "type": "string"
        },
        "website_url": {
          "type": "string"
        }
      }
    },
    "models.PartnerAppStatus": {
      "type": "string",
      "enum": [
        "draft",
        "submitted",
        "approved",
        "rejected",
        "revoked"
      ],
      "x-enum-varnames": [
        "PartnerAppStatusDraft",
        "PartnerAppStatusSubmitted",
        "PartnerAppStatusApproved",
        "PartnerAppStatusRejected",
        "PartnerAppStatusRevoked"
      ]
    },
    "models.PartnerOAuthGrant": {
      "type": "object",
      "properties": {
        "app_id": {
          "type": "string"
        },
        "app_name": {
          "type": "string"
        },
        "created_at": {
          "type": "string"
        },
        "expires_at": {
          "type": "string"
        },
        "grant_id": {
          "type": "string"
        },
        "oauth_client_id": {
          "type": "string"
        },
        "revoked": {
          "type": "boolean"
        },
        "scopes": {
          "description": "Scopes freeze explicit consent; TokenScopes may only narrow during refresh.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "token_scopes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "updated_at": {
          "type": "string"
        }
      }
    },
    "models.PartnerProfile": {
      "type": "object",
      "properties": {
        "client_id": {
          "type": "string"
        },
        "company_name": {
          "type": "string"
        },
        "company_website": {
          "type": "string"
        },
        "created_at": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "first_name": {
          "type": "string"
        },
        "last_name": {
          "type": "string"
        },
        "owner_user_id": {
          "type": "string"
        },
        "updated_at": {
          "type": "string"
        }
      }
    },
    "models.PartnerReviewChecks": {
      "type": "object",
      "properties": {
        "instructions_url_verified": {
          "type": "boolean"
        },
        "marketing_url_verified": {
          "type": "boolean"
        },
        "servicekeel_reference_verified": {
          "type": "boolean"
        },
        "test_account_verified": {
          "type": "boolean"
        }
      }
    },
    "models.PartnerTestCredentials": {
      "type": "object",
      "properties": {
        "login_url": {
          "type": "string"
        },
        "notes": {
          "type": "string"
        },
        "password": {
          "type": "string"
        },
        "username": {
          "type": "string"
        }
      }
    },
    "models.PhoneAttachment": {
      "type": "object",
      "properties": {
        "content_type": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        }
      }
    },
    "models.PhoneAttachmentInput": {
      "type": "object",
      "properties": {
        "content_type": {
          "type": "string"
        },
        "data": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        "name": {
          "type": "string"
        }
      }
    },
    "models.PhoneConnectInput": {
      "type": "object",
      "properties": {
        "access_token": {
          "type": "string"
        },
        "account_id": {
          "type": "string"
        },
        "api_key": {
          "type": "string"
        },
        "api_secret": {
          "type": "string"
        },
        "high_volume_enabled": {
          "type": "boolean"
        },
        "webhook_secret": {
          "type": "string"
        }
      }
    },
    "models.PhoneConnection": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string"
        },
        "automation_enabled": {
          "type": "boolean"
        },
        "booking_form_id": {
          "type": "string"
        },
        "booking_reminder_hours": {
          "type": "integer"
        },
        "health_checked_at": {
          "type": "string"
        },
        "high_volume_enabled": {
          "type": "boolean"
        },
        "incoming_error": {
          "type": "string"
        },
        "incoming_status": {
          "type": "string"
        },
        "integration_id": {
          "type": "string"
        },
        "mms_enabled": {
          "type": "boolean"
        },
        "number_id": {
          "type": "string"
        },
        "oauth_available": {
          "type": "boolean"
        },
        "phone_number": {
          "type": "string"
        },
        "provider": {
          "type": "string"
        },
        "recovery_supported": {
          "type": "boolean"
        },
        "status": {
          "type": "string"
        },
        "subscription_expires_at": {
          "type": "string"
        },
        "sync_in_progress": {
          "type": "boolean"
        },
        "synced_through": {
          "type": "string"
        },
        "voice_device_id": {
          "type": "string"
        },
        "voice_enabled": {
          "type": "boolean"
        },
        "voice_handoff_number": {
          "type": "string"
        },
        "voice_instructions": {
          "type": "string"
        },
        "voice_status": {
          "type": "string"
        },
        "webhook_configured": {
          "type": "boolean"
        },
        "webhook_url": {
          "type": "string"
        }
      }
    },
    "models.PhoneMessage": {
      "type": "object",
      "properties": {
        "attachments": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.PhoneAttachment"
          }
        },
        "automated": {
          "type": "boolean"
        },
        "body": {
          "type": "string"
        },
        "conversation_id": {
          "type": "string"
        },
        "created_at": {
          "type": "string"
        },
        "direction": {
          "type": "string"
        },
        "from": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "integration_id": {
          "type": "string"
        },
        "provider": {
          "type": "string"
        },
        "provider_message_id": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "to": {
          "type": "string"
        },
        "updated_at": {
          "type": "string"
        }
      }
    },
    "models.PhoneSendInput": {
      "type": "object",
      "properties": {
        "attachments": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/models.PhoneAttachmentInput"
          }
        },
        "body": {
          "type": "string"
        },
        "consent_confirmed": {
          "type": "boolean"
        },
        "idempotency_key": {
          "type": "string"
        },
        "integration_id": {
          "type": "string"
        },
        "to": {
          "type": "string"
        }
      }
    }
  },
  "x-tagGroups": [
    {
      "name": "Field Service",
      "tags": [
        "Customers",
        "Locations",
        "Requests",
        "Estimates",
        "Jobs",
        "Visits",
        "Recurring services",
        "Scheduling",
        "Routing",
        "Services",
        "Service catalog",
        "Equipment",
        "Memberships",
        "Staff",
        "Crews",
        "Notes",
        "Checklists",
        "Chemicals",
        "Pipeline stages"
      ]
    },
    {
      "name": "Finance",
      "tags": [
        "Invoices",
        "Payments",
        "Expenses",
        "Time entries"
      ]
    },
    {
      "name": "Communications",
      "tags": [
        "Call records",
        "Phone",
        "Email"
      ]
    },
    {
      "name": "Marketing",
      "tags": [
        "Campaigns",
        "Automations",
        "Review requests",
        "Referrals",
        "Website",
        "Marketing workspace",
        "Marketing API"
      ]
    },
    {
      "name": "Integrations",
      "tags": [
        "Partner OAuth",
        "Partner apps",
        "Partner account",
        "Integration connections",
        "Events",
        "Webhooks",
        "Developer webhooks",
        "QuickBooks",
        "Gusto"
      ]
    },
    {
      "name": "Customer access",
      "tags": [
        "Booking forms",
        "Public booking",
        "Customer portal",
        "Public calendar",
        "Public referrals",
        "Unsubscribe",
        "Public website"
      ]
    },
    {
      "name": "Assistant and files",
      "tags": [
        "Assistant",
        "AI tools",
        "Files"
      ]
    },
    {
      "name": "Administration",
      "tags": [
        "Account billing",
        "Settings",
        "Reporting",
        "Setup",
        "GraphQL"
      ]
    }
  ],
  "tags": [
    {
      "name": "Customers"
    },
    {
      "name": "Locations"
    },
    {
      "name": "Requests"
    },
    {
      "name": "Estimates"
    },
    {
      "name": "Jobs"
    },
    {
      "name": "Visits"
    },
    {
      "name": "Recurring services"
    },
    {
      "name": "Scheduling"
    },
    {
      "name": "Routing"
    },
    {
      "name": "Services"
    },
    {
      "name": "Service catalog"
    },
    {
      "name": "Equipment"
    },
    {
      "name": "Memberships"
    },
    {
      "name": "Staff"
    },
    {
      "name": "Crews"
    },
    {
      "name": "Notes"
    },
    {
      "name": "Checklists"
    },
    {
      "name": "Chemicals"
    },
    {
      "name": "Pipeline stages"
    },
    {
      "name": "Invoices"
    },
    {
      "name": "Payments"
    },
    {
      "name": "Expenses"
    },
    {
      "name": "Time entries"
    },
    {
      "name": "Call records"
    },
    {
      "name": "Phone"
    },
    {
      "name": "Email"
    },
    {
      "name": "Campaigns"
    },
    {
      "name": "Automations"
    },
    {
      "name": "Review requests"
    },
    {
      "name": "Referrals"
    },
    {
      "name": "Website"
    },
    {
      "name": "Marketing workspace"
    },
    {
      "name": "Marketing API"
    },
    {
      "name": "Partner OAuth"
    },
    {
      "name": "Partner apps"
    },
    {
      "name": "Partner account"
    },
    {
      "name": "Integration connections"
    },
    {
      "name": "Events"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Developer webhooks"
    },
    {
      "name": "QuickBooks"
    },
    {
      "name": "Gusto"
    },
    {
      "name": "Booking forms"
    },
    {
      "name": "Public booking"
    },
    {
      "name": "Customer portal"
    },
    {
      "name": "Public calendar"
    },
    {
      "name": "Public referrals"
    },
    {
      "name": "Unsubscribe"
    },
    {
      "name": "Public website"
    },
    {
      "name": "Assistant"
    },
    {
      "name": "AI tools"
    },
    {
      "name": "Files"
    },
    {
      "name": "Account billing"
    },
    {
      "name": "Settings"
    },
    {
      "name": "Reporting"
    },
    {
      "name": "Setup"
    },
    {
      "name": "GraphQL"
    }
  ]
}
