Documentation

Place an order

POST /api/v1/reports/orders: every field including brand and esx, the scope rule, retrying safely with an Idempotency-Key, sandbox and the 201 back.

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.

POST /api/v1/reports/orders

Places an order and returns 201 with the order in its initial state. Measurements arrive later — wait for the completed webhook rather than polling.

Parameters
FieldRequiredDescription
countryYesUS or CA. Validated first — every other field depends on it
streetYesstring
cityYesstring
stateYes2-letter state or province abbreviation
postalCodeYesUS 12345 or 12345-6789; CA A1A 1A1. Format only — we don't check it agrees with the state
latitudeYesnumber, −90 to 90
longitudeYesnumber, −180 to 180
scopeYesprimary_only, primary_and_garage, or all_structures. Not defaulted
reportTypeNoresidential (default), commercial, rapid_squares (Rapid Squares), or storm (Storm Reports, which take their own fields)
esxNotrue to include the Xactimate .esx (+$5, or your account's ESX price in pricing), false to leave it out. Left out, your account's default applies (see esx.default in pricing; you can change it under Balance & billing). Residential and commercial only: true on rapid_squares is 400 ESX_NOT_SUPPORTED. You can also add it later
brandNoThe contractor this order is for: a brand profile id. Its PDF carries that brand for good, and a download link can't change it. Left out, the report carries your account's own branding, also for good. An id that isn't one of your profiles is 400 BRAND_NOT_FOUND and nothing is ordered
claimNoClaim number for the job, up to 64 characters. It goes on the Xactimate .esx, and the file downloads as <claim>.esx — see the ESX endpoint. Set here or not at all
notesNoFree text for the technician
Retrying safely

Send an Idempotency-Key header (8–200 characters of letters, digits and _ . : -) and a retry with the same key never places a second order. It returns the first order with 200 instead of 201, even if the retry's body differs. While the first attempt is still being placed, a retry gets 409 ORDER_IN_PROGRESS; if it failed, 502 ORDER_FAILED with nothing charged, and a new key places a new order.

Request
curl -X POST "https://api.roofquery.com/api/v1/reports/orders" \
  -H "Idempotency-Key: henderson-6515-turnbridge" \
  -H "Authorization: Bearer rq_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "country": "US",
    "street": "6515 Turnbridge Pl",
    "city": "Prospect",
    "state": "KY",
    "postalCode": "40059",
    "latitude": 38.3526391,
    "longitude": -85.6208045,
    "scope": "primary_and_garage",
    "reportType": "residential",
    "esx": true,
    "brand": "rq_brand_7f3a21c9d4e07b16",
    "claim": "CLAIM12345",
    "notes": "Detached garage at rear."
  }'
Response

201 Created. totals, structures and files are empty until the report is delivered. billing.amount is the whole charge, and billing.items breaks it down: the report, and the ESX when the order pays for one.

{
  "orderId": "rq_ord_8f3a21c9d4e07b16",
  "status": "processing",
  "previousStatus": null,
  "event": null,
  "reason": null,
  "sandbox": false,
  "sandboxScenario": null,
  "createdAt": "2026-08-12T14:03:11.000Z",
  "updatedAt": "2026-08-12T14:03:11.000Z",
  "completedAt": null,
  "property": {
    "address": "6515 Turnbridge Pl, Prospect, KY 40059, USA",
    "latitude": 38.3526391,
    "longitude": -85.6208045
  },
  "report": {
    "type": "residential",
    "brand": { "id": "rq_brand_7f3a21c9d4e07b16", "companyName": "Acme Roofing" },
    "claim": "CLAIM12345",
    "scope": "primary_and_garage",
    "scopeLabel": "Primary structure and detached garage",
    "notes": "Detached garage at rear.",
    "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": null,
  "structures": null,
  "files": []
}

Sandbox. A rq_test_sk_* key runs a scheduled timeline against your real webhooks and nothing is measured. The scenario is chosen by the address you order and nothing else — see sandbox scenarios. There is no header or body field that forces one, so a sandbox order and a live order are the same request; only the key differs. esx works the same as live, including your account's default, and nothing is charged.

Errors

validation and VALIDATION (400) for a missing or malformed field, ESX_NOT_SUPPORTED (400) for "esx": true on Rapid Squares, BRAND_NOT_FOUND (400) for a brand that isn't one of your profiles, INSUFFICIENT_CREDITS and BILLING_SETUP_REQUIRED (402), PRODUCT_NOT_ENABLED (403) for a beta product not turned on for the account, and ORDER_FAILED (502). Every code is under Errors.