Skip to content

Connect over HTTP

HTTP is the simplest way to get a device onto CORE-M. A device that can make an authenticated POST request can send telemetry — no broker, no persistent connection, no special client library. Every other protocol in this section ultimately normalizes to the same internal TelemetryPoint that the HTTP endpoint accepts directly.

Method & pathPOST /api/v1/telemetry
Port8080 (REST, served by the gateway)
Content-Typeapplication/json
AuthAuthorization: Bearer sk_live_... or X-API-Key: sk_live_...

The endpoint is fronted by the CORE-M gateway, which authenticates the request, enforces the per-tenant rate limit, and forwards it to the telemetry service. The tenant is derived from the credential — you never put tenant_id in a place where a client could spoof it.

Present your device API key on every request, using either header:

Authorization: Bearer sk_live_8f3c2a...
X-API-Key: sk_live_8f3c2a...

A request with no valid credential is rejected with 401 and the payload is not published. Do not embed the key in URLs or logs.

The body is a TelemetryPoint (or a batch of them — see Batch ingest). A single point has this shape:

{
"device_id": "d7b1c0e2-3f44-4a91-9b2e-2c5a1f0e9d33",
"tenant_id": "t_acme",
"timestamp": "2026-05-29T14:03:00Z",
"numeric_values": {
"temperature": 22.5,
"humidity": 65
},
"string_values": {
"firmware": "1.4.2",
"state": "running"
}
}
FieldTypeNotes
device_idstringThe platform-assigned device UUID.
tenant_idstringThe owning tenant. Must match the credential’s tenant.
timestampstring (RFC 3339)When the reading was taken. Omit to let the server stamp it on arrival.
numeric_valuesmap<string, double>Floating-point sensor readings, keyed by metric name.
string_valuesmap<string, string>String-typed readings (status text, modes, versions).
  1. Build the JSON body for one reading.

  2. POST it with your API key.

    Terminal window
    curl -X POST https://ingest.kronoxdata.com:8080/api/v1/telemetry \
    -H "Authorization: Bearer sk_live_8f3c2a..." \
    -H "Content-Type: application/json" \
    -d '{
    "device_id": "d7b1c0e2-3f44-4a91-9b2e-2c5a1f0e9d33",
    "tenant_id": "t_acme",
    "timestamp": "2026-05-29T14:03:00Z",
    "numeric_values": { "temperature": 22.5, "humidity": 65 },
    "string_values": { "state": "running" }
    }'
  3. On success the endpoint returns 200 with an ingest ack that counts how many points it took:

    {
    "accepted": 1,
    "rejected": 0
    }

The ack is per-point, not per-request: in a batch, malformed or unknown-device points increment rejected while the rest are still accepted.

To amortize connection and auth overhead, send many points in one request. The batch body wraps the points in a points array:

{
"points": [
{
"device_id": "d7b1c0e2-...",
"tenant_id": "t_acme",
"timestamp": "2026-05-29T14:03:00Z",
"numeric_values": { "temperature": 22.5 }
},
{
"device_id": "d7b1c0e2-...",
"tenant_id": "t_acme",
"timestamp": "2026-05-29T14:03:10Z",
"numeric_values": { "temperature": 22.7 }
}
]
}
Terminal window
curl -X POST https://ingest.kronoxdata.com:8080/api/v1/telemetry \
-H "Authorization: Bearer sk_live_8f3c2a..." \
-H "Content-Type: application/json" \
--data-binary @batch.json

The response uses the same { "accepted": N, "rejected": M } shape, summed over the batch.

HTTP ingest is one-way. For server→device messages (shared-attribute pushes, RPC requests, command delivery), a device opens a long-lived Server-Sent Events stream and reads downlinks as they arrive. Each event is a single data: line carrying a JSON-encoded downlink, terminated by a blank line per the SSE spec:

data: {"kind":"rpc","correlation_id":"c-91a2","method":"reboot","params":{}}
data: {"kind":"shared_attributes","attributes":{"reporting_interval":30}}

The device keeps the stream open and reacts to each event; if no stream is connected, downlinks queue until one is. Replies (e.g. an RPC response) are sent back as normal authenticated uplinks.

sequenceDiagram
    participant Dev as Device
    participant GW as Gateway :8080
    participant Tel as Telemetry service
    participant RP as Redpanda telemetry.raw.{tenant}

    Dev->>GW: POST /api/v1/telemetry (Bearer key, JSON)
    GW->>GW: Authenticate + rate limit
    alt invalid credential
        GW-->>Dev: 401 (payload not published)
    else authenticated
        GW->>Tel: IngestTelemetry(points)
        Tel->>Tel: Normalize to TelemetryPoint(s)
        Tel->>RP: Publish accepted points
        Tel-->>GW: { accepted, rejected }
        GW-->>Dev: 200 { accepted, rejected }
    end

Once the first point lands, the device flips to online. If it stops sending for longer than the offline threshold (default 120 seconds) it is marked offline again.

Reach for HTTP when…

  • The device has a normal TCP/TLS stack and an HTTP client.
  • Telemetry is periodic (every few seconds or slower), not a high-rate stream.
  • You want the least possible moving parts — no broker, no DTLS, no session.
  • You are prototyping or integrating from a backend, script, or serverless function.

If you need persistent sessions, server-push at scale, or QoS, look at MQTT. For battery- and bandwidth-constrained devices over UDP, see CoAP.