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:
- Create the model definition.
- Add typed fields.
- Add one or more protocol mappings.
- Run a live collection test against a real device.
- 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:
nameis required, max 100 characters, and must match^[a-zA-Z0-9][a-zA-Z0-9._-]*$.namemust be globally unique (case-insensitive).descriptionmax 500 characters.versionmust be a strict semantic version (MAJOR.MINOR.PATCH, e.g.1.0.0); omit it to default to1.0.0.1.0is 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": []
}
resultscontains parsed, field-mapped data.raw-updatesshows 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:
- Re-reads the model from the database.
- Rebuilds the in-memory model registry.
- 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.