---
title: "Templates"
description: "Map external payloads into platform telemetry."
icon: "file-import"
---

> **For AI agents:** the complete documentation index is at [llms.txt](/llms.txt). Append `.md` to any page URL for its markdown version.

<Note>Templates are in **beta**.</Note>

Templates let you map an external payload shape into an internal platform entity. They are
external-to-internal adapters: they do not define your schema, and they do not decide which data
persists. Your [Thing Type Definition](/platform/data-model#thing-type-definition) remains the
gatekeeper for the metrics a Thing can write to its primary time-series record.

A common template maps JSON payloads into Thing telemetry. You create a template once, then use it
with a generic ingestion endpoint for any Thing whose type includes the mapped metrics.

## How templates work

A template has two independent parts:

| Axis | Meaning |
|------|---------|
| Target | The internal entity or pipeline the template produces. |
| Format | The external input mechanism, selector type, and conversion logic. |

The target defines the output contract: available fields, validation rules, and the downstream sink.
The format defines how the external payload is read and converted.

## Supported formats

| Format | Use it to |
|--------|-----------|
| `JsonPointer` | Map values from JSON payloads with RFC 6901 JSON Pointer selectors. |
| `JavaScriptHandler` | Author custom JavaScript conversion logic for payloads that need code-driven parsing or transformation. |
| `Dbc` | Decode CAN bus payloads with DBC signal definitions. |

## Supported target types

| Target | Produces |
|--------|----------|
| `ThingData` | Time-series metric readings for Things. |
| `Transactions` | Transaction records for business or operational events. |
| `Integrations` | Integration events or commands for connected external systems. |

## JSON Pointer ThingData templates

A `ThingData` + `JsonPointer` template maps an inbound JSON document to metric values for one Thing.
The spec includes:

- `recordsPointer`: optional pointer to an array of readings. If omitted, the whole document is one
  reading.
- `timestamp`: the pointer and timestamp format for each reading.
- `mappings`: metric mappings from external values to `mdef_` metric ids.

Each mapping targets a Metric Definition by id. At apply time, the platform intersects those mappings
with the Thing's active Thing Type Definition. Metrics that are not on the Thing's type are skipped
and reported.

```json
{
  "name": "Acme Telemetry v1",
  "target": "ThingData",
  "format": "JsonPointer",
  "spec": {
    "recordsPointer": "/readings",
    "timestamp": {
      "source": { "pointer": "/ts" },
      "format": "iso8601"
    },
    "mappings": [
      {
        "metricId": "mdef_voltage",
        "source": { "pointer": "/v" },
        "transform": { "scale": 0.1, "offset": 0 }
      },
      {
        "metricId": "mdef_state",
        "source": { "pointer": "/state" },
        "enumMap": { "RUNNING": 1, "IDLE": 0, "FAULT": 2 }
      },
      {
        "metricId": "mdef_mode",
        "source": { "pointer": "/mode" },
        "default": "auto"
      },
      {
        "metricId": "mdef_fw",
        "source": { "pointer": "/header/fw", "scope": "document" }
      }
    ]
  }
}
```

## Selectors and records

Each `source` is a selector object.

| Field | Meaning |
|-------|---------|
| `pointer` | An RFC 6901 JSON Pointer. An empty pointer `""` resolves to the current record root. |
| `scope` | `record` by default. Use `document` to resolve from the root document, such as a shared header value. |

If `recordsPointer` points to an array, each array item becomes one candidate reading. If it is
omitted or empty, the whole document is treated as one reading.

## Timestamp formats

Every emitted reading needs a timestamp. The timestamp mapping supports:

- `iso8601`
- `epoch_ms`
- `epoch_s`

If a record has a missing or unparseable timestamp, the platform skips that record and reports it in
the apply response.

## Missing and invalid values

Templates distinguish missing values from invalid values.

| Case | Behavior |
|------|----------|
| Source is absent or JSON `null` | Write `default` if configured. Otherwise skip that metric. |
| Source is present but cannot be parsed | Skip that metric and report `invalid_value`. |
| Source resolves to an object or array | Skip that metric and report `invalid_value`. |
| Enum source has no `enumMap` entry | Skip that metric and report `invalid_value`. |
| Metric is not on the Thing's active type | Skip that metric and report it as not on the Thing Type Definition. |

A `default` only applies when the source is absent or `null`. It does not rescue a present but invalid
value.

If a record produces no metric values after mapping and validation, the platform skips the whole
reading and reports `no_metric_values`.

## Transforms

Templates support two transform styles:

| Transform | Use with | Notes |
|-----------|----------|-------|
| `transform` | Numeric metrics | Applies `value * scale + offset` to present values. |
| `enumMap` | Enum-shaped `Int` metrics | Maps a source label or code string to the target enum code. |

`transform` and `enumMap` are mutually exclusive. `default` is independent: it may coexist with
either one, but it is written as-is and is not transformed.

Current templates support scalar metric types: `Double`, `Int`, `Bool`, `String`, and enum-shaped `Int`
metrics. `Json` metrics are not supported by templates yet.

## Create and manage templates

Use the Aerovy Platform API to create and manage templates.

| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/v2/templates` | List templates. Supports `target`, `format`, `status`, `continuationToken`, and `pageLimit`. |
| `GET` | `/v2/templates/{templateId}` | Get one template. |
| `POST` | `/v2/templates` | Create a template. |
| `PUT` | `/v2/templates/{templateId}` | Update name, description, spec, and tags. |
| `DELETE` | `/v2/templates/{templateId}` | Soft-delete a template. |
| `POST` | `/v2/templates/{templateId}/test` | Dry-run a sample payload without a Thing or write. |

Template ids use the `tmpl_` prefix. `target` and `format` are immutable after creation. Updating a
template bumps its `revision`, and apply responses echo the revision that processed the payload.

## Apply a template

Apply a `ThingData` template to a Thing with:

```http
POST /v2/thing/{thingId}/data/template/{templateId}?previewOnly=false
X-Api-Key: avy...
Content-Type: application/json
```

The request body is the raw external JSON payload. The platform loads the Thing, resolves the active
Thing Type Definition, maps the payload, writes valid readings, and returns a processing report.

Set `previewOnly=true` to preview the generated write without persisting it.

```json
{
  "previewOnly": false,
  "templateId": "tmpl_abc123",
  "templateRevision": 7,
  "recordsParsed": 100,
  "readingsWritten": 98,
  "metricValuesWritten": 540,
  "recordsSkipped": [
    { "index": 12, "reason": "invalid_timestamp", "value": "n/a" },
    { "index": 30, "reason": "no_metric_values" }
  ],
  "metricsSkippedNotOnTtd": ["mdef_torque"],
  "fieldsSkippedMissing": [
    { "metricId": "mdef_mode", "count": 3 }
  ],
  "fieldsInvalid": [
    { "metricId": "mdef_voltage", "reason": "parse_failed", "count": 2 }
  ]
}
```

## Validation and limits

Template create and update validates the spec before saving it.

- `target` and `format` must be known values with a registered handler.
- Every JSON Pointer must be valid RFC 6901.
- `timestamp.format` must be `iso8601`, `epoch_ms`, or `epoch_s`.
- Every mapped `metricId` must exist and must not be deleted.
- A template cannot map the same metric twice.
- Numeric transforms only apply to numeric metrics.
- `enumMap` only applies to enum-shaped `Int` metrics.
- Defaults must be coercible to the target metric type.
- A template can have up to 100 mappings.
- The inline serialized spec must stay under 256 KB.

Apply requests are limited to 1 MB and 1,000 records per request.

## Failure behavior

Structural problems fail the request and write nothing. Examples include an unknown template, an
unknown Thing, invalid JSON, a `recordsPointer` that does not resolve to an array, a Thing without a
Thing Type Definition, a deleted Thing Type Definition, or a missing time-series table.

Per-record and per-field problems do not fail the whole request. The platform skips the affected
record or metric, writes the rest, and includes diagnostics in the apply report.

## Related docs

- [Data model](/platform/data-model) explains Thing Type Definitions and Metric Definitions.
- [Ingesting data](/platform/ingesting-data) covers the standard telemetry ingestion path.
- [Querying data](/platform/querying-data) shows how to read telemetry after it is written.
