External development
Besides authoring rules in the Data Preparation application, you can develop, version, test, and deploy rules from your own development environment using the open-source Data Preparation rules template repository.
What it is
The template repository is a ready-to-use project for developing Data Preparation rules in an IDE such as Visual Studio Code instead of the in-app editor. Each rule lives in its own folder with a TypeScript smart function, a data-prep.yaml configuration, and test cases. The repository ships with the tooling to type-check, lint, validate, test, and deploy rules.
Why use it
Developing rules externally gives you:
- Version control — keep rules in Git alongside the rest of your IoT solution, with full history and review workflows.
- IDE tooling — TypeScript IntelliSense, inline schema validation for the YAML files, linting that flags unsupported runtime features, and AI coding assistance.
- CI/CD pipelines — run offline checks on every change and deploy automatically from GitHub Actions.
Getting started
Fork or clone the template repository and follow its README. The typical workflow is:
- Create a rule from the provided scaffolding command.
- Validate it offline (type-check, lint, schema validation).
- Test it against your tenant using the platform’s testing endpoint.
- Deploy it to your tenant.
Refer to the template repository’s README for the full developer guide, including prerequisites, credentials, and CI/CD setup.
TypeScript API reference
Smart function input and output types are published as the @c8y/dataprep-types npm package. The generated TypeScript API reference (TypeDoc) is available at Data Preparation Smart Function API.
REST API
The Data Preparation control plane REST API — used by the template’s test and deploy tooling — is documented it the Cumulocity OpenAPI Specification. To access interactive API documentation within your tenant, subscribe to and install the Api-doc extension from Administration > Ecosystem > Extensions and open the API documentation application > Data Preparation tab.
You can also retrieve the raw OpenAPI JSON specification directly:
curl -u '<username>' 'https://<your-tenant>/service/dataprep/v3/api-docs'
Rule package format
Rules are deployed as a gzip-compressed TAR archive (tar.gz). This section describes the archive structure required by the deploy endpoint. The npm run deploy script in the template repository builds archives in this format automatically.
Archive structure
Files must be at the root level of the archive:
| Path | Required | Description |
|---|---|---|
| data-prep.yaml | Yes | Rule configuration (see below). |
<smartFunctionFile> |
Yes | Bundled Javascript file. The filename must match the smartFunctionFile field in data-prep.yaml. |
| tests/*.yaml | No | Test cases; each file becomes a named test stored on the deployed rule. |
Any other files in the archive are silently ignored.
Archives are subject to the following limits:
- Maximum 100 entries per archive.
- Maximum 1 MB per file (
dataprep/rule-import.max-file-size). - Maximum 10 MB total extracted size (
dataprep/rule-import.max-total-size). - Entries containing path traversal segments (
..) are rejected.
data-prep.yaml schema
| Field | Type | Required | Description |
|---|---|---|---|
smartFunctionFile |
string | Yes | Filename of the bundled Javascript file at the archive root. |
input |
object | Yes | Transport and topic configuration (see below). |
description |
string | No | Human-readable rule description. |
tags |
string[] | No | Tag list. |
disabled |
boolean | No | Whether the rule deploys as disabled; defaults to false. |
The input object:
| Field | Type | Required | Description |
|---|---|---|---|
transport |
string | Yes | Transport type. Currently, only "mqtt" is supported. |
topicPattern |
string | Yes | Topic filter. Use * as a wildcard to match zero or more characters. Transport-specific wildcards such as + and # are not supported. |
clientIDPattern |
string | No | Device client ID filter. Use * as a wildcard to match zero or more characters. |
transportID |
string | No | Transport connection identifier. If not specified, this field falls back to the transport value. |
Example data-prep.yaml:
smartFunctionFile: transform.js
description: "Converts temperature sensor readings to Cumulocity measurements"
tags:
- sensor
- temperature
input:
transport: mqtt
topicPattern: "sensors/temperature/*"
clientIDPattern: "*"
Test file format
Each .yaml file under tests/ defines a named test case. The filename without extension is the test name. Test files are optional — if included in the archive they are stored server-side on the deployed rule.
| Field | Type | Required | Description |
|---|---|---|---|
inputs |
array | Yes | One or more device messages to send as input (see below). |
expectedOutput |
array | No | Expected Cumulocity object outputs used for comparison against actual outputs when running tests. |
Each item in inputs has the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
payload |
string | Yes | Message payload. |
payloadFormat |
string | Yes | Format of the payload field. One of text, json, or base64. |
topic |
string | Yes | Topic string. |
clientID |
string | Yes | Device client ID. |
time |
string | Yes | ISO 8601 timestamp, for example 2026-01-01T12:00:00.000Z. |
Example tests/temperature-reading.yaml:
inputs:
- payload: |
{
"temperature": 25.5
}
payloadFormat: json
topic: "devices/sensor01/data"
clientID: "sensor01"
time: "2026-01-01T12:00:00.000Z"
expectedOutput:
- cumulocityType: measurement
externalSource:
- externalId: sensor01
type: c8y_Serial
payload:
type: c8y_Weather
time: "2026-01-01T12:00:00.000Z"
c8y_Temperature:
T:
value: 25.5
unit: "°C"
Deploy endpoint
PUT /service/dataprep/v1/rules/{name}/deployed
Content-Type: application/gzip
The rule name comes from the URL path, not from the archive. If a rule with the given name already exists, it is overwritten. The endpoint requires the DATA_PREPARATION_DEPLOYMENTS_ADMIN permission.
| Response code | Meaning |
|---|---|
200 |
Rule deployed successfully. |
400 |
Malformed archive, missing required files, invalid YAML, or invalid rule name. |
413 |
An archive entry exceeds the maximum permitted size. |
422 |
Package is structurally valid but the rule fails domain validation (for example, topicPattern contains **). |
Example using curl:
curl -X PUT \
-H "Content-Type: application/gzip" \
-H "Authorization: <token>" \
--data-binary @my-rule.tar.gz \
'https://<your-tenant>/service/dataprep/v1/rules/my-rule/deployed'