Routes & Interfaces (Read)

All route and interface endpoints support multi-scope querying. In addition to area_id, you can pass pi_id (protocol instance), rd_id (routing domain), or network_id to query across all areas within that scope. The backend resolves the scope to a set of area IDs using the hierarchy. Exactly one scope parameter is required.

List Inter-Area Route Summary

GET /api/v1/routes/inter-area/summary?area_id={areaID}
GET /api/v1/routes/inter-area/summary?pi_id={piID}
GET /api/v1/routes/inter-area/summary?rd_id={rdID}
GET /api/v1/routes/inter-area/summary?network_id={networkID}

Paginated inter-area routes grouped by network prefix. Returns aggregate statistics per prefix for the summary/drill-down UI pattern.

Query parameters (mutually exclusive, one required):

Optional:

Response: 200 OK

{
  "summaries": [
    {
      "network": "10.1.0.0/16",
      "router_count": 3,
      "min_metric": 10,
      "max_metric": 30,
      "area_count": 2
    }
  ],
  "total": 142
}

Error: 400 Bad Request — Missing scope parameter

List Inter-Area Routes

GET /api/v1/routes/inter-area?area_id={areaID}
GET /api/v1/routes/inter-area?pi_id={piID}
GET /api/v1/routes/inter-area?rd_id={rdID}
GET /api/v1/routes/inter-area?network_id={networkID}

List inter-area routes (Type 3 Summary-LSA) for a specific scope. When network is provided, returns paginated per-router detail for that prefix (drill-down mode with display_name enrichment). Without network, returns all routes (legacy behavior for time-travel).

Query parameters (mutually exclusive, one required):

Optional:

Response (with network): 200 OK

{
  "routes": [
    {
      "id": "uuid",
      "network": "10.1.0.0/16",
      "adv_router": "10.0.0.2",
      "device_id": "uuid",
      "display_name": "rtr-core-01",
      "metric": 20,
      "area_id": "uuid",
      "first_seen": "2026-02-17T10:00:00Z",
      "last_seen": "2026-02-18T14:00:00Z"
    }
  ],
  "total": 3
}

Response (without network, legacy): 200 OK — flat JSON array of routes (no pagination envelope).

Error: 400 Bad Request — Missing scope parameter

List External Route Summary

GET /api/v1/routes/external/summary?area_id={areaID}
GET /api/v1/routes/external/summary?pi_id={piID}
GET /api/v1/routes/external/summary?rd_id={rdID}
GET /api/v1/routes/external/summary?network_id={networkID}

Paginated external routes grouped by (network, metric_type). Returns aggregate statistics per prefix for the summary/drill-down UI pattern.

Query parameters (mutually exclusive, one required):

Optional:

Response: 200 OK

{
  "summaries": [
    {
      "network": "0.0.0.0/0",
      "metric_type": 2,
      "asbr_count": 2,
      "has_nssa": false,
      "has_fwd_addr": false
    }
  ],
  "total": 89
}

Error: 400 Bad Request — Missing scope parameter

List External Routes

GET /api/v1/routes/external?area_id={areaID}
GET /api/v1/routes/external?pi_id={piID}
GET /api/v1/routes/external?rd_id={rdID}
GET /api/v1/routes/external?network_id={networkID}

List external routes (Type 5 AS-External and Type 7 NSSA-External LSA) for a specific scope. When network is provided, returns paginated per-ASBR detail for that prefix (drill-down mode with display_name enrichment). Without network, returns all routes (legacy behavior for time-travel).

Query parameters (mutually exclusive, one required):

Optional:

Response (with network): 200 OK

{
  "routes": [
    {
      "id": "uuid",
      "network": "0.0.0.0/0",
      "adv_router": "10.0.0.5",
      "device_id": "uuid",
      "display_name": "rtr-edge-01",
      "metric_type": 2,
      "metric": 1,
      "fwd_addr": null,
      "tag": 0,
      "is_nssa": false,
      "area_id": "uuid",
      "first_seen": "2026-02-17T10:00:00Z",
      "last_seen": "2026-02-18T14:00:00Z"
    }
  ],
  "total": 2
}

Response (without network, legacy): 200 OK — flat JSON array of routes (no pagination envelope).

Notes:

Error: 400 Bad Request — Missing scope parameter

List ASBR Entries

