{
  "openapi": "3.0.1",
  "info": {
    "title": "AI Drone Roof Inspection Report — Findings & Repair Scope",
    "description": "Turn drone and ground roof photos into a per-building inspection report: defect findings per photo, condition grade, repair scope, materials and labor from your price list, a Markdown report and contact sheet. Rules mode or Claude vision. Pay per photo and per building.",
    "version": "0.1",
    "x-build-id": "BbSGdQ8YGn2OyfyhS"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/owlabee~drone-roof-inspection-report/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-owlabee-drone-roof-inspection-report",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/owlabee~drone-roof-inspection-report/runs": {
      "post": {
        "operationId": "runs-sync-owlabee-drone-roof-inspection-report",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/owlabee~drone-roof-inspection-report/run-sync": {
      "post": {
        "operationId": "run-sync-owlabee-drone-roof-inspection-report",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "imageUrls"
        ],
        "properties": {
          "imageUrls": {
            "title": "Roof photo URLs",
            "minItems": 1,
            "uniqueItems": true,
            "type": "array",
            "description": "Roof photos to inspect, drone or ground, one per line, in the order you want them numbered (the first line is Photo 1): public http(s) image URLs, Apify key-value-store references in the form kvs://<storeId>/<key> (use kvs://default/<key> for this run's own store), or data: URLs with inline base64 images. JPEG, PNG, WebP, GIF and TIFF are supported; HEIC usually cannot be decoded on the platform, convert it to JPEG first. EXIF time, GPS and camera are read from each file. The three prefilled photos (public-domain U.S. government photos from Wikimedia Commons) are the small, fast set used by Apify's daily automated test.",
            "items": {
              "type": "string"
            }
          },
          "buildings": {
            "title": "Building labels (optional)",
            "type": "array",
            "description": "Tell the Actor which photos belong to which building, as a JSON array of objects: `label` (e.g. \"Building 12\" or \"Clubhouse\"), `photos` (photo numbers and ranges like \"1-7, 158\"), `stories` (integer, used for the height charge) and `units` (free text, e.g. \"12-17\"). Photos not covered by a label are grouped automatically. Without labels, photos are grouped by GPS distance when they carry GPS, otherwise by gaps in the capture time. Example: [{\"label\": \"Building 1\", \"photos\": \"1-12\", \"stories\": 2, \"units\": \"101-108\"}].",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "title": "Label",
                  "type": "string",
                  "description": "Building name shown in the report."
                },
                "photos": {
                  "title": "Photos",
                  "type": "string",
                  "description": "Photo numbers and ranges, e.g. \"1-7, 158\"."
                },
                "stories": {
                  "title": "Stories",
                  "type": "integer",
                  "description": "Number of stories, used for the height charge."
                },
                "units": {
                  "title": "Units",
                  "type": "string",
                  "description": "Units or addresses covered by this roof, free text."
                }
              },
              "required": [
                "label"
              ]
            }
          },
          "analyzer": {
            "title": "Analyzer",
            "enum": [
              "rules",
              "byok",
              "hosted"
            ],
            "type": "string",
            "description": "Leave empty for the default (the Console prefills `rules` so Apify's daily automated test never calls a model; clear it or pick a mode): Claude vision on the Actor's own key (`hosted`) when the Actor has its model key configured, otherwise the free-of-model rules analyzer. Set `rules` for a model-free run (EXIF index, grouping into buildings, contact sheet, empty findings; no key), or `byok` to use your own Anthropic API key (tokens billed by Anthropic to you). In model modes a model problem never fails the run: the photo gets the rules output, `analysisMethod: \"rules-fallback\"` with an `analysisWarning`, and is charged the rules price."
          },
          "anthropicApiKey": {
            "title": "Anthropic API key (byok mode)",
            "type": "string",
            "description": "Your Anthropic API key, used only when Analyzer is `byok`. Stored encrypted as a secret and sent only to api.anthropic.com. Get one at console.anthropic.com."
          },
          "modelTier": {
            "title": "Model tier (byok and hosted modes)",
            "enum": [
              "fast",
              "balanced",
              "best"
            ],
            "type": "string",
            "description": "`fast` (default): Claude Haiku 5.5, cheapest and quick, fine for clear drone overviews and close-ups. `balanced`: Claude Sonnet 5.5, better on subtle defects (granule loss, lifted tabs, flashing details). `best`: Claude Opus 5.5 for the hardest photo sets, available only with your own key (analyzer `byok`); a hosted run with `best` is rejected before any charge. In hosted mode the tier sets the per-photo and per-building prices ($0.02 + $0.10 for fast, $0.08 + $0.30 for balanced); in byok mode the tier only changes what Anthropic bills you.",
            "default": "fast"
          },
          "instructions": {
            "title": "Inspection instructions (model modes, optional)",
            "type": "string",
            "description": "Extra guidance for the model, in plain language: the roof system (\"architectural asphalt shingles over plywood\", \"TPO membrane on the flat sections\"), what to ignore (\"skip the solar panels\", \"photos 40-45 are the parking lot\"), or local terminology (\"we call the cap shingles 'ridge'\"). Ignored by the rules analyzer."
          },
          "priceList": {
            "title": "Price list (optional)",
            "type": "object",
            "description": "Your own prices for the deterministic estimate, as JSON: `materials` [{name, packSize, perUnit, appliesTo, priceUsd}] where `perUnit` is packs per unit of the repair action named in `appliesTo` (shingle-zone-repair, cap-repair, flashing-repair, boot-or-vent-replacement…), rounded up to whole packs per building; `laborBundles` [{hours, priceUsd}] (the largest bundle whose hours do not exceed the estimated hours, never below the smallest is used); `heightCharges` [{stories, priceUsd}]; `laborRounding` 'down' (default: largest bundle at or below the estimated hours, as in the reference report) or 'up' (smallest bundle that covers them); `hoursPerZone` (default 0.4); `zoneSf` (repair zone size in square feet, default 25); `currency`. Leave empty to use the built-in EXAMPLE price list, which is a placeholder and not market pricing: replace it with yours before quoting. All quantities are photo estimates and are field-verified on the roof."
          },
          "company": {
            "title": "Company and property (optional)",
            "type": "object",
            "description": "Header details for the report, as JSON: `name`, `email`, `phone`, `website`, `inspector`, `propertyName`, `propertyAddress`, `inspectionDate`. All optional; missing fields are left out of the report."
          },
          "maxPhotos": {
            "title": "Max photos",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "Upper bound on photos processed in this run; photos beyond it are skipped and not charged. Use it together with the run's maximum total charge to control spend.",
            "default": 200
          },
          "photosPerModelCall": {
            "title": "Photos per model call",
            "minimum": 1,
            "maximum": 8,
            "type": "integer",
            "description": "How many photos of the same building are sent to the model in one call (model modes). Larger batches give the model more context per call; smaller batches cost less per call when the spending limit is tight.",
            "default": 4
          },
          "groupingDistanceMeters": {
            "title": "Grouping distance (meters)",
            "minimum": 5,
            "maximum": 500,
            "type": "number",
            "description": "Photos with GPS whose positions are within this distance of a building's photos are grouped into the same building. Used only for photos not covered by Building labels.",
            "default": 25
          },
          "groupingGapMinutes": {
            "title": "Grouping time gap (minutes)",
            "minimum": 1,
            "maximum": 240,
            "type": "number",
            "description": "For photos without GPS, a gap in capture time longer than this starts a new building. Used only for photos not covered by Building labels.",
            "default": 10
          },
          "maxConcurrency": {
            "title": "Max concurrency",
            "minimum": 1,
            "maximum": 4,
            "type": "integer",
            "description": "Photos downloaded and decoded, and model calls made, in parallel. Large photos need memory: keep it at 2 on 1 GB.",
            "default": 2
          },
          "maxFileSizeMb": {
            "title": "Max file size (MB)",
            "minimum": 1,
            "maximum": 50,
            "type": "integer",
            "description": "Photos larger than this are rejected with a TOO_LARGE error item instead of being downloaded. Raise it up to 50 MB for full-resolution drone photos.",
            "default": 25
          },
          "downloadTimeoutSecs": {
            "title": "Download timeout (seconds)",
            "minimum": 5,
            "maximum": 120,
            "type": "integer",
            "description": "Maximum time to download one photo before it is recorded as a TIMEOUT error item.",
            "default": 20
          },
          "includeContactSheet": {
            "title": "Write a contact sheet",
            "type": "boolean",
            "description": "Also write CONTACT_SHEET to the key-value store: a JPEG montage of the first 100 photos labeled with their photo numbers, handy for the field crew.",
            "default": true
          },
          "webhookUrl": {
            "title": "Webhook URL (optional)",
            "pattern": "^https?://.+",
            "type": "string",
            "description": "If set, the Actor POSTs a JSON summary of the run (photos, buildings, errors, findings, model token totals, charges) to this URL when it finishes — 10 s timeout, 2 retries. Failures are logged, never fatal."
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}