GET /bgp/history/candidates
Per-peer candidate RIB intervals for one (prefix, device) over a window — the "why did the best path change" lanes, served from the full-mode per-peer RIB changelog (bgp_rib_entry_history). Each interval carries the candidate's attributes and the recorded is_best, so a client reconstructs the live candidate set (and which one is best) at any instant by interval overlap.
Auth: all authenticated roles
Query params: as_id, prefix, and device_id (all required); from/to (required, RFC 3339); optional routing_domain_id. match is always exact (lanes never follow a covering aggregate).
Response: 200 OK
{
"full_available": true,
"candidates": [
{
"peer_ip": "198.51.100.1", "peer_as": 65001, "peer_type": "ebgp", "path_id": 0,
"next_hop": "198.51.100.1", "as_path": "65001 65100", "local_pref": 200, "med": 0,
"origin": "igp", "is_best": true,
"valid_from": "2026-07-01T10:00:00Z", "valid_to": "2026-07-01T10:30:00Z"
}
]
}
valid_to is omitted for an open (still-live) interval; local_pref (peer sent none) and communities (empty) are likewise omitted. full_available is false when no full RIB history exists for the window — candidates is then empty (guaranteed: if any candidate exists in the window, full_available reports true even when recording stopped mid-window) and the UI shows a "requires full mode" hint rather than a misleading "no changes" state. Bounded by design: one prefix on one device → at most the peer count × churn.
Errors: 400 if as_id, prefix, device_id, or the window is missing/invalid.