Skip to content

Road Bulletins ​

Multi Conditions

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:

FieldTypeRequiredConstraintsDescription
point[number, number]✓exactly 2 values, [longitude, latitude]End of the segment.
conditionstring enum✓one of the values belowCondition 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:

ValueMeaning
UNKNOWNRoad section's condition is unknown
NOMINALRoad section is 2WD driveable in most cases or easily driven in a 4WD.
SOMEWHAT_DEGRADEDRoad section has rough spots, water bars or moderate washouts and may require high-clearance 4WD
HEAVILY_DEGRADEDRoad section requires high clearance 4WD. There may be challenging washouts, dangerous sections or exceptionally deep water bars.
IMPASSABLERoad 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: example 1

{
  "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.

example 2

{
    "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.

example 2

{
    "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:

FieldTypeRequiredConstraintsDescription
referencestring✓non-blankA draft upload reference returned from POST /api/integrations/v1/draft/upload.
captionstring✓1–500 charactersShort 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:

FieldTypeRequiredConstraintsDescription
categorystring enum✓see values belowWhat 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.

FieldTypeRequiredConstraintsDescription
featureIdstring——Required when updating an existing feature; omit when creating.
finalTypestring enum✓see values belowThe feature's state after the report.
commentstring—max 500 charsOptional note about the change.
removedboolean——Set true if the feature no longer exists. Defaults to false.
createdboolean——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]
}
FieldTypeDescription
referencestringOpaque 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:

  1. Upload each image to POST /draft/upload, collecting the reference IDs.
  2. Build the bulletin payload, dropping each reference into photos[].reference with its caption.
  3. POST /bulletins with 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.

Service Road Atlas API documentation