Documentation

Get an order

GET /api/v1/reports/orders/:id and /status: the whole order with its measurements and file links, plus a resolved status block for your code.

API reference

Base URL: https://api.roofquery.com. Authenticate with Authorization: Bearer <your key> — except the file routes (report and storm report files), which take no key.

Copies this whole page as Markdown, so you can paste it into an assistant and have it write the integration. Nothing secret goes on the clipboard — your keys aren't included.

GET /api/v1/reports/orders/:id

The whole order. Identical in shape to the webhook body and to /status, so one handler covers all three — that's the point of the shared payload builder. Once the report is delivered, totals, structures[] and files[] fill in. Field-by-field detail is under Report data.

There is deliberately no paginated list endpoint. State is driven by webhooks, and a list endpoint just invites the polling the webhook stream already replaces.

Request
curl -H "Authorization: Bearer rq_live_sk_..." \
  "https://api.roofquery.com/api/v1/reports/orders/rq_ord_8f3a21c9d4e07b16"
Response

Delivered order, trimmed — structures[].faces[] continues for every facet:

{
  "orderId": "rq_ord_8f3a21c9d4e07b16",
  "status": "completed",
  "sandbox": false,
  "createdAt": "2026-08-12T14:03:11.000Z",
  "completedAt": "2026-08-12T16:01:44.000Z",
  "property": {
    "address": "6515 Turnbridge Pl, Prospect, KY 40059, USA",
    "latitude": 38.3526391,
    "longitude": -85.6208045
  },
  "report": {
    "type": "residential",
    "brand": null,
    "scope": "primary_and_garage",
    "scopeLabel": "Primary structure and detached garage",
    "format": "xml",
    "esxIncluded": true
  },
  "billing": {
    "amount": 15, "currency": "usd", "method": "account_balance",
    "holdReference": "rq_ord_8f3a21c9d4e07b16",
    "items": [ { "item": "report", "amount": 10 }, { "item": "esx", "amount": 5 } ],
    "refunded": false, "released": false
  },
  "revisions": [],
  "totals": {
    "structureCount": 1,
    "totalAreaSqFt": 5597.29,
    "totalSquares": 55.97,
    "totalEdgeLengthFt": 1234.58,
    "facetCount": 33,
    "predominantPitch": "12/12",
    "edgeTotals": {
      "EAVE":      { "lengthFt": 276.31, "count": 27 },
      "RAKE":      { "lengthFt": 169.20, "count": 13 },
      "RIDGE":     { "lengthFt": 114.58, "count": 12 },
      "HIP":       { "lengthFt": 304.25, "count": 20 },
      "VALLEY":    { "lengthFt": 237.30, "count": 17 },
      "STEP_FLASHING": { "lengthFt": 72.81, "count":  9 },
      "WALL_FLASHING": { "lengthFt": 55.45, "count": 14 },
      "PARAPET":   { "lengthFt":   0.00, "count":  0 },
      "BEND":      { "lengthFt":   4.68, "count":  1 },
      "OTHER":     { "lengthFt":   0.00, "count":  0 }
    },
    "hipsAndRidgesLengthFt": 418.83,
    "pitchAreas": { "12/12": 5204.08, "5/12": 257.97, "11/12": 66.42, "10/12": 48.56, "3/12": 20.26 },
    "complexity": "complex",
    "estimatedAtticSqFt": 4023.89,
    "diagrams": {
      "outline": "https://api.roofquery.com/api/v1/reports/orders/rq_ord_8f3a21c9d4e07b16/diagrams/all/outline",
      "area":    "https://api.roofquery.com/api/v1/reports/orders/rq_ord_8f3a21c9d4e07b16/diagrams/all/area",
      "pitch":   "https://api.roofquery.com/api/v1/reports/orders/rq_ord_8f3a21c9d4e07b16/diagrams/all/pitch",
      "lengths": "https://api.roofquery.com/api/v1/reports/orders/rq_ord_8f3a21c9d4e07b16/diagrams/all/lengths"
    }
  },
  "structures": [
    {
      "index": 1,
      "facetCount": 33,
      "totalAreaSqFt": 5597.29,
      "totalSquares": 55.97,
      "predominantPitch": "12/12",
      "edgeTotals": { "...": "same ten categories, for this building" },
      "hipsAndRidgesLengthFt": 418.83,
      "complexity": "complex",
      "estimatedAtticSqFt": 4023.89,
      "diagrams": { "...": "same four types, addressed to structure 1" },
      "faces": [
        {
          "id": "F1",
          "areaSqFt": 338.51,
          "pitch": "12/12",
          "edges": [
            { "type": "RAKE",   "lengthFt": 23.49 },
            { "type": "EAVE",   "lengthFt":  6.11 },
            { "type": "VALLEY", "lengthFt": 28.76 },
            { "type": "RIDGE",  "lengthFt": 22.72 }
          ]
        }
      ]
    }
  ],
  "files": [
    { "name": "report.pdf", "url": "https://api.roofquery.com/api/v1/reports/orders/rq_ord_8f3a21c9d4e07b16/pdf" },
    { "name": "report.xml", "url": "https://api.roofquery.com/api/v1/reports/orders/rq_ord_8f3a21c9d4e07b16/xml" },
    { "name": "report.esx", "url": "https://api.roofquery.com/api/v1/reports/orders/rq_ord_8f3a21c9d4e07b16/esx" }
  ]
}

report.esx is in files[] only when report.esxIncluded is true: the order was placed with ESX, has had it added since, or was placed before ESX became an option. billing.items lists the report and, when paid for, the ESX. report.brand is the contractor the order was placed for ({ "id", "companyName" }), or null when it carries your account's own branding.

Errors

An unknown order id and an order belonging to someone else both return 404 not_found, never 403 — otherwise the API could be used to probe which order ids exist.

GET /api/v1/reports/orders/:id/status

Everything GET /orders/:id returns, plus a statusDetail block that answers the two questions you'd otherwise write a switch statement for: is anything further expected, and can I download yet.

Request
curl -H "Authorization: Bearer rq_live_sk_..." \
  "https://api.roofquery.com/api/v1/reports/orders/rq_ord_8f3a21c9d4e07b16/status"
Response
{
  "orderId": "rq_ord_8f3a21c9d4e07b16",
  "status": "in-progress",
  "statusDetail": {
    "code": "in-progress",
    "label": "In progress",
    "description": "A technician is measuring the property.",
    "terminal": false,
    "filesReady": false,
    "known": true,
    "changedAt": "2026-08-12T14:22:07.000Z",
    "estimatedDeliveryHours": 2,
    "estimatedReadyAt": "2026-08-12T16:03:11.000Z",
    "openRevisionId": null
  },
  "totals": null,
  "files": []
}
FieldDescription
terminalNothing further is expected on this order
filesReadyThe PDF and XML (and the ESX, when the order includes it) can be fetched right now. Also requires files[] to be non-empty, so it can't disagree with the payload
knownfalse if we add a status your code hasn't seen. Branch on this rather than on an exhaustive switch
changedAtWhen the order last moved
estimatedReadyAtOrder time plus the product's typical turnaround. null once files are ready
openRevisionIdThe revision currently open, or null

status stays the same plain string it is in the webhook payload, so a handler written against webhooks works here unchanged. Turnaround — around 2 hours residential, 8 commercial — is an estimate, not a commitment. Errors are the same as GET /orders/:id.