Register a stream
One call that creates the dataset, its channel columns, and the storage behind them.
Every ingestion path starts here. POST /api/streams/register creates three
things at once:
- a dataset row for this run
- a channel snapshot — your channels, mapped to
ch_0,ch_1, … - the time-series table the data will actually live in, with the column
types your declared
dataTypes imply
The third is why this call and not datasets/create: registration is the only
HTTP path that leaves you with storage you can immediately write to. If it
fails to provision, it fails the whole request rather than handing back a
dataset whose first append would be refused.
Do not use POST /api/datasets/create for this. It makes a dataset row and
nothing else — no channels, no storage — and appending to it fails with a 409
until something else provisions the table. register is what you want in almost
every case.
The call
/api/streams/registerscope datasets:writeProp
Type
curl -s -X POST https://app.xpectraflow.com/api/streams/register \
-H "x-api-key: $XPECTRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"experimentId": "'"$EXPERIMENT_ID"'",
"datasetName": "cold-flow-014",
"sampleRateHz": 1000,
"format": "columnar-v1",
"channels": [
{ "name": "thrust", "unit": "kN", "dataType": "float" },
{ "name": "chamber_pressure", "unit": "bar", "dataType": "float" },
{ "name": "valve_open", "unit": "", "dataType": "bool" }
]
}'{
"datasetId": "3d81f0a2-55c7-4f9e-9a1b-77c2e0d43a10",
"hypertableName": "hyper-3d81f0a2-55c7-4f9e-9a1b-77c2e0d43a10",
"channelIndices": [0, 1, 2],
"ingestToken": "b7f1…9ac2"
}Reading the response
Declaring channels well
Declare all of them up front
The order fixes the indices, and the column types are set from dataType when
the table is created. Adding a channel later is possible but does not widen the
table — see the channels page.
Get dataType right the first time
float → DOUBLE PRECISION, int → BIGINT, bool → BOOLEAN. Changing it
afterwards relabels the channel; it does not convert data already written or
alter the column.
Use names you will recognise in six months
chamber_pressure, not ch3. The name is what append accepts as an alias, and
what appears on every chart axis.
Narrow streams
Omit channels entirely and set the shape:
curl -s -X POST https://app.xpectraflow.com/api/streams/register \
-H "x-api-key: $XPECTRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"experimentId": "'"$EXPERIMENT_ID"'",
"datasetName": "sortie-2026-07-31-sysid1",
"storageShape": "narrow",
"codec": "mavlink",
"dialect": "ardupilotmega",
"format": "mavlink-raw-v1"
}'channelIndices comes back empty, which is correct: a narrow dataset's signals
are discovered from the data as it arrives, because which messages a vehicle
sends is not knowable before it starts talking.
A wide stream with no channels is rejected — a wide table's columns are its schema, so it cannot be provisioned without them:
"details": [{ "field": "channels", "message": "at least one channel is required for a wide stream" }]Errors
| Status | code | When |
|---|---|---|
| 400 | invalid_request | No channels on a wide stream, unknown codec, more than 4096 channels |
| 403 | insufficient_scope | Key lacks datasets:write |
| 404 | not_found | No such experiment in this organization |