Skip to main content

Telemetry

POST /telemetry is the single write path into the platform. Gateways, simulators, and batch jobs push JSON arrays of records; everything the Topology projection and the SNR prediction features consume flows through here.

POST /telemetry
Content-Type: application/json

Auth: x-api-key. See Authentication.

Request body

The body is a bare JSON array of records, not a wrapper object:

[
{
"measurement": "satellite",
"time": "2026-07-09T14:31:42Z",
"tags": { "node_id": "SAT-0012", "node_type": "satellite" },
"fields": { "lat": 47.61, "lon": -122.33, "altitude_km": 550.2, "utilization": 0.42 }
},
{
"measurement": "link",
"time": "2026-07-09T14:31:42Z",
"tags": { "link_id": "SAT-0012:GS-SEA-01", "node_id": "SAT-0012" },
"fields": { "snr_db": 12.4, "utilization": 0.55 }
}
]
FieldTypeRequiredDescription
measurementstring, non-emptyyesSeries name. Any non-empty name is accepted; there is no write allowlist.
timeISO 8601 datetimeyesSample timestamp.
tagsobject of string valuesno, default emptyIndexed dimensions. Use node_id (and link_id for links) so topology and predictions can key entities.
fieldsobject of number or string valuesno, default emptyThe measured values.

Two things happen server-side that you do not control:

  • Tenant stamping. Every record is force-stamped with your tenant, derived from the API key. You cannot write into another tenant, and any tenant value you supply is overwritten.
  • No write allowlist. Unlike topology reads, writes accept any measurement name. But only link, satellite, ground_station, weather, and telemetry are readable back through GET /topology, so records under other names are stored but invisible to the current read surface. Stick to the five canonical measurements unless the platform team has agreed otherwise.

Response: 202 acknowledgment

Ingest is acknowledged, not silently swallowed:

{
"write_id": "w-8c41f2ae",
"accepted_count": 118,
"rejected_count": 2,
"rejected_indices": [4, 87]
}
FieldDescription
write_idServer identifier for this write; log it for support and reconciliation.
accepted_countRecords written.
rejected_countRecords rejected.
rejected_indicesZero-based indices into your submitted array, or null when nothing was rejected.

Handling partial rejection

A 202 with rejected_count > 0 means the accepted records are already written. Never resubmit the whole batch: the accepted rows would be written a second time. Instead:

  1. Map rejected_indices back to the records you sent.
  2. Fix or drop those records (a common cause is a malformed field value).
  3. Resubmit only the corrected records.

Limits

LimitValueError
Records per batch1,000413 {"error": "batch_too_large", "max": 1000, "received": N}
Body size1 MB413 {"error": "request_too_large", ...}
Content-Lengthrequired411 {"error": "length_required"}

Chunk on record count first; 1,000 typical records fit well under 1 MB. A batching loop is in Ingest telemetry.

Idempotency warning

Telemetry ingest is not idempotent and the platform does not deduplicate. Retrying a batch that was already written writes every record twice, which skews utilization rollups and prediction features.

  • Safe to resubmit: 429, 413, 422, 400. These were rejected before ingest.
  • Ambiguous: timeouts and dropped connections after the request was sent. The batch may or may not have landed. Log the batch and its intended write_id context for reconciliation instead of auto-replaying, or accept the double-write risk explicitly.

Example

curl -sS -X POST \
-H "x-api-key: $CONSTELLATION_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{
"measurement": "ground_station",
"time": "2026-07-09T14:31:55Z",
"tags": { "node_id": "GS-SEA-01", "node_type": "ground_station" },
"fields": { "lat": 47.44, "lon": -122.3, "utilization": 0.61 }
}
]' \
"https://api.constellation.space/telemetry"

Errors

StatusBodyCause
422detail listA record failed schema validation (empty measurement, bad time, wrong tag types).
413batch_too_large or request_too_largeOver the batch or body cap; split and resend.
411 / 400length_required / invalid_content_lengthContent-Length problems.
401 / 403 / 404see AuthenticationKey or tenant problems.
429rate_limited or auth_lockoutRate limits; honor Retry-After.
503{"error": "telemetry_unavailable", "upstream": "influx", "retry_after_seconds": 5}Backend transient; retrying is a write, so mind the idempotency warning.