Supported Protocols
The Haltless edge agent and direct ingestion system support multiple industrial protocols.
HTTP Push (Edge Agent)
The most common approach. Your agent collects data from any source and pushes it via HTTP.
Endpoint
POST /api/v1/ingest
Authentication
X-API-Key: your_api_key
Request body
{
"readings": [
{
"machine_identifier": "CNC-001",
"timestamp": "2026-04-04T10:30:00Z",
"metric_name": "temperature",
"value": 72.5,
"unit": "celsius",
"raw_tag": "ns2_i1001"
}
]
}
Batch limits
| Parameter | Limit |
|---|---|
| Max readings per batch | 1,000 |
| Max request body size | 10 MB |
| Timestamp range | 3,650 days past to 5 minutes future |
Response
HTTP 207 Multi-Status:
{
"accepted_count": 1,
"rejected_count": 0,
"errors": []
}
If some readings fail:
{
"accepted_count": 5,
"rejected_count": 2,
"errors": [
{
"index": 3,
"machine_identifier": "UNKNOWN-001",
"error": "Machine not found"
},
{
"index": 6,
"machine_identifier": "CNC-001",
"error": "Value must be finite"
}
]
}
Python example
import requests
import time
API_KEY = "hk_your_api_key"
API_URL = "https://api.haltless.io/api/v1/ingest"
readings = [
{
"machine_identifier": "CNC-001",
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"metric_name": "temperature",
"value": 72.5,
"unit": "celsius",
},
{
"machine_identifier": "CNC-001",
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"metric_name": "vibration",
"value": 3.2,
"unit": "mm/s",
},
]
response = requests.post(
API_URL,
json={"readings": readings},
headers={"X-API-Key": API_KEY},
)
print(response.json())
# {"accepted_count": 2, "rejected_count": 0, "errors": []}
TypeScript/Node.js example
const API_KEY = "hk_your_api_key";
const API_URL = "https://api.haltless.io/api/v1/ingest";
const response = await fetch(API_URL, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
readings: [
{
machine_identifier: "CNC-001",
timestamp: new Date().toISOString(),
metric_name: "temperature",
value: 72.5,
unit: "celsius",
},
],
}),
});
const result = await response.json();
console.log(result);
OPC-UA
OPC-UA (Open Platform Communications Unified Architecture) is the standard for industrial automation.
Edge agent configuration
[[source]]
name = "cnc-mill-1"
machine_identifier = "CNC-001"
protocol = "opcua"
[source.opcua]
endpoint = "opc.tcp://192.168.1.100:4840"
security_mode = "SignAndEncrypt"
security_policy = "Basic256Sha256"
username = "haltless"
password = "secure_password"
[[source.opcua.nodes]]
node_id = "ns=2;i=1001"
metric_name = "spindle_temperature"
unit = "celsius"
[[source.opcua.nodes]]
node_id = "ns=2;s=Vibration.X"
metric_name = "vibration_x"
unit = "mm/s"
Direct ingestion (cloud polling)
If your OPC-UA server is accessible from the internet, you can use direct ingestion instead of an edge agent:
curl -X POST https://api.haltless.io/api/v1/ingest-sources \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{
"machine_id": "MACHINE_UUID",
"name": "CNC Mill OPC-UA",
"protocol": "opcua",
"host": "opcua.yourfactory.com",
"port": 4840,
"poll_interval_seconds": 30,
"is_active": true,
"config": {
"nodes": [
{"node_id": "ns=2;i=1001", "metric_name": "temperature", "unit": "celsius"},
{"node_id": "ns=2;i=1002", "metric_name": "vibration", "unit": "mm/s"}
]
}
}'
Security modes
| Mode | Description |
|---|---|
None | No security (development only) |
Sign | Messages are signed but not encrypted |
SignAndEncrypt | Full security (recommended for production) |
Node ID formats
| Format | Example |
|---|---|
| Numeric | ns=2;i=1001 |
| String | ns=2;s=Temperature.Value |
| GUID | ns=2;g=09087e75-8e5e-499b-954f-f2a9603db28a |
| Opaque | ns=2;b=M/RbKBsRVkePCePcx24oRA== |
Modbus TCP
Modbus TCP is widely used for PLCs, sensors, and industrial controllers.
Edge agent configuration
[[source]]
name = "conveyor-motor"
machine_identifier = "CONV-001"
protocol = "modbus"
[source.modbus]
host = "192.168.1.101"
port = 502
unit_id = 1
poll_interval = 10
[[source.modbus.registers]]
address = 100
count = 1
register_type = "holding"
metric_name = "motor_temperature"
unit = "celsius"
scale = 0.1
offset = 0.0
[[source.modbus.registers]]
address = 102
count = 1
register_type = "input"
metric_name = "motor_speed"
unit = "rpm"
scale = 1.0
offset = 0.0
Direct ingestion (cloud polling)
curl -X POST https://api.haltless.io/api/v1/ingest-sources \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{
"machine_id": "MACHINE_UUID",
"name": "Conveyor Motor Modbus",
"protocol": "modbus",
"host": "modbus.yourfactory.com",
"port": 502,
"poll_interval_seconds": 10,
"is_active": true,
"config": {
"registers": [
{
"address": 100,
"count": 1,
"register_type": "holding",
"metric_name": "temperature",
"unit": "celsius",
"scale": 0.1,
"offset": 0.0
}
]
}
}'
Register types
| Type | Modbus function | Description |
|---|---|---|
holding | FC 03 | Read/write registers (most common) |
input | FC 04 | Read-only registers |
Scaling
Raw Modbus values are often integers. Use scale and offset to convert:
actual_value = (raw_value * scale) + offset
Example: A temperature sensor returns 725 as a raw value with scale: 0.1 and offset: 0.0:
725 * 0.1 + 0.0 = 72.5 celsius
Testing connections
You can test direct ingestion connectivity before enabling:
curl -X POST https://api.haltless.io/api/v1/ingest-sources/SOURCE_ID/test-connection \
-H "Authorization: Bearer YOUR_JWT"
{
"success": true,
"message": "Connected successfully",
"latency_ms": 45
}