Collectors (Write)

Create Collector

POST /api/v1/collectors

Request body:

{
  "name": "collector-1",
  "description": "Primary collector",
  "config": { ... },
  "area_id": "uuid",
  "enabled": true
}

Validation: name, config, and area_id are all required. When present in the config blob, config.snmp.target, config.gre.local_ip, and config.gre.remote_ip must each be a valid IP address, and config.discovery_mode must be one of gre, snmp, isis-gre, isis-snmp, eigrp-snmp (absent = GRE; bgpls is engine-managed and refused with its own message) — otherwise the request is rejected with 400. An isis-snmp config additionally requires config.hierarchy.area to be exactly level-2 or level-1:<canonical-area-address>; config.snmp.crawl_abrs=true is accepted only with level-2. Missing, generic (level-1), malformed, over-long, or non-canonical area values are rejected rather than starting an unscoped recorder. An eigrp-snmp config may carry config.eigrp.route_walk_interval_seconds: how often the routers' EIGRP topology and RIB tables are read for the devices this recorder covers. Absent or 0 inherits the network's discovery_interval_hours; any other value must be a whole number of minutes expressed in seconds (a multiple of 60) between 300 (five minutes) and 2678400 (31 days), and anything else — a negative, a fraction, a non-minute value such as 330, an overflowing magnitude, a string — is rejected rather than repaired or read as "inherit" — by this endpoint, by the collector-manager, and by the store reader the SNMP poller obeys, so a row imported around the API cannot poll on an unsupported cadence while its collector row reads error. The web form writes an explicit 3600-second value for a new EIGRP recorder; clearing that field or omitting the API property retains the inheritance contract, and editing an older recorder does not add the new UI default. The same number fixes the route-history freshness lease (2 × interval + 10 min). A configured override gets its own poll schedule rather than riding the counters tick, so it also applies where counter polling is disabled; changing it takes one immediate walk even while the poller is running (the recorder refresh wakes the target's own timer), and an on-demand discovery (POST /api/v1/snmp/discover-now) always reads the tables however long the interval. It overrides the network cadence in both directions and governs the route walk only — hierarchy and interface enrichment keep riding the network discovery pass. config.snmp.targets is a bootstrap seed list, not the recording scope: an EIGRP recorder records every device in its network, and an empty list is valid at create and on update (deleting a device prunes its address from it, and such a recorder must stay editable). enabled defaults to true when omitted; an explicit false creates the collector stopped.

Response: 201 Created

Error: 400 Bad Request — Missing required field (name, config, or area_id), an invalid IP in config.snmp.target / config.gre.local_ip / config.gre.remote_ip, or an invalid IS-IS SNMP home-area/crawler combination

Update Collector

PUT /api/v1/collectors/{collectorID}

Performs a partial merge-update (only fields provided are updated). enabled only changes when explicitly present in the body — a config-only update never starts or stops the collector. For backward compatibility on ordinary collectors, name: null, name: "", and area_id: null are no-ops; a non-null area_id, including the empty string, follows the historical update path and may still fail database validation. A completed bootstrap child is stricter: supplying an empty or null protected identity field is an attempted identity change and returns 400.

Request body: Same as create (all fields optional).

Response: 200 OK

Errors: 404 Not Found; 400 Bad Request when a completed bootstrap child attempts identity/provenance drift; 409 Conflict while a child is still owned by an active bootstrap parent.

Toggle Collector

POST /api/v1/collectors/{collectorID}/toggle

Flips the enabled flag and updates the status field ("pending" if enabled, "stopped" if disabled).

Response: 200 OK (updated collector object)

Errors: 404 Not Found; 409 Conflict while a child is still owned by an active bootstrap parent.

Delete Collector

DELETE /api/v1/collectors/{collectorID}

Response: 204 No Content

Errors: 404 Not Found; 409 Conflict while a child is still owned by an active bootstrap parent.