Skip to main content
Version: V3.2

Insert Data

The Data API allows you to insert time-series data into the Capture platform using two authentication methods: JWT tokens or API tokens.

info

Timestamps are specified in UTC time.

Endpoint

URLhttps://portal.captureplatform.com/api/data
METHODPOST

Authentication Methods

The Data API supports two authentication methods. Choose the one that best fits your use case.

Option 1: JWT Token Authentication

When to use: Use JWT tokens when you have edge devices or loggers that need to authenticate and send data directly to the platform. This method is ideal for IoT devices and embedded systems that collect data autonomously.

tip

To be able to insert data, a logger user is needed and a "retention to write" needs to be attached to the logger.

Headers

HeaderValue
AuthVersionV0.0.1
DataVersionV0.0.3
AuthorizationBearer <Received auth token>

Example Request

curl -X POST "https://portal.captureplatform.com/api/data" \
-H "AuthVersion: V0.0.1" \
-H "DataVersion: V0.0.3" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"Metrics": [
{
"Name": "temperature",
"Tags": {
"device_id": "logger_001",
"location": "warehouse_A"
},
"Fields": {
"value": 22.5,
"humidity": 65
},
"Timestamp": "1708876800"
}
],
"Timestamp": "1708876800"
}'

Option 2: API Token Authentication

When to use: Use API tokens for backend services, scheduled jobs, or applications that integrate with the Capture platform. This method provides more flexibility as you can specify the target database and retention policy directly in the request.

tip

Make sure the API token has write permissions on the desired database.

Headers

HeaderValue
AuthVersionV0.0.1
DataVersionV0.0.5
Content-Typeapplication/json
AuthorizationApiToken <Your api token>

Query Parameters

ParameterValue
Database<UID of the database>
Retention<Name of the retention>
tip

The UID of the database can be found in the edit database panel on the Capture platform.

Example Request

curl -X POST "https://portal.captureplatform.com/api/data?Database=a1b2c3d4-e5f6-7890-abcd-ef1234567890&Retention=oneYear" \
-H "AuthVersion: V0.0.1" \
-H "DataVersion: V0.0.5" \
-H "Content-Type: application/json" \
-H "Authorization: ApiToken cap_1a2b3c4d5e6f7g8h9i0j" \
-d '{
"Metrics": [
{
"Name": "production_data",
"Tags": {
"machine_id": "CNC_001",
"shift": "morning"
},
"Fields": {
"parts_produced": 150,
"defect_count": 2,
"efficiency": 98.7
},
"Timestamp": "1708876800"
}
],
"Timestamp": "1708876800"
}'

Key Differences:

FeatureJWT TokenAPI Token
Use CaseEdge devices, IoT sensors, loggersBackend services, integrations, scheduled jobs
Database TargetPre-configured with loggerSpecified in query parameters
FlexibilityFixed retention per loggerCan switch databases and retentions
Token ManagementObtained via authentication flowGenerated and managed in Capture platform
Best ForAutonomous data collection at the edgeCentralized data management and integration

Request Body

The request body structure is the same regardless of the authentication method used. It contains an array of Metrics with a MeasurementName, Tags, Fields, and Timestamps. We call this object a CaptureObject.

Here's a complete example showing how to insert data into two different measurements. This example includes 2 rows for MeasurementName (with different serial numbers and machine IDs) and 1 row for DifferentMeasurementName:

{
"Metrics": [
{
"Name": "MeasurementName",
"Tags": {
"SerialNumber": "123456",
"MachineId": "Machine1"
},
"Fields": {
"sensor1": 20,
"sensor2": 50.5
},
"Timestamp": "1585554027" // Time of data collection. This gets inserted into the database.
},
{
"Name": "MeasurementName",
"Tags": {
"SerialNumber": "123457",
"MachineId": "Machine2"
},
"Fields": {
"sensor1": 30,
"sensor2": 15.9
},
"Timestamp": "1585554028"
},
{
"Name": "DifferentMeasurementName",
"Tags": {
"OperatorId": "654321"
},
"Fields": {
"action1": "This is an action"
},
"Timestamp": "1585559852"
}
],
"Timestamp": "1588765687" // Optional. Used in background to sort data for more efficient inserting.
}

