Skip to main content

Sensor Data

Push sensor readings into Haltless and query them back as latest values, time-series history, or aggregated summaries. Ingestion at `POST /api/v1/ingest` authenticates with an API key; every other endpoint on this page requires a bearer JWT.

Endpoints

MethodPathDescription
POST/api/v1/ingestIngest a batch of readings (API key)
GET/api/v1/machines/{machine_id}/readings/latestLatest value per metric
GET/api/v1/machines/{machine_id}/readingsTime-series history
GET/api/v1/machines/{machine_id}/summaryAggregated stats per metric
GET/api/v1/machines/{machine_id}/tag-mappingsTag mappings for a machine
GET/api/v1/machines/{machine_id}/baselinesComputed baselines per metric
POST/api/v1/ingest-sourcesCreate a polling source
GET/api/v1/ingest-sourcesList polling sources
GET/api/v1/ingest-sources/{source_id}Get a polling source
PUT/api/v1/ingest-sources/{source_id}Update a polling source
DELETE/api/v1/ingest-sources/{source_id}Delete a polling source
POST/api/v1/ingest-sources/{source_id}/test-connectionTest a source connection

Ingest readings

POST /api/v1/ingest

Authenticates with an API key sent in the X-API-Key header — not a bearer JWT. Send up to 1,000 readings per request. See the API Keys guide for issuing keys and the Direct Ingestion guide for a walkthrough.

Reading fields

FieldTypeRequiredConstraints
machine_identifierstringYesMust match a registered machine (1–255 chars)
timestampstringYesISO 8601 UTC; up to 5 minutes in the future
metric_namestringYes1–255 chars
valuenumberYesMust be finite (no NaN or Infinity)
unitstringYesUnit label, for example celsius, mm/s, psi
raw_tagstringNoOriginal tag name from the source system
idempotency_keystringNoA UUID that dedupes a reading across retries
curl -X POST https://api.haltless.io/api/v1/ingest \
-H "X-API-Key: <api-key>" \
-H "Content-Type: application/json" \
-d '{
"readings": [
{
"machine_identifier": "CNC-001",
"timestamp": "2026-08-07T10:30:00Z",
"metric_name": "temperature",
"value": 72.5,
"unit": "celsius"
},
{
"machine_identifier": "CNC-001",
"timestamp": "2026-08-07T10:30:00Z",
"metric_name": "vibration",
"value": 3.2,
"unit": "mm/s",
"raw_tag": "ns2_i1002"
}
]
}'

Response207 Multi-Status

{
"accepted_count": 2,
"rejected_count": 0,
"errors": []
}

When some readings fail, the accepted ones are still stored and each rejected reading is reported by its position in the batch:

{
"accepted_count": 1,
"rejected_count": 1,
"errors": [
{
"index": 1,
"machine_identifier": "UNKNOWN-001",
"error": "Machine not found"
}
]
}

Latest readings

GET /api/v1/machines/{machine_id}/readings/latest

Returns the most recent reading for each metric, keyed by metric name.

{
"machine_id": "550e8400-e29b-41d4-a716-446655440000",
"readings": {
"temperature": {
"timestamp": "2026-08-07T10:30:00Z",
"value": 72.5,
"unit": "celsius",
"raw_tag": null
},
"vibration": {
"timestamp": "2026-08-07T10:30:00Z",
"value": 3.2,
"unit": "mm/s",
"raw_tag": "ns2_i1002"
}
}
}

Time-series history

GET /api/v1/machines/{machine_id}/readings

Query historical readings, optionally downsampled to a fixed interval. When start and end are omitted, the last 24 hours are returned.

ParameterTypeDescription
startdatetimeStart of the range (ISO 8601 UTC)
enddatetimeEnd of the range (ISO 8601 UTC)
metricstringFilter to a single metric (up to 255 chars)
intervalstringDownsample bucket: 1m, 5m, 15m, 1h, 6h, 1d

Response200 OK

[
{
"metric_name": "temperature",
"unit": "celsius",
"data_points": [
{ "timestamp": "2026-08-07T00:00:00Z", "value": 71.2 },
{ "timestamp": "2026-08-07T00:05:00Z", "value": 72.1 }
]
}
]

Machine summary

GET /api/v1/machines/{machine_id}/summary

Returns min, max, average, and latest values for each metric.

{
"machine_id": "550e8400-e29b-41d4-a716-446655440000",
"metrics": [
{
"metric_name": "temperature",
"unit": "celsius",
"min_value": 68.4,
"max_value": 87.3,
"avg_value": 73.1,
"latest_value": 72.5,
"latest_timestamp": "2026-08-07T10:30:00Z"
}
]
}

Tag mappings for a machine

GET /api/v1/machines/{machine_id}/tag-mappings

Returns every tag-to-metric mapping for the machine. To create or correct mappings, see Machines.

[
{
"id": "9b2f...",
"tenant_id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"machine_id": "550e8400-e29b-41d4-a716-446655440000",
"raw_tag": "ns2_i1001_temp_motor_1",
"standardized_name": "motor_temperature",
"unit": "celsius",
"is_confirmed": true,
"confidence_score": 0.94,
"created_at": "2026-08-07T09:00:00Z",
"updated_at": "2026-08-07T09:05:00Z"
}
]

Metric baselines

GET /api/v1/machines/{machine_id}/baselines

Returns computed baseline statistics (mean and standard deviation) for each metric.

[
{
"id": "a1b2...",
"machine_id": "550e8400-e29b-41d4-a716-446655440000",
"metric_name": "temperature",
"mean_value": 72.8,
"std_dev": 1.9,
"min_value": 68.4,
"max_value": 79.0,
"sample_count": 420,
"window_start": "2026-07-31T00:00:00Z",
"window_end": "2026-08-07T00:00:00Z"
}
]

Polling sources

Polling sources let Haltless connect to an OPC-UA or Modbus endpoint and pull readings on a schedule, as an alternative to pushing to `/api/v1/ingest`. These endpoints require an operator or administrator role.

Create a source

POST /api/v1/ingest-sources

FieldTypeRequiredDescription
machine_iduuidYesMachine the source feeds
namestringYesSource name (1–200 chars)
protocolstringYesopcua or modbus
hoststringYesExternally reachable host (1–255 chars)
portintegerYes165535
poll_interval_secondsintegerNo53600 (default 30)
is_activebooleanNoWhether polling is enabled (default true)
configobjectNoProtocol-specific settings

Response201 Created

{
"id": "c3d4...",
"tenant_id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"machine_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Line 3 OPC-UA",
"protocol": "opcua",
"host": "opc.example.com",
"port": 4840,
"poll_interval_seconds": 30,
"is_active": true,
"config": {},
"last_polled_at": null,
"last_error": null,
"created_at": "2026-08-07T10:00:00Z",
"updated_at": "2026-08-07T10:00:00Z"
}

List, get, update, and delete

GET /api/v1/ingest-sources lists your sources and GET /api/v1/ingest-sources/{source_id} returns one. PUT /api/v1/ingest-sources/{source_id} updates a source and DELETE /api/v1/ingest-sources/{source_id} removes it (204 No Content).

Test a connection

POST /api/v1/ingest-sources/{source_id}/test-connection attempts to connect to the configured endpoint and reports the result.

{
"success": true,
"message": "Connected successfully."
}