Measurements
Create a measurement, share the returned link so it can be measured in Flashline, then read it back when you need the numbers. result is filled in once the status reaches complete.
Creates a measurement for the connected team and returns it in pending state, with the link the contractor opens to measure it in Flashline. Store the returned id; it is how you read the result later.
Returns 201 Created, or 200 OK with the original measurement, unchanged, when the same external.id was seen before from your app. Other fields in the duplicate request are ignored, so a double-clicked button never creates two and never edits one.
Body
| Field | Type | Description |
|---|---|---|
| addressrequired | object | Where to measure. Geocoded on our side; the contractor confirms the rooftop in Flashline. |
| address.line1required | string | Street address. Max 200. |
| address.line2optional | string | Unit or suite. |
| address.cityrequired | string | |
| address.regionrequired | string | Province or state code, e.g. ON, NY. |
| address.postal_coderequired | string | |
| address.countryoptional | string | ISO 3166-1 alpha-2, CA or US. Defaults to the team's country. |
| customerrequired | object | The homeowner, as the contractor will see them. |
| customer.namerequired | string | Max 120. |
| customer.emailone of two | string | One of email or phone is required. |
| customer.phoneone of two | string | Any format; stored as given. |
| notesoptional | string | Free text for the contractor: access, timing, what the customer said. Max 2000. Never shown to the customer. |
| externaloptional | object | Your side of the link. Recommended. |
| external.idoptional | string | Your own id for this proposal. Max 128. Unique per app; drives idempotency. |
| external.urloptional | string | HTTPS link back into your product, shown to the contractor in Flashline as "Open {label}". |
| external.labeloptional | string | What the contractor sees, e.g. Proposal #88213. Max 80. Defaults to the app name. |
A missing or malformed field answers 422 validation_failed; error.errors lists { "field": "customer", "message": "email or phone is required" } entries.
POST /api/measurements
Authorization: Bearer flt_…
Flashline-Version: 2026-09-02
Content-Type: application/json
{
"address": {
"line1": "412 Maple Crescent",
"city": "Oakville",
"region": "ON",
"postal_code": "L6H 2K4",
"country": "CA"
},
"customer": {
"name": "Dana Whitfield",
"email": "dana@example.com",
"phone": "+1 905 555 0142"
},
"notes": "Rear eaves overflow; customer home after 4pm.",
"external": {
"id": "prop_88213",
"url": "https://crm.example.com/proposals/88213",
"label": "Proposal #88213"
}
}
// 201 Created
{
"id": "msr_7Hx2Kd9mQ4",
"status": "pending",
"created_at": "2026-09-03T14:12:09Z",
"updated_at": "2026-09-03T14:12:09Z",
"completed_at": null,
"address": {
"line1": "412 Maple Crescent",
"line2": null,
"city": "Oakville",
"region": "ON",
"postal_code": "L6H 2K4",
"country": "CA",
"formatted": "412 Maple Crescent, Oakville, ON L6H 2K4, Canada",
"lat": 43.4675,
"lng": -79.6877
},
"request": {
"id": "req_Q4nT8b",
"created_at": "2026-09-03T14:12:09Z",
"customer": {
"name": "Dana Whitfield",
"email": "dana@example.com",
"phone": "+1 905 555 0142"
},
"notes": "Rear eaves overflow; customer home after 4pm.",
"external": {
"id": "prop_88213",
"url": "https://crm.example.com/proposals/88213",
"label": "Proposal #88213"
},
"url": "https://www.flashlinegutters.com/dashboard/requests/req_Q4nT8b"
},
"result": null
}
Returns the measurement as it stands: the same object the create call returned, with result filled in once the contractor completes it. There are no webhooks yet: read it when your user asks for the result, or poll on a schedule you choose; a measurement that reached complete stays complete. 404 not_found for an id that does not belong to the connected team.
The measurement object
| Field | Type | Description |
|---|---|---|
| id | string | msr_-prefixed, stable for life. Store this. |
| status | enum | pending: nobody has started. in_progress: a contractor opened it. complete: the measurement is done and result is present. canceled: withdrawn by the contractor in Flashline. There is no API call to cancel. |
| created_at, updated_at | time | RFC 3339 UTC. updated_at moves on any change. |
| completed_at | time | null | When the contractor completed the measurement. |
| address | object | The property. The components are what you sent, normalized, and never change after creation. formatted, lat and lng come from geocoding at creation and are refined to the measured rooftop when the contractor confirms it. |
| request | object | Flashline's request record, the inbox item your call created. |
| request.id | string | req_-prefixed; the id the contractor sees in Flashline. |
| request.customer | object | name, email, phone as stored; the contractor may correct them. |
| request.notes | string | null | The notes you sent, as the contractor sees them. |
| request.external | object | null | id, url, label: your side of the link. |
| request.url | url | Opens the request in Flashline. Needs a signed-in member of the team. This is what a "Measure in Flashline" button opens. |
| result | object | null | Present when status is complete. |
| result.gutter_length_ft | number | Total gutter run in decimal feet, including the contractor's override if set. |
| result.gutter_size_in | number | null | The size the contractor picked, e.g. 5 or 6. |
| result.corner_count | integer | Inside and outside corners on the traced runs. |
| result.downspout_count | integer | |
| result.downspout_length_ft | number | Total, elbows included. |
| result.downspouts[] | array | One entry per downspout: length_ft, floors (storeys it drops, 1 to 3), size_in (null until assigned). |
| result.installer_message | string | null | The contractor's message to the homeowner about this property. Plain text. |
| result.asset_pack[] | array | Every visual Flashline produced, in presentation order: kind, url, content_type. URLs are public, stable and do not expire: embed them directly in your proposals, or copy them to your own storage. Kinds: aerial, aerial_annotated, intro, measurements, risk_analysis, hangers, certification. New kinds may be added; ignore unknown ones. Empty until generated. |
result is null until the contractor completes the measurement. That is a state, not an error.
// 200 OK
{
"id": "msr_7Hx2Kd9mQ4",
"status": "complete",
"created_at": "2026-09-03T14:12:09Z",
"updated_at": "2026-09-04T15:02:10Z",
"completed_at": "2026-09-04T15:02:10Z",
"address": {
"line1": "412 Maple Crescent",
"line2": null,
"city": "Oakville",
"region": "ON",
"postal_code": "L6H 2K4",
"country": "CA",
"formatted": "412 Maple Crescent, Oakville, ON L6H 2K4, Canada",
"lat": 43.46752,
"lng": -79.68771
},
"request": {
"id": "req_Q4nT8b",
"created_at": "2026-09-03T14:12:09Z",
"customer": { "name": "Dana Whitfield", "email": "dana@example.com", "phone": "+1 905 555 0142" },
"notes": "Rear eaves overflow; customer home after 4pm.",
"external": { "id": "prop_88213", "url": "https://crm.example.com/proposals/88213", "label": "Proposal #88213" },
"url": "https://www.flashlinegutters.com/dashboard/requests/req_Q4nT8b"
},
"result": {
"gutter_length_ft": 186.5,
"gutter_size_in": 5,
"corner_count": 9,
"downspout_count": 6,
"downspout_length_ft": 118,
"downspouts": [
{ "length_ft": 22, "floors": 2, "size_in": 3 },
{ "length_ft": 22, "floors": 2, "size_in": 3 },
{ "length_ft": 12, "floors": 1, "size_in": 3 },
{ "length_ft": 12, "floors": 1, "size_in": 3 },
{ "length_ft": 25, "floors": 2, "size_in": 3 },
{ "length_ft": 25, "floors": 2, "size_in": 3 }
],
"installer_message": "Dana, the rear eaves are undersized for the roof area feeding them…",
"asset_pack": [
{ "kind": "aerial", "url": "https://…/aerial.png", "content_type": "image/png" },
{ "kind": "aerial_annotated", "url": "https://…/aerial-annotated.jpg", "content_type": "image/jpeg" },
{ "kind": "intro", "url": "https://…/intro.png", "content_type": "image/png" },
{ "kind": "measurements", "url": "https://…/measurements.png", "content_type": "image/png" },
{ "kind": "risk_analysis", "url": "https://…/risk-analysis.png", "content_type": "image/png" },
{ "kind": "hangers", "url": "https://…/hangers.png", "content_type": "image/png" },
{ "kind": "certification", "url": "https://…/certification.png", "content_type": "image/png" }
]
}
}