This request will create the following tables in your database:

MeasurementName table:

timestampSerialNumber (tag)MachineId (tag)sensor1 (field)sensor2 (field)
1585554027123456Machine120.050.5
1585554028123457Machine230.015.9

DifferentMeasurementName table:

timestampOperatorId (tag)action1 (field)
1585559852654321"This is an action"

Field Types

The value of the field can be one of the following types.

Primitives

TypeExample
Float"sensor1": 30.0
Int"sensor1": 30
String"sensor1": "30"
Bool"sensor1": true
warning

If a field contains a base64 encoded blob, there is a limit of 1024 bytes. If you need to store larger blobs, link a blobstore and those items will be automatically uploaded to the blobstore.

Binary / Blob Fields

To upload binary data (images, files, etc.) directly to a linked blobstore, add the DataType: PlainWithBinary header to your request and encode the field value as a data URI:

data:<mimetype>[;<key>=<value>]*;base64,<base64-encoded-data>

The field value in the database will be replaced with the blob URL after a successful upload.

Supported metadata parameters:

ParameterDescriptionExample
extFile extension to use in the blob nameext=jpeg
dstpathCustom path/filename in the blobstore. If omitted, the path is auto-generated as <measurement>/<field>/<timestamp>_<tagshash>.<ext>dstpath=images/camera1/photo.jpg

Example — uploading a JPEG image:

curl -X POST "https://portal.captureplatform.com/api/data?Database=<db-uid>&Retention=oneYear" \
-H "AuthVersion: V0.0.1" \
-H "DataVersion: V0.0.5" \
-H "DataType: PlainWithBinary" \
-H "Content-Type: application/json" \
-H "Authorization: ApiToken YOUR_SECRET_API_TOKEN_HERE" \
-d '{
"Metrics": [
{
"Name": "inspections",
"Tags": { "camera": "cam_01" },
"Fields": {
"image": "data:image/jpeg;ext=jpeg;base64,/9j/4AAQSkZJRgAB..."
},
"Timestamp": "1708876800"
}
]
}'

After the request, the image field in the database will contain the blobstore URL instead of the raw data URI. Mind that, just like in capture data that you can choose the field name.

Objects

"Defect": {
"X": 250,
"Y": 10000,
"Size-X": 50,
"Size-Y": 100
}

Objects like the example above will be flattened into separate fields in the database.

Defect_XDefect_YDefect_Size-XDefect_Size-Y
2501000050100

Array of Objects

 "Defects": [
{
"X": 250,
"Y": 10000,
"Size-X": 50,
"Size-Y": 100,
"#Type": "defect1"
},
{
"X": 300,
"Y": 10000,
"Size-X": 50,
"Size-Y": 100,
"#Type": "defect1"
},
{
"X": 250,
"Y": 10000,
"Size-X": 50,
"Size-Y": 100,
"#Type": "defect2"
}
]

The following example will result in:

Defects_XDefects_YDefects_Size-XDefects_Size-YType (tag)arrIndex (tag)
2501000050100defect10
3001000050100defect11
2501000050100defect22

When multiple objects in an array share the same timestamp, an arrIndex tag is automatically added to make each row unique. This tag indicates the position in the array (starting from 0).

You can also create custom tags by prefixing field names with #. In the example above, #Type becomes a tag named Type in the database instead of a field.

CDC Object (Change Data Capture)

CDC (Change Data Capture) is a specialized format for tracking data modifications over time. Use CDC when you need to:

  • Track the history of changes to data points
  • Implement update and delete operations on existing data
  • Mirror database operations from external systems

Let's look at an example of creating a new sensor reading in the measurement_name table:

Unescaped CDC Object:

