Appearance
Road Bulletins

A road bulletin is a report made on a given road that contains at a minimum a date and description. Uses can optionally supply images, road condition ratings, modes of transport and road feature reports. A standard API request looks like so:
POST /api/integrations/v1/bulletins
Authorization: Bearer <access token>
{
"pathId": "...",
"reportDate": 1731369600000,
"description": "...",
"conditionRatings": [ ... ],
"photos": [],
"vehicles": [],
"featureUpdates": []
}We'll talk about each of these properties below:
pathId
Type: string · Required · Constraints: non-blank
Must correspond to the path you're submitting a road report for. Invalid or unknown IDs are rejected with a not-found error.
reportDate
Type: string (ISO date YYYY-MM-DD) · Required · Constraints: past or present
The date the user observed the road. Future dates are rejected with a validation-failed error.
WARNING
Report date drives condition merging. When two reports overlap on the same road, the one with the later reportDate wins. See Merging Road Conditions below.
description
Type: string · Required · Constraints: 8–1000 characters
A text field for users to describe what they saw on the road. The 8-character minimum ensures reports carry some context — one-word reports like "bad" are rejected.
conditionRatings
Type: array<object> · Optional (defaults to [])
Each entry:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
point | [number, number] | ✓ | exactly 2 values, [longitude, latitude] | End of the segment. |
condition | string enum | ✓ | one of the values below | Condition applied backwards to the previous entry's point. |
While this is an optional part of the report, it's one of the key aspects to indicating the state of a road. Roads can have multiple condition ratings at different segments of the road. The following conditions are supported:
| Value | Meaning |
|---|---|
UNKNOWN | Road section's condition is unknown |
NOMINAL | Road section is 2WD driveable in most cases or easily driven in a 4WD. |
SOMEWHAT_DEGRADED | Road section has rough spots, water bars or moderate washouts and may require high-clearance 4WD |
HEAVILY_DEGRADED | Road section requires high clearance 4WD. There may be challenging washouts, dangerous sections or exceptionally deep water bars. |
IMPASSABLE | Road section is impassable or requires highly specialized vehicles to drive |
The way this is modelled in the road bulletin is an array of conditions + lat/long coordinates. Conditions are rendered in a "look-back" fashion. This means that when you set a condition at point x, all points before point x will adapt the rating you provide. A subsequent condition at point y will set all points between x and y as the new condition.
Merging Road Conditions
As is often the case multiple reports can exist for a road and they can come in any order of date/time. A recent report can be submitted for the first 2km of the road and then subsequently a different user can issue a report from several works prior for the entirety of the road. The Service Road Atlas handles this complex merging of road conditions automatically using the report date as a tie-breaker. The result is that when users submit a report their conditions are not guaranteed to be fully reflected on the map if newer reports have conditions which take precedence. Developers integrating with this API do not need to worry about this behind the scenes logic, but it's explained here for completeness.
Now let's walk through a few practical examples of submitting road conditions:
Example 1 - Regular Report
Here we want to report a road whose condition is good up to ~1.75km, somewhat degraded between 1.75km and 5.3km and impassable from thereafter: 
{
"pathId": "...",
"reportDate": 1731369600000,
"description": "...",
"conditionRatings": [
{
"point": [ 50.30011, -127.67656],
"condition": "NOMINAL"
},
{
"point": [ 50.30021, -127.67211],
"condition": "SOMEWHAT_DEGRADED"
},
{
"point": [ 50.30011, -127.67656],
"condition": "IMPASSABLE"
}
],
"photos": [],
"vehicles": [],
"featureUpdates": []
}Note that points are made up in this case, but for a real road report they will need to correspond to a point a long the road
Example 2 - User does not drive beginning of road
Often times a user will not drive the beginning of a road and only report conditions for a later section. As you'll recall, condition ratings apply backwards, so if a user reports that the road is heavily degraded at the halfway point, it would apply all the way to the beginning of the road; even if they didn't drive that part.
To indicate that a section of road was not driven, especially at the beginning, you can set the first condition rating as UNKNOWN with the point matching the starting point of what the user actually drove or is reporting on.

{
"pathId": "...",
"reportDate": 1731369600000,
"description": "...",
"conditionRatings": [
{
"point": [ 50.30011, -127.67656],
"condition": "UNKNOWN"
},
{
"point": [ 50.30021, -127.67211],
"condition": "SOMEWHAT_DEGRADED"
},
{
"point": [ 50.30011, -127.67656],
"condition": "HEAVILY_DEGRADED"
}
],
"photos": [],
"vehicles": [],
"featureUpdates": []
}Example 3 - Interleaving Conditions
Roads do not need to progress linearly in terms of degradation. A user can report a road as NOMINAL -> SOMEWHAT_DEGRADED -> NOMINAL for example. Likewise if they drove from kilometer 2 to 3 and then 7 to 8, you can interleave the UNKNOWN condition rating to indicate that.

