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
| Method | Path | Description |
|---|---|---|
POST | /api/v1/ingest | Ingest a batch of readings (API key) |
GET | /api/v1/machines/{machine_id}/readings/latest | Latest value per metric |
GET | /api/v1/machines/{machine_id}/readings | Time-series history |
GET | /api/v1/machines/{machine_id}/summary | Aggregated stats per metric |
GET | /api/v1/machines/{machine_id}/tag-mappings | Tag mappings for a machine |
GET | /api/v1/machines/{machine_id}/baselines | Computed baselines per metric |
POST | /api/v1/ingest-sources | Create a polling source |
GET | /api/v1/ingest-sources | List 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-connection | Test 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
| Field | Type | Required | Constraints |
|---|---|---|---|
machine_identifier | string | Yes | Must match a registered machine (1–255 chars) |
timestamp | string | Yes | ISO 8601 UTC; up to 5 minutes in the future |
metric_name | string | Yes | 1–255 chars |
value | number | Yes | Must be finite (no NaN or Infinity) |
unit | string | Yes | Unit label, for example celsius, mm/s, psi |
raw_tag | string | No | Original tag name from the source system |
idempotency_key | string | No | A 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"
}
]
}'
Response — 207 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.
| Parameter | Type | Description |
|---|---|---|
start | datetime | Start of the range (ISO 8601 UTC) |
end | datetime | End of the range (ISO 8601 UTC) |
metric | string | Filter to a single metric (up to 255 chars) |
interval | string | Downsample bucket: 1m, 5m, 15m, 1h, 6h, 1d |
Response — 200 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
| Field | Type | Required | Description |
|---|---|---|---|
machine_id | uuid | Yes | Machine the source feeds |
name | string | Yes | Source name (1–200 chars) |
protocol | string | Yes | opcua or modbus |
host | string | Yes | Externally reachable host (1–255 chars) |
port | integer | Yes | 1–65535 |
poll_interval_seconds | integer | No | 5–3600 (default 30) |
is_active | boolean | No | Whether polling is enabled (default true) |
config | object | No | Protocol-specific settings |
Response — 201 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."
}