Skip to content
Documentation Prelude Collector

Workflow

End-to-end API workflow for creating, testing, and subscribing to a data model in Prelude Collector — request and response shapes for every step.

This page walks through the API calls to take a model from "doesn't exist yet" to "actively collecting data". After this first mention, the rest of this page refers to Prelude Collector as "the collector". Models are stored in the database, loaded into the collection engine at startup, and hot-reloaded within ~500 ms of any change to their fields or mappings — active subscriptions are re-configured automatically without a restart.

This page is the data-shape reference. For a narrative walk-through of building a multi-vendor model, see the interface counters tutorial.

Families, versions, and the {id} path segment

A model is a family (a uniquely-named identity) that owns one or more versions, each labeled with a semantic version such as 1.0.0. Fields and mappings belong to a version. In the endpoints below, the {id} segment of the model item routes (/models/{id}/...) is a version ID — the list response returns each family's id plus a version-id you can address. See Families and versions for the concept.

End-to-end data shape

A complete, ready-to-use custom model goes through five API steps:

  1. Create the model definition.
  2. Add typed fields.
  3. Add one or more protocol mappings.
  4. Run a live collection test against a real device.
  5. Create a subscription to start continuous collection.

The rest of this page documents the request and response shapes for each step. For the exact endpoint paths and full schemas, see the API reference.

Step 1 — Create the model

export BASE="https://collector.example.com"
export TOKEN="<your-api-token>"

curl -s -X POST "$BASE/api/v1/models" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-bgp-summary",
    "description": "BGP summary table via SNMP",
    "version": "1.0.0"
  }'

Bruno: 04 Models / Create model

UI: https://collector.example.com/models/new

Validation rules:

  • name is required, max 100 characters, and must match ^[a-zA-Z0-9][a-zA-Z0-9._-]*$.
  • name must be globally unique (case-insensitive).
  • description max 500 characters.
  • version must be a strict semantic version (MAJOR.MINOR.PATCH, e.g. 1.0.0); omit it to default to 1.0.0. 1.0 is rejected.

This creates the model family and its first version in one call.

Response 201 Created:

{
  "id": 7,
  "name": "my-bgp-summary",
  "description": "BGP summary table via SNMP",
  "version": "1.0.0",
  "version-id": 7,
  "is-active": true,
  "fields": [],
  "mappings": []
}

Use version-id as the {id} in the field, mapping, test, and export endpoints below. You can also inline fields in the create body. The API validates each field's type and format before saving.

Step 2 — Add fields

curl -s -X POST "$BASE/api/v1/models/7/fields" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "neighbor-address",
    "field-type": "string",
    "format": "ip-address",
    "required": true,
    "is-key": true,
    "description": "BGP neighbor IP (row key)",
    "position": 0
  }'

Bruno: 04 Models / Add field

UI: https://collector.example.com/models/7

Flag exactly one field with "is-key": true — the model's protocol-independent identity key that uniquely identifies each collected row. Repeat for each field. The supported field-type values are:

string   int32   int64   uint32   uint64   float32   float64   boolean

To change the display order after adding all fields, send an array of field IDs in the desired order to the reorder endpoint:

curl -s -X POST "$BASE/api/v1/models/7/fields/reorder" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[12, 10, 11, 13]'

Bruno: 04 Models / Reorder fields

The engine assigns position values from the array index. For the update and delete endpoints, see the API reference.

Step 3 — Add a protocol mapping

curl -s -X POST "$BASE/api/v1/models/7/mappings" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "protocol": "snmp",
    "netos": "ios-xr",
    "key-field": "neighbor-address",
    "snmp-walk-oids": ["1.3.6.1.2.1.15.3"],
    "field-mappings": {
      "bgpPeerRemoteAddr": "neighbor-address",
      "bgpPeerState":      "session-state",
      "bgpPeerRemoteAs":   "peer-as"
    },
    "value-transforms": {
      "session-state": {
        "1": "idle",
        "2": "connect",
        "3": "active",
        "4": "opensent",
        "5": "openconfirm",
        "6": "established"
      }
    }
  }'

Bruno: 04 Models / Add mapping

UI: https://collector.example.com/models/7/mappings/new

Protocol-specific path fields

Use exactly one set of path fields for the chosen protocol:

Protocol Path fields
gnmi gnmi-paths (array), optional gnmi-path-filter
snmp snmp-walk-oids and / or snmp-scalar-oids
netconf netconf-filter-paths (array of XML subtree filter strings)
cli cli-commands (array, may use {param} placeholders), cli-template (TTP template), cli-discovery-params

Version constraints

To target a specific firmware range, add version-constraints:

{
  "protocol": "gnmi",
  "netos": "ios-xr",
  "version-constraints": [">=7.5", "<8.0"],
  "key-field": "neighbor-address"
}

The engine picks the matching mapping with the highest specificity (most constraints) for the device's reported version. If two equally-specific mappings match, both are skipped and an error is recorded.

Uniqueness constraint

The combination (model, protocol, netos, version-constraints) must be unique. Adding a second mapping with the same protocol, NetOS, and version constraints returns 409 Conflict.

Path syntax warnings

