# Netzhandwerker Telemetry Normalizer

> Netzhandwerker Telemetry Normalizer is a paid API for AI agents from tools.halowerk.com, paid per call via x402, $0.002/call, status unknown (last checked 2026-09-15).

Converts SenML (JSON or CBOR) and LwM2M TLV payloads into a unified record shape with resolved names, units, absolute timestamps, and typed values, with optional time-axis alignment.

## Facts

- Endpoint: POST https://tools.halowerk.com/v1/telemetry/normalize
- Price: $0.002/call
- Payment: x402
- Status: unknown
- Last checked: 2026-09-15
- Activations on Zero: 0
- Tags: x402
- Canonical page: https://www.zero.xyz/c/netzhandwerker-telemetry-normalizer-ca845e73
- Structured record (JSON): https://api.zero.xyz/v1/capabilities/cap_czOr10cDJjTaWuI_qOwzb

Status and success rate cover calls made through Zero and Zero's own probes. Third-party monitors may report differently.

## How to call it through Zero

Zero handles the 402 payment challenge and records the run. With the Zero CLI installed (`npm i -g @zeroxyz/cli`):

```sh
zero fetch --capability netzhandwerker-telemetry-normalizer-ca845e73 -d '<json body>'
```

Example prompt: Normalize this SenML JSON payload into resolved records with absolute timestamps — I want every series aligned on a 60-second grid using linear interpolation, and use 'urn:dev:ow:10e2073a01080063/' as the base_name.

## When to prefer this

Use this endpoint when you have raw IoT wire-format payloads (SenML JSON, SenML CBOR, or LwM2M TLV) that need to be converted into a consistent, typed, human-readable record shape without any device connectivity or server-side state. Particularly valuable when you need to align multiple heterogeneous sensor series onto a shared time axis for downstream analytics. Prefer this over custom parsers when you need deterministic, stateless, idempotent conversion with proper type resolution and explicit handling of ambiguous TLV resource types.

## Known failure modes

- Payload exceeds 1 MB decoded or 256 KB request body — returns size error
- LwM2M TLV resource IDs not provided in value_types — affected resources returned as opaque base64 with warnings, not errors
- SenML records with relative times and no base_time_unix_s anchor — endpoint cannot invent a 'now', records may have missing timestamps
- Aligned grid exceeds 10,000 points (interval too small for time range) — returns grid size error
- Unsupported format value — returns validation error
- max_records exceeded by payload — records beyond the limit are silently dropped up to the configured maximum

## How this service works

Turns SenML (JSON or CBOR) and LwM2M TLV into one record shape: resolved name, unit, absolute timestamp, typed value. Optionally puts every series on a shared time axis with a chosen interval and fill rule (last value, bucket mean, linear). Works only on the payload you pass: no device is contacted, nothing is stored, the same request returns the same bytes. TLV carries no type on the wire; unmapped resource ids come back as opaque base64 with a warning instead of a guess.

## Output

A JSON object containing an array of normalized records, each with a resolved name, unit, absolute Unix timestamp, and typed value. If align=true, also returns every numeric series resampled onto a shared time grid at the requested interval. Unmapped LwM2M TLV resource IDs are returned as opaque base64 strings with accompanying warnings rather than guessed types.

## Request schema (JSON Schema)

```json
{
 "type": "object",
 "properties": {
  "align": {
   "type": "boolean",
   "default": false,
   "description": "Also return every numeric series on one shared time grid."
  },
  "format": {
   "enum": [
    "senml_json",
    "senml_cbor",
    "lwm2m_tlv"
   ],
   "type": "string",
   "description": "Wire format of payload. senml_cbor and lwm2m_tlv expect base64."
  },
  "payload": {
   "description": "SenML records as a JSON array (or a JSON string holding one), or base64 for senml_cbor and lwm2m_tlv. At most 1000000 bytes after decoding; the service caps the whole request body at 256 KB."
  },
  "base_name": {
   "type": "string",
   "maxLength": 200,
   "description": "Prefix put in front of every resolved record name. For lwm2m_tlv this is where the device identity goes."
  },
  "interval_s": {
   "type": "number",
   "default": 60,
   "maximum": 86400,
   "minimum": 0.001,
   "description": "Grid spacing in seconds. The grid may not exceed 10000 points."
  },
  "max_records": {
   "type": "integer",
   "default": 5000,
   "maximum": 20000,
   "minimum": 1,
   "description": "Upper bound on records taken from the payload."
  },
  "tolerance_s": {
   "type": "number",
   "default": 0,
   "maximum": 86400,
   "minimum": 0,
   "description": "align_method=last only: how stale the carried value may be before the grid point is left empty. 0 means no limit."
  },
  "value_types": {
   "type": "object",
   "description": "lwm2m_tlv only: resource id as key, one of integer, float, boolean, string, time, opaque as value. Ids left out come back as opaque base64 with a warning instead of a guessed type."
  },
  "align_method": {
   "enum": [
    "last",
    "mean",
    "linear"
   ],
   "type": "string",
   "default": "last",
   "description": "How a grid point is filled: last value at or before the point, mean over the bucket, or linear interpolation between neighbours."
  },
  "base_time_unix_s": {
   "type": "number",
   "description": "Anchor in seconds since 1970 for records that carry no absolute time. SenML calls times below 2^28 relative to now; this endpoint is stateless and will not invent a now."
  }
 }
}
```

## More

- Live health (JSON, refreshed every minute): https://www.zero.xyz/c/netzhandwerker-telemetry-normalizer-ca845e73/health.json
- [Zero catalog index](https://www.zero.xyz/llms.txt)
- [Other services from tools.halowerk.com](https://www.zero.xyz/host/tools.halowerk.com/llms.txt)