GET /api/v1/routes/asbr?area_id={areaID}
GET /api/v1/routes/asbr?pi_id={piID}
GET /api/v1/routes/asbr?rd_id={rdID}
GET /api/v1/routes/asbr?network_id={networkID}

List ASBR reachability entries (Type 4 Summary-LSA) for a specific scope.

Query parameters (mutually exclusive, one required):

Optional:

Response: 200 OK

[
  {
    "id": "uuid",
    "asbr_id": "10.0.0.5",
    "adv_router": "10.0.0.2",
    "device_id": "uuid",
    "metric": 10,
    "area_id": "uuid",
    "first_seen": "2026-02-17T10:00:00Z",
    "last_seen": "2026-02-18T14:00:00Z"
  }
]

Notes:

Error: 400 Bad Request — Missing scope parameter

List Device Interfaces

GET /api/v1/devices/{deviceID}/interfaces

List interfaces for a specific device. Interface data is extracted from Router-LSA P2P and transit link entries.

Response: 200 OK

[
  {
    "id": "uuid",
    "device_id": "uuid",
    "ip_address": "10.0.12.1",
    "mask": "255.255.255.252",
    "peer_id": "10.0.0.2",
    "cost": 10,
    "link_type": "p2p",
    "first_seen": "2026-02-17T10:00:00Z",
    "last_seen": "2026-02-18T14:00:00Z"
  }
]

Notes:

Error: 500 Internal Server Error — Database error

List Device Neighbors

GET /api/v1/devices/{deviceID}/neighbors
GET /api/v1/devices/{deviceID}/neighbors?area_id={areaID}

List OSPF neighbors for a specific device. Returns neighbor adjacency details including link state, costs, interface names, and ABR/ASBR flags.

Path parameters:

Query parameters:

Response: 200 OK

{
  "device_id": "uuid",
  "router_id": "10.0.0.1",
  "hostname": "router1",
  "neighbors": [
    {
      "neighbor_id": "10.0.0.2",
      "device_id": "uuid",
      "hostname": "router2",
      "state": "up",
      "address": "10.1.1.2",
      "local_address": "10.1.1.1",
      "interface": "Gi0/0/1",
      "cost": 10,
      "reverse_cost": 10,
      "link_type": "transit",
      "area_id": "0.0.0.0",
      "is_abr": false,
      "is_asbr": false,
      "first_seen": "2026-02-24T10:00:00Z",
      "last_seen": "2026-02-24T12:00:00Z"
    }
  ],
  "summary": {
    "total": 5,
    "up": 4,
    "down": 1
  }
}

Notes:

Error responses:

Get Device EIGRP

GET /api/v1/devices/{deviceID}/eigrp

A device's EIGRP adjacencies and EIGRP-enabled interfaces, discovered passively via CISCO-EIGRP-MIB (SNMP). All-role read. An unknown device yields empty lists, not an error.

Path parameters:

Response: 200 OK

{
  "neighbors": [
    {
      "id": "uuid",
      "device_id": "uuid",
      "peer_addr": "10.0.12.1",
      "address_family": "ipv4",
      "as_number": 100,
      "vrf_name": "default",
      "local_ifindex": 2,
      "local_if_name": "Et0/1",
      "remote_device_id": null,
      "remote_hostname": "",
      "hold_time": 14,
      "srtt_ms": 1600,
      "rto_ms": 5000,
      "uptime": "01:00:13",
      "source": "snmp",
      "first_seen": "2026-07-20T10:00:00Z",
      "last_seen": "2026-07-20T12:00:00Z"
    }
  ],
  "interfaces": [
    { "if_index": 2, "name": "Et0/1", "hello_interval": 5, "peer_count": 1, "mean_srtt": 1600 }
  ]
}

Notes:

Error responses:

Get Network EIGRP Topology

GET /api/v1/networks/{networkID}/eigrp/topology

Every EIGRP neighbor whose device belongs to the network, for the topology overlay / EIGRP Topology Reports panel. The frontend derives undirected adjacency edges from the (device_id, remote_device_id) pairs (an edge is bidirectional when both endpoints observe each other, a half-adjacency otherwise). All-role read.

Path parameters:

Response: 200 OK

{
  "neighbors": [
    {
      "id": "uuid",
      "device_id": "uuid",
      "remote_device_id": "uuid",
      "peer_addr": "10.0.12.2",
      "address_family": "ipv4",
      "as_number": 100,
      "vrf_name": "default",
      "device_hostname": "r1",
      "remote_hostname": "r2"
    }
  ]
}

