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 600s (10 minutes), set per-profile via inactivity_timeout_ms (there is no global env var)) 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.