InfluxDB
Collection INFLUXDB and Delivery INFLUXDB_WRITE. A single External Connection serves all three
generations of the product — 1.8, 2.x and 3.x — because they are not different protocols: it is the
same HTTP API, with endpoints, authentication and query language of its own in each version.
| Version | Default language | Addressing | Authentication |
|---|---|---|---|
| 1.8 | InfluxQL | database + retention policy | Token, or username and password |
| 2.x | Flux | organization + bucket | API token |
| 3.x | SQL | database | API token |
Setting the wrong version in the connection is the most common misconfiguration here, and it shows up as HTTP 404 everywhere — not as a message about the version. Test Connection reads the server’s real version and shows it in the result, so use it before investigating anything else.
Connection
| Field | What it does |
|---|---|
| Server URL | Server root, with no path (e.g. http://influxdb:8086) |
| Version | Defines API endpoints, authentication and the default language |
| Organization (org) | 2.x only. The token belongs to an organization, and a wrong name answers 404 without saying the org is the problem |
| Default bucket (2.x) / Database (1.8 and 3.x) | Default read and write target. Collection and Delivery may override it |
| Retention policy | 1.8 only. Empty uses the database default policy |
| API token | Required on 2.x and 3.x. On 1.8, only if the server uses token authentication |
| Username / Password | 1.8 only. Sent as Basic — CMS never uses the ?u=&p= parameters, which would leave the password in the server access log |
| Validate TLS certificate / CA certificate | Same semantics as the other connections: the CA is only needed when the certificate comes from an internal CA |
| Test query (Keep Alive) | Optional. Empty, Keep Alive only checks that the server answers; filled in, it also proves the bucket answers reads |
Test Connection runs in three stages, and each one fails for a different reason: /ping
validates URL, TLS and routing (and returns the server version); the bucket listing validates the
credential and that the configured bucket exists; the test query, if any, proves reads work.
A token restricted to a single bucket — the recommended setup — cannot list the others. CMS treats that as normal: the second stage is skipped, and the test result states that the listing was not available for that credential.
Collection
The Query runs on the scheduled interval and its result becomes messages, with the same Result Mode as the SQL collections (all rows in one payload, or one message per row).
There is no Key Column + Post-Collection Command pair here: InfluxDB has no “mark row as
read”. Incremental sweeping is done through the time window of the query itself —
{{ultimaExecucao}} at the start of the window makes each run continue where the previous one
stopped, without repeating or skipping samples.
// Flux (2.x) — {{bucket}} resolves to this Collection's bucket, or the connection default
from(bucket: "{{bucket}}")
|> range(start: {{ultimaExecucao}})
|> filter(fn: (r) => r._measurement == "producao")
|> filter(fn: (r) => r._field == "temperatura")-- InfluxQL (1.8) — same idea, in that version's dialect
SELECT mean("temperatura") FROM "producao"
WHERE time > '{{ultimaExecucao}}' GROUP BY time(5m), "equipamento"The available placeholders are the same as in the HTTP collection: {{ultimaExecucao}},
{{dataHoraAtual}}, Variables and Input Parameters, plus {{bucket}}. There is no bind variable
in any of the three InfluxDB languages — the value is interpolated into the query text, so treat an
Input Parameter here as what it is: content that becomes part of the command.
How the result becomes JSON
Each result row becomes an object. In Flux the response is InfluxDB’s own annotated CSV, converted
using the types it declares — a number comes back as a number, and the timestamp keeps its
nanoseconds (it is ISO text, not a Date, precisely so nothing is truncated). The protocol control
columns (result, table) are dropped.
{ "_time": "2026-08-22T12:00:00Z", "_value": 900.5, "_field": "temperatura", "equipamento": "forno-1" }Delivery
The delivery writes points into a series. In InfluxDB the schema is born from the write: measurement, tags and fields come into existence on the first write — there is no table to create beforehand.
There are two modes:
Mapped
CMS builds the line protocol from the fields of the already transformed payload. For each field you choose whether it is a tag (indexed metadata, always text, what you filter by later) or a field (the measured value), and optionally a different name at the destination.
A payload that is an array of objects writes one point per item — the natural path for a batch coming from a collection that emitted several rows in a single message.
InfluxDB pins a field’s type on the first write. Writing 10 as an integer today makes 10.5
be rejected tomorrow with field type conflict, and fixing it requires rewriting the series. That
is why the Automatic type writes every number as a float; pick Integer only when you are
sure that field will never have a fractional part.
Line Protocol
The Content Template output (or, if it is empty, the transformed payload itself) is written as is. It is the way out for formats the mapping cannot express — and in it, line protocol escaping becomes the responsibility of whoever wrote the template.
producao,equipamento=forno-1,linha=L2 temperatura=900.5,pecas=12iTimestamp and precision
| Source | What it writes |
|---|---|
| Now | The delivery instant |
| Payload field | A field from the message (ISO 8601 or an integer epoch in the configured precision) — use it when the data was produced before reaching CMS |
| Server | Nothing: InfluxDB stamps it on ingestion |
Now stamps the point with the CMS clock, but a query window (range(start: -1h), with no
stop) ends at the InfluxDB server’s now(). If the CMS clock runs ahead of InfluxDB’s, the
point lands in the future and disappears from queries until the server catches up — the data is
written, but it does not show. Keep both clocks in NTP sync; where that is not possible, use the
Server timestamp.
Lowering the precision does not drop lines, but it rounds the instant — and two samples landing
on the same rounded instant, with the same tags, become one: InfluxDB overwrites by
measurement + tags + timestamp. When in doubt, keep ns.
What CMS blocks in the query
The collection query goes through a guard of its own, on save and again before running. What it blocks depends on the language:
| Language | Blocked |
|---|---|
| Flux | to() (writes back into a bucket) and the network, external SQL and secrets packages: http, requests, sql, secrets, slack, pagerduty and the like |
| InfluxQL | DELETE, DROP, CREATE, ALTER, GRANT, REVOKE, KILL, SET, SELECT ... INTO and multiple statements |
| SQL (3.x) | All DML, DDL and COPY (which writes a file on the server host); the query must start with SELECT, WITH, SHOW, EXPLAIN or DESCRIBE |
Text inside quotes and comments does not count: a measurement named http_requests or a column
delete_count go through normally.
As with the SQL databases, this is defense in depth. The protection that really matters is the permission of the token used in the connection: a read-only token, restricted to the integration’s bucket, makes everything else redundant.
Test environment
The project’s docker-compose.test.yml — the test environment, kept apart from the product stack —
ships an InfluxDB 2.7 ready to exercise both sides, with sample data already loaded:
docker compose -f docker-compose.test.yml up -d influxdbIn the External Connection use http://host.docker.internal:8086 — an address that works both
with cms-api in a container and running natively on the machine —, org cms,
token the INFLUX_TOKEN value from the root .env (generated by ./scripts/instalar.sh), bucket cms_leitura for the collection and cms_escrita for the delivery.