Connect Custom Data Sources — Consolidated Reference
Connect Custom Data Sources — Consolidated Reference
Purpose: Single-document reference for vibe coding against the Eyer custom data source integration. Consolidates all 8 pages from the "Connect Custom Data Sources" section of the Eyer Knowledge Base. Last synced: 2026-04-03.
1. Overview (README FIRST)
Data can come from an open source agent, custom code, scripts, or instrumented existing code.
Two paths:
Path A — Open source / custom agent (no format control): You need a mapping from your agent's JSON output to the Eyer internal format. Follow Section 1 below.
Path B — Instrumented code (you control output format): Skip mapping. Format directly to the Eyer generic format. Follow Section 2 below.
Path A — Step by step
Get your metrics with names and timestamps in a flat JSON format. Less nesting = easier mapping.
Structure data with metrics/values in a
fieldssection and metadata in atagssection. Thefieldssection can repeat (e.g., one per database).Create a mapping from your data to the Eyer internal format (see mapping examples below).
Validate the mapping via the Push API test endpoint.
Contact Eyer to enable your custom endpoint.
Example flat JSON (Telegraf output):
{
"metrics": [
{
"fields": {
"disk_reads_mean": 1.012,
"disk_writes_per_sec_mean": 0.857,
"last_Run_mean": 1740047577489,
"log_bytes_flushed_mean": 3.959,
"logical_reads_mean": 914.996,
"total_sessions_mean": 14,
"waits_total": 0,
"waits_total_mean": 7.3
},
"name": "dbwatch",
"tags": {
"host": "testmachine1",
"instance": "DatabaseServer1"
},
"timestamp": 1740047760
}
]
}Path B — Step by step
Refer to the Eyer generic input format (Section 5 below).
Structure your output accordingly.
Contact Eyer to enable your custom endpoint.
2. Input Data Format Requirements
Core rules
Eyer ingests time series data: sequential data points with timestamps and one or more measured values.
All data must be in JSON format. Prefer flatter structures with limited nesting.
Timestamps must be in epoch (
1745352000000) or ISO (2025-11-11T11:00:00Z) format.Recommended resolution: 1-minute aggregates before sending. Eyer's minimum operating resolution is 1 minute.
If local aggregation is not feasible, send raw data. Eyer will aggregate automatically to per-minute.
If no aggregation function is specified, Eyer defaults to average.
JSON examples
Example 1 — Database metrics (Telegraf):
{
"metrics": [
{
"fields": {
"disk_reads_mean": 1.012,
"disk_writes_per_sec_mean": 0.857,
"last_Run_mean": 1740047577489,
"log_bytes_flushed_mean": 3.959,
"logical_reads_mean": 914.996,
"total_sessions_mean": 14,
"waits_total": 0,
"waits_total_mean": 7.3
},
"name": "dbwatch",
"tags": {
"host": "testmachine1",
"instance": "Database1",
"url": "http://localhost:8080/dashboard/dbWatch%2001/data/HkRmUaLz0Z.telegraf.json"
},
"timestamp": 1740047760
}
]
}Example 2 — SNMP traffic (repeating ports):
{
"metrics": [
{
"fields": { "ifHCInOctets": 0, "ifHCOutOctets": 0 },
"name": "Octets_field",
"tags": {
"agent_host": "192.168.88.54",
"host": "DESKTOP-S01F7CP",
"hostname": "US-8-150W",
"ifName": "3/26"
},
"timestamp": 1741167360
},
{
"fields": { "ifHCInOctets": 0, "ifHCOutOctets": 0 },
"name": "Octets_field",
"tags": {
"agent_host": "192.168.88.54",
"host": "DESKTOP-S01F7CP",
"hostname": "US-8-150W",
"ifName": "3/11"
},
"timestamp": 1741167360
}
]
}Example 3 — Prometheus flat array:
[
{
"fields": { "value": 100 },
"name": "prometheus",
"tags": {
"field_type": "go_gc_gogc_percent",
"host": "eyer-server-4.local",
"url": "http://localhost:9100/metrics"
},
"timestamp": 1763897800
},
{
"fields": { "value": 7 },
"name": "prometheus",
"tags": {
"field_type": "go_goroutines",
"host": "eyer-server-4.local",
"url": "http://localhost:9100/metrics"
},
"timestamp": 1763897800
}
]3. Mapping Example 1 — One Node Per Repeating Field (new node per instance)
Use case: Each repeating fields entry creates a new node with its own metrics, differentiated by a tag field.
Input (Telegraf database metrics):
{
"metrics": [
{
"fields": {
"disk_reads_mean": 1.012,
"disk_writes_per_sec_mean": 0.857,
"log_bytes_flushed_mean": 3.959,
"logical_reads_mean": 914.996,
"total_sessions_mean": 14,
"waits_total_mean": 7.3
},
"name": "dbwatch",
"tags": {
"host": "testmachine1",
"instance": "Database1",
"url": "http://localhost:8080/dashboard/dbWatch%2001/data/HkRmUaLz0Z.telegraf.json"
},
"timestamp": 1740047760
}
]
}Mapping JSON:
{
"selector": ["tags.host", "tags.instance", "timestamp"],
"type": "telegraf.waitstats",
"id_selector": "tags.instance",
"root": "metrics",
"system": "tags.host",
"mappings": [
{
"type": "telegraf.waitstats.waits",
"guid": "16e41dba-e6a4-4427-b753-cf6eee41e384",
"name": "dbWatch waits",
"timestamp": "timestamp",
"fields": [
{ "name": "fields.disk_reads_mean", "display": "Disk reads", "type": "float", "aggregation": "avg", "guid": "9ed41a78-3094-4e41-98b5-b1f98f63dc3a" },
{ "name": "fields.disk_writes_per_sec_mean", "display": "Disk writes per second", "type": "float", "aggregation": "avg", "guid": "1203a428-0b80-4240-8c1b-f7fa2665ec22" },
{ "name": "fields.log_bytes_flushed_mean", "display": "Log bytes flushed", "type": "float", "aggregation": "avg", "guid": "2add293a-446a-468e-91c3-8f164fe697c8" },
{ "name": "fields.logical_reads_mean", "display": "Logical reads", "type": "float", "aggregation": "avg", "guid": "7acb184d-c4ce-4287-ab4a-f63f15976bac" },
{ "name": "fields.total_sessions_mean", "display": "Total sessions", "type": "int", "aggregation": "avg", "guid": "30c71236-48ad-4fa8-b282-f587ffe8b4b9" },
{ "name": "fields.waits_total_mean", "display": "Waits total", "type": "float", "aggregation": "avg", "guid": "1f2a5a97-338b-4a92-8846-f27cd9f6b71f" }
],
"name_map": "tags.instance"
}
],
"skip_merge": true
}Mapping field reference:
Field | Description |
|---|---|
| Array of fields used to regroup data |
| Internal type string; suffix used to register API endpoint (e.g. |
| Tag used for node uniqueness checks, combined with |
| Root object to start parsing from |
| Field to populate the System value |
| Array of node type definitions; each element = one node type with N stat types |
| Internal node type ID. Must be unique. Prefixed with top-level |
| Optional UUID per node type. Auto-generated from |
| User-friendly node type name |
| Field path to pick timestamp from |
| Internal stat name; must correspond to input field path |
| User-facing display name |
| Data type string (e.g. |
|
|
| Optional UUID per stat type. Auto-deduced from |
| Tag field to use as the node name |
|
|
Output (Eyer internal format):
[
{
"Node": {
"Name": "Database1",
"SystemName": "testmachine1",
"AgentId": "telegraf.waitstats",
"AtomId": "Database1"
},
"NodeType": {
"Id": "16e41dba-e6a4-4427-b753-cf6eee41e384",
"Name": "telegraf.waitstats.waits",
"DisplayName": "Database waits"
},
"Stats": [
{ "Name": "fields.disk_reads_mean", "Type": "float", "Guid": "9ed41a78-3094-4e41-98b5-b1f98f63dc3a", "Aggregation": "avg", "Display": "Disk reads", "Value": 1.012, "Timestamp": 1740047760 },
{ "Name": "fields.disk_writes_per_sec_mean", "Type": "float", "Guid": "1203a428-0b80-4240-8c1b-f7fa2665ec22", "Aggregation": "avg", "Display": "Disk writes per second", "Value": 0.857, "Timestamp": 1740047760 },
{ "Name": "fields.total_sessions_mean", "Type": "int", "Guid": "30c71236-48ad-4fa8-b282-f587ffe8b4b9", "Aggregation": "avg", "Display": "Total sessions", "Value": 14, "Timestamp": 1740047760 },
{ "Name": "fields.waits_total_mean", "Type": "float", "Guid": "1f2a5a97-338b-4a92-8846-f27cd9f6b71f", "Aggregation": "avg", "Display": "Waits total", "Value": 7.3, "Timestamp": 1740047760 }
]
}
]4. Mapping Example 2 — All Metrics on One Node (flatten into single node)
Use case: All repeating fields entries collapse onto the same node when the hostname tag is identical. Contrast with Example 1 which creates one node per instance.
Input (Telegraf SNMP traffic per switch port):
{
"metrics": [
{
"fields": { "ifHCInOctets": 0, "ifHCOutOctets": 0 },
"name": "Octets_field",
"tags": { "agent_host": "192.168.88.54", "host": "DESKTOP-S01F7CP", "hostname": "US-8-150W", "ifName": "3/26" },
"timestamp": 1741167360
},
{
"fields": { "ifHCInOctets": 0, "ifHCOutOctets": 0 },
"name": "Octets_field",
"tags": { "agent_host": "192.168.88.54", "host": "DESKTOP-S01F7CP", "hostname": "US-8-150W", "ifName": "3/11" },
"timestamp": 1741167360
}
]
}Mapping JSON:
{
"selector": ["tags.hostname", "timestamp"],
"type": "booomi.telegraf.snmp",
"id_selector": "tags.hostname",
"root": "metrics",
"system": "tags.hostname",
"skip_merge": true,
"mappings": [
{
"type": "booomi.telegraf.snmp.interface",
"name": "SNMP Device",
"guid": "7f92fc9c-23a3-462a-a556-9a7da1694e4a",
"timestamp": "timestamp",
"fields": [
{ "name": "fields.ifHCInOctets", "name_map": "tags.ifName", "display": "In Octets", "type": "Count", "aggregation": "avg", "cumulative": true },
{ "name": "fields.ifHCOutOctets", "name_map": "tags.ifName", "display": "Out Octets", "type": "Count", "aggregation": "avg", "cumulative": true }
],
"name_map": "tags.hostname"
}
]
}Key difference vs Example 1:
name_mapis on the field level (not mapping level), which makes each field's name dynamically suffixed with the tag value (tags.ifName), collapsing all ports onto a single node.
Output (Eyer internal format):
[
{
"Node": { "Name": "US-8-150W", "SystemName": "US-8-150W", "AgentId": "booomi.telegraf.snmp", "AtomId": "US-8-150W" },
"NodeType": { "Id": "7f92fc9c-23a3-462a-a556-9a7da1694e4a", "Name": "booomi.telegraf.snmp.interface", "DisplayName": "SNMP Device" },
"Stats": [
{ "Name": "fields.ifHCInOctets", "Type": "Count", "Aggregation": "avg", "Display": "In Octets", "Value": 0, "Timestamp": 1741167360 },
{ "Name": "fields.ifHCOutOctets", "Type": "Count", "Aggregation": "avg", "Display": "Out Octets", "Value": 0, "Timestamp": 1741167360 }
]
}
]5. Eyer Generic Input Format (no mapping needed)
Use this when you control the output format of your data source. No mapping file required — format directly to this schema and send to Eyer.
Rules
All fields are mandatory
NodeTypemust not contain spacesTimestampmust be in UNIX epoch formatAggregationvalues:avg,max,sum
Example
In this example: System = "Server 1", two node types (disk and CPU), multiple stat types per node.
{
"metrics": [
{
"Generic": "HD 1",
"Time": 1742548364.371,
"Timestamp": 1742548364,
"NodeType": "server.disk",
"StatType": "I/O",
"System": "Server 1",
"Value": 10,
"Aggregation": "avg"
},
{
"Generic": "HD 1",
"Time": 1742548364.371,
"Timestamp": 1742548364,
"NodeType": "server.disk",
"StatType": "Utilization",
"System": "Server 1",
"Value": 12976534,
"Aggregation": "avg"
},
{
"Generic": "CPU 1",
"Time": 1742548364.371,
"Timestamp": 1742548364,
"NodeType": "server.cpu",
"StatType": "Usage",
"System": "Server 1",
"Value": 34,
"Aggregation": "avg"
}
]
}To add more nodes: replace HD 1 with HD 2, CPU 1 with CPU 2, etc.
Once output is ready, send a sample to Eyer (support email or Discord) to get your endpoint enabled.
6. Mapping Example 3 — Using an LLM to Generate Mappings
An LLM (Claude, ChatGPT, Gemini, Mistral) can generate mapping JSON from your input data. The prompt pattern is:
Provide an example input JSON (your source data)
Provide an example mapping JSON (from Example 1 or 2 above)
Provide the expected output JSON (the mapped result)
Then ask: "How would the mapping look for this new input?"
Critical: Choose the example that matches the node topology you want. If you feed Example 1 to the LLM, it will produce one-node-per-instance mappings. If you feed Example 2, it will collapse to a single node.
LLM prompt template:
Learn how to do a JSON mapping. You have the following input file:
[PASTE YOUR EXAMPLE INPUT JSON]
The JSON mapping for it is:
[PASTE EXAMPLE MAPPING JSON]
The resulting output is:
[PASTE EXAMPLE OUTPUT JSON]
Question: How would the mapping for the following new input look?
[PASTE YOUR ACTUAL INPUT JSON]Resulting LLM output for an SNMP interface input (using Example 1 as reference):
{
"selector": ["tags.host", "tags.ifName", "timestamp"],
"type": "telegraf.ifstats",
"id_selector": "tags.ifName",
"root": "metrics",
"system": "tags.host",
"mappings": [
{
"type": "telegraf.ifstats.octets",
"guid": "b5d4d7c9-8f52-4df5-8101-ec5c63a8a123",
"name": "Interface Octets",
"timestamp": "timestamp",
"fields": [
{ "name": "fields.ifInOctets", "display": "Inbound Octets", "type": "int", "aggregation": "avg", "guid": "8f1c9de3-91ef-4a34-bc76-e2d05d2a6e51" },
{ "name": "fields.ifOutOctets", "display": "Outbound Octets", "type": "int", "aggregation": "avg", "guid": "d10f4182-fc7c-4d65-a8c5-b72a2e6ec351" }
],
"name_map": "tags.ifName"
}
],
"skip_merge": true
}Note: This LLM used Example 1 as the reference, so the result creates one node per switch port (tags.ifName). To get all ports on a single node, use Example 2 as the reference mapping instead.
After generating the mapping, always validate it with the Push API test endpoint (Section 7 below) before deploying.
7. Push API — Test and Validate Your Mapping
Use this endpoint to test a mapping against sample data before deploying. Returns the Eyer internal format so you can validate the node/metrics structure.
Endpoint:
POST https://eyer-prod-api-data-ne-stage.azurewebsites.net/api/mapping/testHeaders:
authenticate: <your apiTokenPush>
Content-Type: application/jsonRequest body structure:
{
"mapping": {
/* Your mapping JSON goes here */
},
"data": [
/* Your test input data goes here */
]
}Full example request (copy-paste ready):
{
"mapping": {
"selector": ["tags.host", "tags.instance", "timestamp"],
"type": "telegraf.waitstats",
"id_selector": "tags.instance",
"root": "metrics",
"system": "tags.host",
"mappings": [
{
"type": "telegraf.waitstats.waits",
"guid": "16e41dba-e6a4-4427-b753-cf6eee41e384",
"name": "Database waits",
"timestamp": "timestamp",
"fields": [
{ "name": "fields.disk_reads_mean", "display": "Disk reads", "type": "float", "aggregation": "avg", "guid": "9ed41a78-3094-4e41-98b5-b1f98f63dc3a" },
{ "name": "fields.disk_writes_per_sec_mean", "display": "Disk writes per second", "type": "float", "aggregation": "avg", "guid": "1203a428-0b80-4240-8c1b-f7fa2665ec22" },
{ "name": "fields.log_bytes_flushed_mean", "display": "Log bytes flushed", "type": "float", "aggregation": "avg", "guid": "2add293a-446a-468e-91c3-8f164fe697c8" },
{ "name": "fields.logical_reads_mean", "display": "Logical reads", "type": "float", "aggregation": "avg", "guid": "7acb184d-c4ce-4287-ab4a-f63f15976bac" },
{ "name": "fields.total_sessions_mean", "display": "Total sessions", "type": "int", "aggregation": "avg", "guid": "30c71236-48ad-4fa8-b282-f587ffe8b4b9" },
{ "name": "fields.waits_total_mean", "display": "Waits total", "type": "float", "aggregation": "avg", "guid": "1f2a5a97-338b-4a92-8846-f27cd9f6b71f" }
],
"name_map": "tags.instance"
}
],
"skip_merge": true
},
"data": {
"metrics": [
{
"fields": {
"disk_reads_mean": 1.012,
"disk_writes_per_sec_mean": 0.857,
"last_Run_mean": 1740047577489,
"log_bytes_flushed_mean": 3.959,
"logical_reads_mean": 914.996,
"total_sessions_mean": 14,
"waits_total": 0,
"waits_total_mean": 7.3
},
"name": "dbwatch",
"tags": {
"host": "testmachine1",
"instance": "Database1",
"url": "http://localhost:8080/dashboard/dbWatch%2001/data/HkRmUaLz0Z.telegraf.json"
},
"timestamp": 1740047760
}
]
}
}8. Eyer Internal Data Format and Structure
All custom data, once mapped, is stored in the Eyer node→metrics structure.
Hierarchy:
System — the host/server/device where the agent is installed
Node — a monitored entity within the system (e.g., a database, network port, hard drive). Multiple nodes per system.
Stats — one or more metrics per node (e.g., disk reads, CPU usage)
Internal format schema:
[
{
"Node": {
"Name": "<node name from name_map tag>",
"SystemName": "<system name from system field>",
"AgentId": "<top-level type from mapping>",
"AtomId": "<same as Name>"
},
"NodeType": {
"Id": "<guid from mappings[].guid>",
"Name": "<mappings[].type>",
"DisplayName": "<mappings[].name>"
},
"Stats": [
{
"Name": "<fields[].name>",
"Type": "<fields[].type>",
"Guid": "<fields[].guid>",
"Aggregation": "<fields[].aggregation>",
"Display": "<fields[].display>",
"Value": 0.0,
"Timestamp": 0
}
]
}
]Concrete example (database server with one database node):
[
{
"Node": {
"Name": "Database1",
"SystemName": "testmachine1",
"AgentId": "telegraf.waitstats",
"AtomId": "Database1"
},
"NodeType": {
"Id": "16e41dba-e6a4-4427-b753-cf6eee41e384",
"Name": "telegraf.waitstats.waits",
"DisplayName": "Database waits"
},
"Stats": [
{ "Name": "fields.disk_reads_mean", "Type": "float", "Guid": "9ed41a78-3094-4e41-98b5-b1f98f63dc3a", "Aggregation": "avg", "Display": "Disk reads", "Value": 1.012, "Timestamp": 1740047760 },
{ "Name": "fields.disk_writes_per_sec_mean", "Type": "float", "Guid": "1203a428-0b80-4240-8c1b-f7fa2665ec22", "Aggregation": "avg", "Display": "Disk writes per second", "Value": 0.857, "Timestamp": 1740047760 },
{ "Name": "fields.logical_reads_mean", "Type": "float", "Guid": "7acb184d-c4ce-4287-ab4a-f63f15976bac", "Aggregation": "avg", "Display": "Logical reads", "Value": 914.996, "Timestamp": 1740047760 },
{ "Name": "fields.total_sessions_mean", "Type": "int", "Guid": "30c71236-48ad-4fa8-b282-f587ffe8b4b9", "Aggregation": "avg", "Display": "Total sessions", "Value": 14, "Timestamp": 1740047760 },
{ "Name": "fields.waits_total_mean", "Type": "float", "Guid": "1f2a5a97-338b-4a92-8846-f27cd9f6b71f", "Aggregation": "avg", "Display": "Waits total", "Value": 7.3, "Timestamp": 1740047760 }
]
}
]Quick Decision Reference
Situation | Action |
|---|---|
You control the output format | Use generic input format (Section 5). No mapping needed. |
Using an open source agent (Telegraf, Prometheus, etc.) | Create a mapping. Use Example 1 or 2 as base. |
Each data entity should be a separate node | Use mapping pattern from Example 1 ( |
All entities should share one node with dynamic metric names | Use mapping pattern from Example 2 ( |
Not sure how to write the mapping JSON | Use LLM with the prompt pattern in Section 6 |
Mapping written — need to verify before going live | POST to validation endpoint (Section 7) |
Endpoint not yet active | Contact Eyer via support email or Discord |