After saving a mapping, the API may include a warnings array on the response if it detects path syntax issues — for example, an invalid gNMI path format. These are non-blocking; the mapping is saved regardless.

{
  "id": 3,
  "protocol": "gnmi",
  "warnings": ["gnmi-paths[0]: path does not begin with a module prefix"]
}

Step 4 — Run a live collection test

Before subscribing, validate the model against a real device:

curl -s -X POST "$BASE/api/v1/models/7/test" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "device-id": 3,
    "mapping-id": 5
  }'

Bruno: 04 Models / Test mapping

UI: https://collector.example.com/models/7/test

The engine connects to the device using the protocol configured for the mapping and collects data for up to 8 seconds, returning early once 1.5 seconds elapse with no new updates (the initial burst has settled).

Response 200 OK:

{
  "device": "router-core-01",
  "protocol": "snmp",
  "raw-data-received": true,
  "results": [
    {
      "key": "192.0.2.1",
      "model-name": "my-bgp-summary",
      "data": {
        "neighbor-address": "192.0.2.1",
        "session-state": "established",
        "peer-as": 65001
      }
    }
  ],
  "raw-updates": []
}
  • results contains parsed, field-mapped data.
  • raw-updates shows the raw protocol output before any mapping is applied — useful when debugging field-mapping keys.

Tip

If raw-data-received is true but results is empty, the parser received data but could not extract the key field. Check that key-field matches an actual leaf name in raw-updates.

Step 5 — Create a subscription

Once the test passes, activate the model on a device:

curl -s -X POST "$BASE/api/v1/subscriptions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "device-id": 3,
    "data-model-definition-id": 4,
    "protocol": "snmp",
    "interval": 60
  }'

Bruno: 05 Subscriptions / Create subscription

UI: https://collector.example.com/subscriptions/new

device-id, data-model-definition-id (the model family ID), and protocol are required. Add data-model-version-id to pin a specific version; leave it out (or null) to track the latest active version as the family evolves. The collector defaults interval to 60 seconds and forces enabled to true on creation; the new subscription starts in the stopped status and transitions on its first poll. The collection engine resolves the correct mapping at each poll cycle based on the device's NetOS and firmware version. See the Subscriptions API for the full field list.

Evolve a model with a new version

To change a model without disturbing devices already collecting it, create a new version instead of editing the live one:

curl -s -X POST "$BASE/api/v1/models/7/new-version" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "version": "1.1.0" }'

This clones the source version's fields and mappings under the same family name as a new, inactive draft. Omit the body to auto-bump the minor version. Edit the draft's fields and mappings, then activate it (set is-active to true via PUT /api/v1/models/{version-id}). Subscriptions that track the latest active version pick it up automatically; pinned subscriptions stay on their pinned version until you move them.

The version string on a version is immutable — use new-version to introduce a new one rather than editing it in place.

Import / export format

Models can be exported and imported as self-contained JSON files, which makes it easy to move a working model between environments or to seed a starter catalogue.

Export

curl -s -X GET "$BASE/api/v1/models/7/export" \
  -H "Authorization: Bearer $TOKEN" \
  -o my-bgp-summary.json

Bruno: 04 Models / Export model

UI: https://collector.example.com/models/7

The response is a JSON document with Content-Disposition: attachment.

Import

curl -s -X POST "$BASE/api/v1/models/0/import" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary @my-bgp-summary.json

Bruno: 04 Models / Import model

UI: https://collector.example.com/models

The path takes a model ID for routing consistency but the import handler ignores it — every imported model is created from the JSON body. Import fails with 400 if a model with the same name already exists.

Export schema

{
  "name": "my-bgp-summary",
  "description": "BGP summary table via SNMP",
  "version": "1.0",
  "fields": [
    {
      "name": "neighbor-address",
      "field-type": "string",
      "format": "ip-address",
      "required": true,
      "description": "BGP neighbor IP (row key)",
      "position": 0
    },
    {
      "name": "session-state",
      "field-type": "string",
      "required": false,
      "description": "BGP session state",
      "position": 1
    }
  ],
  "mappings": [
    {
      "protocol": "snmp",
      "netos": "ios-xr",
      "version-constraints": [],
      "key-field": "neighbor-address",
      "snmp-walk-oids": ["1.3.6.1.2.1.15.3"],
      "field-mappings": {
        "bgpPeerRemoteAddr": "neighbor-address",
        "bgpPeerState":      "session-state"
      },
      "value-transforms": {
        "session-state": {
          "6": "established"
        }
      }
    }
  ]
}

All array and map fields (version-constraints, gnmi-paths, field-mappings, field-transforms, value-transforms, ignore-values, cli-discovery-params) are optional and are omitted when empty.

Hot-reload behaviour

After any field or mapping change (create, update, delete), the collector debounces the event and fires a reload ~500 ms after the last change. The reload:

  1. Re-reads the model from the database.
  2. Rebuilds the in-memory model registry.
  3. Re-subscribes every active device collector that uses the model with the new configuration.

If you delete the model or set is-active to false, it is removed from the registry immediately and all associated collectors are torn down.

See also

Filtering by: