Skip to Content

Validation Rules

ModularIoT validates every metric payload at ingestion time. Understanding these rules helps you build drivers that produce clean, accepted data.

Validation Pipeline

Incoming payloads pass through multiple validation stages:

Payload → Schema Validation → Key Validation → Type Validation → Bounds Check → Accept ↓ ↓ ↓ ↓ Reject Reject Reject Reject

Each stage can reject the payload with a specific error code. The pipeline stops at the first failure.

Schema Validation

Every payload must conform to the miot.metrics@1.0 schema:

CheckRequirementError Code
schema_versionMust be "1.0"INVALID_SCHEMA_VERSION
device_idRequired, 1-255 charactersMISSING_DEVICE_ID
timestampRequired, ISO 8601 with timezoneINVALID_TIMESTAMP
metricsRequired, non-empty arrayEMPTY_METRICS

Key Validation

Metric keys must be either canonical or extension keys.

Canonical Keys

Keys must exactly match a registered canonical key:

✓ engine.rpm ✓ vehicle.speed ✓ fuel.level ✗ Engine.RPM (case mismatch) ✗ engineRpm (wrong format) ✗ engine_rpm (underscore instead of dot)

Extension Keys (Custom Metrics)

For vendor-specific metrics, use the x. prefix:

✓ x.vendor.custom_metric ✓ x.teltonika.din1 ✓ x.queclink.report_type ✗ custom.vendor.metric (wrong prefix) ✗ vendor.custom (no prefix)

Extension keys bypass type and bounds validation but are still logged.

Unknown Key Behavior

Unknown keys (not canonical and not x. prefixed) are rejected by default:

ModeBehavior
Strict (default)Reject entire payload
LenientAccept payload, log warning, drop unknown metric

Type Validation

Each canonical key specifies an expected value type:

TypeJSON TypeExampleCanonical Keys
NUMBERnumber72.5Most metrics
BOOLEANbooleantrueidle.state, brake.state, dtc.mil_on
STRINGstring"1G1YY22G965109753"vehicle.vin, vehicle.protocol
ARRAYarray["P0301", "P0420"]dtc.codes
OBJECTobject{...}engine.freeze_frame

Type mismatches cause rejection:

// ✗ REJECTED: engine.rpm expects NUMBER, got STRING { "key": "engine.rpm", "value": "2500" } // ✓ ACCEPTED { "key": "engine.rpm", "value": 2500 }

Bounds Validation

Numeric metrics have defined valid ranges. Values outside these bounds are rejected.

Powertrain Bounds

KeyMinMaxUnitNotes
engine.rpm08,000rpmHigher for motorcycles—contact support
vehicle.speed0300km/hCovers most vehicles
engine.load0100%Engine load percentage
throttle.position0100%Throttle opening
engine.coolant_temp-40150°COperating range
engine.intake_air_temp-4080°CIntake air temp
engine.runtime0sUnbounded (session)
engine.total_runtime0hUnbounded (lifetime)

Fuel Bounds

KeyMinMaxUnitNotes
fuel.level0100%Tank percentage
fuel.rate0100L/hInstantaneous rate
fuel.used0LCumulative (unbounded)
battery.voltage030VCovers 12V and 24V systems
engine.torque_pct0100%Torque utilization

Motion Bounds

KeyMinMaxUnitNotes
odometer0kmUnbounded (lifetime)
accelerator.position0100%Pedal position

Emissions Bounds

KeyMinMaxUnitNotes
fuel.trim.short-100100%Short-term fuel trim
fuel.trim.long-100100%Long-term fuel trim
lambda02ratioAir-fuel equivalence
catalyst.temp01,000°CCatalyst temperature

Electrical Bounds

KeyMinMaxUnitNotes
battery.current-100100ANegative = discharging

Timestamp Validation

Timestamps prevent stale or future data from polluting the pipeline.

RuleDefaultConfigurable
Not in futureMax 1 hour aheadYes
Not too oldMax 30 days oldYes
Valid formatISO 8601 with timezoneNo

Valid Timestamps

✓ 2024-01-15T10:30:00Z ✓ 2024-01-15T10:30:00+00:00 ✓ 2024-01-15T05:30:00-05:00 ✗ 2024-01-15T10:30:00 (missing timezone) ✗ 2024-01-15 (missing time) ✗ 1705315800 (Unix timestamp—not supported)

Per-Metric Timestamps

When batching historical data, per-metric timestamps are validated individually:

{ "timestamp": "2024-01-15T10:30:00Z", "metrics": [ { "key": "vehicle.speed", "value": 60, "timestamp": "2024-01-15T10:29:55Z" }, { "key": "vehicle.speed", "value": 65, "timestamp": "1990-01-01T00:00:00Z" } ] }

The second metric would be rejected (too old) while the first is accepted.

Payload Size Limits

LimitValuePurpose
Max payload size1 MBPrevents memory exhaustion
Max metrics per payload1,000Bounds processing time
Max key length255 charsPrevents storage abuse
Max string value1,000 charsReasonable for VIN, codes

Validation Response

Failed validation returns a structured error:

{ "error": { "code": "OUT_OF_BOUNDS", "message": "Metric 'engine.rpm' value 15000 is out of bounds [0, 8000]", "field": "metrics[2].value", "suggestion": "Check sensor calibration or unit conversion" } }

Error Codes

CodeMeaning
INVALID_SCHEMA_VERSIONUnknown or missing schema version
MISSING_DEVICE_IDDevice ID not provided
INVALID_TIMESTAMPMalformed or missing timestamp
EMPTY_METRICSMetrics array empty
UNKNOWN_KEYKey not canonical and not x. prefixed
TYPE_MISMATCHValue type doesn’t match definition
UNIT_MISMATCHWrong unit for metric
OUT_OF_BOUNDSNumeric value outside valid range
PAYLOAD_TOO_LARGEExceeds size limit
RATE_LIMITEDToo many requests

Best Practices

  1. Validate locally first — SDKs include validation; catch errors before transmission
  2. Use canonical units — Convert at the edge, not at ingestion
  3. Include timezone always — UTC (Z suffix) is preferred
  4. Handle rejection gracefully — Log, alert, and retry with corrections
  5. Monitor rejection rates — High rates indicate driver bugs or sensor issues

→ See Reference > Error Codes for complete error documentation

Last updated on