{
"before": null,
"after": {
"time": "2026-02-16 10:52:00.000 +0100",
"gateway_id": "241776eb-945a-4a53-958c-9b8681e42af0",
"gateway": "Logger1",
"name": "sensor1",
"value_count": 1000,
"value_avg": 50,
"value_min": 40,
"value_max": 60,
"tags": "{\"Connection_Tag\": \"connection_1\", \"loggerActivationUid\": \"241776eb-945a-4a53-958c-9b8681e42af0\"}"
},
"source": {
"db": "capture",
"table": "measurement_name"
},
"op": "c",
"ts_ns": 1771235558000000000
}

This CDC object must be stringified and placed in the Fields. Here's the complete request:

{
"Metrics": [
{
"Name": "cdc_example",
"Tags": {},
"Fields": {
"cdc_payload": "{\"before\": null,\"after\": {\"time\": \"2026-02-16 10:52:00.000 +0100\",\"gateway_id\": \"241776eb-945a-4a53-958c-9b8681e42af0\",\"gateway\": \"Logger1\",\"name\": \"sensor1\",\"value_count\": 1000,\"value_avg\": 50,\"value_min\": 40,\"value_max\": 60,\"tags\": \"{\\\"Connection_Tag\\\": \\\"connection_1\\\", \\\"loggerActivationUid\\\": \\\"241776eb-945a-4a53-958c-9b8681e42af0\\\"}\"},\"source\": {\"db\": \"capture\",\"table\": \"measurement_name\"},\"op\": \"c\",\"ts_ns\": 1771235558000000000}"
},
"Timestamp": "1771235558"
}
]
}

This creates the following row in the measurement_name table:

timegateway_idgatewaynamevalue_countvalue_avgvalue_minvalue_maxtags
"2026-02-16 10:52:00.000 +0100""241776eb-945a-4a53-958c-9b8681e42af0""Logger1""sensor1"1000504060{"Connection_Tag": "connection_1", "loggerActivationUid": "241776eb-945a-4a53-958c-9b8681e42af0"}

Understanding the CDC Structure:

The CDC object contains several key components that control how data is inserted or modified:

CaptureObject Structure Requirements
  • Name: Must start with cdc_ (e.g., cdc_example). The rest of the name is arbitrary.
  • Tags: Must be an empty object {}.
  • Fields: Contains the stringified CDC object. The field key name (e.g., cdc_payload) is arbitrary - the actual table name comes from source.table in the CDC object.
CDC Object Fields

before

The current state of the data point in the database. In our example, this is null because we're creating a new row that doesn't exist yet.

Operation Typebefore Value
Create (c)null
Read (r)Current data
Update (u)Current data
Delete (d)Current data

after

The new state of the data point after the operation. In our example, this contains all the sensor data we want to insert:

{
"time": "2026-02-16 10:52:00.000 +0100",
"gateway_id": "241776eb-945a-4a53-958c-9b8681e42af0",
"gateway": "Logger1",
"name": "sensor1",
"value_count": 1000,
"value_avg": 50,
"value_min": 40,
"value_max": 60,
"tags": "{\"Connection_Tag\": \"connection_1\", \"loggerActivationUid\": \"241776eb-945a-4a53-958c-9b8681e42af0\"}"
}

The structure of after must match your target table schema exactly - each field in this object becomes a column in the database.

Operation Typeafter Value
Create (c)New data
Read (r)Current data
Update (u)Updated data
Delete (d)null

source

Specifies where this data should be written:

  • db: The database name (in our example: "capture")
  • table: The table/measurement name (in our example: "measurement_name")

op (operation)

The type of operation to perform. In our example, "c" means we're creating a new data point.

OperationDescriptionUse Case
"c"Create - Insert a new data pointAdding new sensor readings
"r"Read - Read-only operation (rarely used for API)Replication/synchronization only
"u"Update - Modify an existing data pointCorrecting existing data
"d"Delete - Remove an existing data pointRemoving invalid data

ts_ns

The timestamp of the operation in nanoseconds since Unix epoch. In our example: 1771235558000000000 represents the time when this change occurred.