{
"pathId": "...",
"reportDate": 1731369600000,
"description": "...",
"conditionRatings": [
{
"point": [ 50.30011, -127.67656],
"condition": "NOMINAL"
},
{
"point": [ 50.30021, -127.67211],
"condition": "SOMEWHAT_DEGRADED"
},
{
"point": [ 50.30011, -127.67656],
"condition": "NOMINAL"
},
{
"point": [ 50.30011, -127.67656],
"condition": "HEAVILY_DEGRADED"
}
],
"photos": [],
"vehicles": [],
"featureUpdates": []
}photos
Type: array<object> · Optional (defaults to []) · Max items: 8
Each entry:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
reference | string | ✓ | non-blank | A draft upload reference returned from POST /api/integrations/v1/draft/upload. |
caption | string | ✓ | 1–500 characters | Short caption displayed beneath the photo. |
Draft references expire if they aren't attached to a bulletin within one hour. Re-upload if you hit a not-found error on submission.
vehicles
Type: array<object> · Optional (defaults to [])
Each entry:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
category | string enum | ✓ | see values below | What was driven. |
Valid category values:
BICYCLE, E_BICYCLE, DIRTBIKE, DUAL_SPORT, ATV, UTV_SXS, CAR, CROSSOVER, SUV, VAN, TRUCK, CUSTOM_4X4, ON_FOOT
featureUpdates
Type: array<object> · Optional (defaults to [])
Feature updates let the report flag changes to physical features of a road — gates, bridges, impasses, and so on.
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
featureId | string | — | — | Required when updating an existing feature; omit when creating. |
finalType | string enum | ✓ | see values below | The feature's state after the report. |
comment | string | — | max 500 chars | Optional note about the change. |
removed | boolean | — | — | Set true if the feature no longer exists. Defaults to false. |
created | boolean | — | — | Set true when adding a new feature. Defaults to false. |
position | [number, number] | — | exactly 2 values, [longitude, latitude] | Required when created is true. |
Valid finalType values:
WARNING, IMPASSE, BYPASS, BRIDGE_IN, BRIDGE_OUT, BRIDGE_UNKNOWN, FOOT_BRIDGE, OPEN_GATE, LOCKED_GATE, POTENTIAL_GATE, HYDROPOWER_STATION
INFO
MANAGED_OPEN_GATE and MANAGED_LOCKED_GATE are reserved for automatic updates and cannot be set via the API.
Uploading photos
Photos are a two-step process: upload the image file first to receive a short-lived reference, then include that reference in the bulletin's photos array when you submit.
1. Upload the image
POST /api/integrations/v1/draft/upload
Authorization: Bearer <access token>
Content-Type: multipart/form-data
file=<image bytes>Accepted formats: PNG, JPEG, WebP. Scope required: bulletins:write.
Response:
json
{
"reference": "6554a...",
"location": [-127.67656, 50.30011]
}| Field | Type | Description |
|---|---|---|
reference | string | Opaque ID you'll include in the bulletin's photos array. |
location | [longitude, latitude] | GPS coordinates read from the image EXIF. null if none were present. |
WARNING
Drafts live for one hour. Attach them to a bulletin via POST /bulletins or PUT /bulletins/{id} before they expire — references not used in time are garbage-collected and will return a not-found error on submission.
2. Attach it to a bulletin
Include each reference in the photos array along with a caption:
json
"photos": [
{
"reference": "6554a...",
"caption": "Washed-out culvert at the switchback"
}
]Maximum 8 photos per bulletin.
Putting it all together
The complete flow for a bulletin with photos looks like this:
- Upload each image to
POST /draft/upload, collecting thereferenceIDs. - Build the bulletin payload, dropping each reference into
photos[].referencewith its caption. POST /bulletinswith the payload.
POST /api/integrations/v1/bulletins
Authorization: Bearer <access token>
Content-Type: application/json
{
"pathId": "6530bb9f...",
"reportDate": "2026-11-04",
"description": "Drove in to the trailhead. Bridge at km 3.2 looks rough but passable in a stock 4x4.",
"conditionRatings": [
{ "point": [-127.67656, 50.30011], "condition": "NOMINAL" },
{ "point": [-127.67211, 50.30021], "condition": "SOMEWHAT_DEGRADED" }
],
"photos": [
{ "reference": "6554a...", "caption": "Bridge approach" },
{ "reference": "6554b...", "caption": "Deck from above" }
],
"vehicles": [
{ "category": "SUV", "name": "4Runner" }
],
"featureUpdates": [
{
"featureId": "66abc...",
"finalType": "BRIDGE_IN",
"comment": "Still in — deck boards are soft but structure is sound"
}
]
}A successful call returns 201 Created with the full bulletin body (see Reading Bulletins for the response shape).
Updating a bulletin
PUT /api/integrations/v1/bulletins/{id}
Authorization: Bearer <access token>Scope required: bulletins:write. The caller must own the bulletin.
The request body has the same shape as a create — minus pathId, which is fixed at creation time. This is a full replacement: any arrays you omit or send empty replace the stored values. Re-upload photos as drafts and re-reference them if you want to keep them.
Returns 200 OK with the updated bulletin body.
WARNING
Bulletins can only be edited for 7 days after they were created, measured from the createdDate on the bulletin. Attempts to edit outside this window return a bad-request error. This limit exists to prevent retroactively rewriting old reports once other users have relied on them.
Deleting a bulletin
DELETE /api/integrations/v1/bulletins/{id}
Authorization: Bearer <access token>Scope required: bulletins:write. The caller must own the bulletin.
Returns 204 No Content. The bulletin and its photos are permanently removed; condition ratings on affected roads are recomputed from the remaining reports.
WARNING
Bulletins can only be deleted for 48 hours after they were created, measured from the createdDate on the bulletin. Attempts to delete outside this window return a bad-request error. This limit is in place to prevent mass deletion of older reports.