Notes:

Error responses:

Get EIGRP Observed Path

GET /api/v1/eigrp/path?source_device_id={deviceID}&dest_ip={address}
GET /api/v1/eigrp/path?source_device_id={deviceID}&dest_ip={address}&at={RFC3339}

Reconstructs the EIGRP forwarding path from a source device toward a destination address by chaining the observed next-hops — not by running SPF. EIGRP's composite metric mixes min(bandwidth) with summed delay, so it is not expressible as one additive edge cost, and its components are not readable over SNMP. The routers have already run DUAL, so Osprey reads the resulting distributed FIB (cEigrpTopoTable) instead of modelling it. All-role read.

Query parameters:

Response: 200 OK

{
  "hops": [
    { "device_id": "uuid-a", "route": { "dest_prefix": "10.0.99.0/24", "next_hop": "10.0.12.2", "fdistance": 128256, "route_origin": "Internal" }, "next_device_id": "uuid-b" },
    { "device_id": "uuid-b", "route": { "dest_prefix": "10.0.99.0/24", "route_origin": "Connected" } }
  ],
  "devices": ["uuid-a", "uuid-b"],
  "complete": true,
  "terminated": "delivered"
}

Without at, the existing live response shape is unchanged. With at, the response also contains eigrp_history and uses a historical route DTO that intentionally has no live row id, source, or first/last/create/update timestamps. Coverage is complete, partial, or unavailable; temporal evidence is sampled, bracketed, carried_forward, or unavailable.

Notes:

Error responses:

SPF tree and EIGRP. GET /api/v1/topology/spf-tree refuses an EIGRP area with 400. EIGRP runs DUAL, not SPF, and its links are stored with cost 0 because the composite metric is a property of a whole path rather than of an edge — rendering a Dijkstra tree over them would produce a tidy all-zero result that looks authoritative and means nothing. Use the observed path endpoint above instead. For the same reason EIGRP areas are excluded from the shared per-area graph loader, so path, RIB and simulation never traverse a zero-cost EIGRP segment (which would otherwise win every cheapest-path comparison).

List Interfaces by Scope

GET /api/v1/interfaces?area_id={areaID}
GET /api/v1/interfaces?pi_id={piID}
GET /api/v1/interfaces?rd_id={rdID}
GET /api/v1/interfaces?network_id={networkID}

List all interfaces for devices in a specific scope. Interface data is extracted from Router-LSA P2P and transit link entries. When OSPFv2 and OSPFv3 interfaces share the same (device_id, if_name, area_label), they are merged into a single entry with both IPv4 and IPv6 addresses.

Query parameters (mutually exclusive, one required):

Optional:

Response: 200 OK

[
  {
    "id": "uuid",
    "device_id": "uuid",
    "ip_address": "10.0.12.1",
    "mask": "255.255.255.252",
    "ip_address_v6": "2001:db8::1",
    "mask_v6": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc",
    "peer_id": "10.0.0.2",
    "cost": 10,
    "link_type": "p2p",
    "first_seen": "2026-02-17T10:00:00Z",
    "last_seen": "2026-02-18T14:00:00Z"
  }
]

Notes:

Error: 400 Bad Request — Missing scope parameter

List Stub Networks

GET /api/v1/stub-networks?area_id={areaID}
GET /api/v1/stub-networks?pi_id={piID}
GET /api/v1/stub-networks?rd_id={rdID}
GET /api/v1/stub-networks?network_id={networkID}

List stub networks (host routes, loopbacks, attached networks with no adjacency) for a specific scope. Supports server-side pagination and search for live mode. Time-travel mode (at param) uses the legacy unpaginated path.

Query parameters (mutually exclusive, one required):

Optional:

Response (paginated, no at): 200 OK

{
  "stubs": [
    {
      "id": "uuid",
      "network": "192.168.1.0/24",
      "device_id": "uuid",
      "display_name": "rtr-core-01",
      "cost": 1,
      "area_id": "uuid",
      "first_seen": "2026-02-17T10:00:00Z",
      "last_seen": "2026-02-18T14:00:00Z"
    }
  ],
  "total": 1204
}

Response (legacy, with at): 200 OK — flat JSON array of stub networks (no display_name, no pagination envelope).

Notes:

Error: 400 Bad Request — Missing scope parameter