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.
A profile is one contractor you order for: a company name, a contact block, a license
number, two colours and a logo. Name it as brand when you
place an order, and that order's PDF
carries the contractor's brand, logo included, for good. The profile's
contact email and phone are never printed: they're who we reach if we need more
information about the property. Nothing about your own account defaults changes.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/branding/profiles | Every profile on the account, newest first |
| POST | /api/v1/branding/profiles | Create. Returns 201 with the profile and its id |
| GET | /api/v1/branding/profiles/:id | One profile |
| PATCH | /api/v1/branding/profiles/:id | Change any text or colour field. Omitted fields are left alone |
| DELETE | /api/v1/branding/profiles/:id | Delete the profile and its logo file. A profile that orders were placed for can't be deleted: 409 BRAND_IN_USE (edit it instead) |
| PUT | /api/v1/branding/profiles/:id/logo | Upload the logo: raw image bytes as the body, with an image Content-Type |
| DELETE | /api/v1/branding/profiles/:id/logo | Remove the logo. The rest of the profile stays |
The JSON body of POST and PATCH.
| Field | Required | Description |
|---|---|---|
companyName | To create | Prints on the header band, footer and cover attribution, and is how the profile is listed. 80 chars |
licenseNumber | No | Contact block, as License # …. 40 chars |
contactEmail | No | Never printed. Who we email about a property. 160 chars |
contactPhone | No | Never printed. Who we call about a property. 32 chars |
address | No | Contact block. 160 chars |
publicPhone | No | Contact block. 32 chars. A 10-digit number is formatted (502) 555-0100 |
publicEmail | No | Contact block. 160 chars |
brandColor | No | Hex. Defaults to RoofQuery blue |
brandTextColor | No | Hex. Picked for contrast when omitted |
With no contact on the profile, we reach your own account's contact instead; an order is never refused for it.
curl -X POST "https://api.roofquery.com/api/v1/branding/profiles" \
-H "Authorization: Bearer rq_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"companyName": "Acme Roofing",
"address": "900 Bardstown Rd, Louisville, KY 40204",
"publicPhone": "5025559876",
"publicEmail": "[email protected]",
"licenseNumber": "KY-RC-48213",
"contactEmail": "[email protected]",
"contactPhone": "5025550142",
"brandColor": "#0F7A4D"
}'
HTTP/1.1 201 Created
{
"profile": {
"profileId": "rq_brand_7f3a21c9d4e07b16",
"companyName": "Acme Roofing",
"address": "900 Bardstown Rd, Louisville, KY 40204",
"publicPhone": "5025559876",
"publicEmail": "[email protected]",
"licenseNumber": "KY-RC-48213",
"contactEmail": "[email protected]",
"contactPhone": "5025550142",
"brandColor": "#0F7A4D",
"brandTextColor": null,
"hasLogo": false,
"createdAt": "2026-09-09T14:02:11.000Z",
"updatedAt": "2026-09-09T14:02:11.000Z"
}
}
Then the logo, as bytes. No multipart, no JSON wrapper — the file is the body:
curl -X PUT "https://api.roofquery.com/api/v1/branding/profiles/rq_brand_7f3a21c9d4e07b16/logo" \
-H "Authorization: Bearer rq_live_sk_..." \
-H "Content-Type: image/png" \
--data-binary @acme-logo.png
And then order for that contractor with "brand": "rq_brand_7f3a21c9d4e07b16". Every PDF of that order carries Acme Roofing.
A profile is complete. Every printed field comes from the profile and nothing falls through to your account defaults — a profile with no phone number prints no phone number, rather than yours under the contractor's name. The PDF is drawn when it's downloaded, so editing a profile updates the reports ordered for it.
Orders placed before 22 October 2026 don't have a brand of their own: they
still take one on a download link as ?brand=<profileId>, checked
against the company that placed the order (an unknown or foreign id is
404 not_found, like an unknown order id). See
White labeling. Ids are
rq_brand_ plus 64 random bits.
Logos go through the same pipeline as your account logo: PNG, JPEG, WebP, GIF, AVIF or
SVG, under 5MB, squared to a 512×512 PNG with transparent padding. There is no URL
field, on purpose — see White
labeling. Up to 500 profiles per account; past that,
409 profile_limit. Profiles can also be created and edited by hand in the
portal under Branding → Profiles,
which shows each one's id and a test PDF.
validation (400), not_found (404), profile_limit and BRAND_IN_USE (409), and for
logos empty_upload (400), too_large (413, over 5MB),
payload_too_large (413, a body over 6MB), unsupported_type (415) and
unreadable_image (422). Every code is under Errors.