Skip to main content
Version: V3.2

Overview

Webhooks let Capture push raw telemetry data to your own HTTP endpoint as it arrives on the platform — either per data point (real-time) or grouped (batch). You configure everything through the REST API: create a webhook configuration once, link it to one or more devices, and add scopes to control exactly which data is forwarded.

Experimental

Webhooks are an experimental feature. The API surface and payload format may still change between releases, and the feature may not be enabled on every environment. Contact the Capture team before building production integrations on top of it.

How it works

Data starts flowing to your endpoint once both of the following exist:

  1. A webhook configuration — the destination: URL, HTTP method, delivery mode, authentication, and headers. Owned by your company and reusable across devices.
  2. A device link with at least one enabled scope (data route) — which device forwards data, and how much of it (whole device, one connection, or one measurement).

Deleting a configuration or removing the last scope stops delivery automatically. Configuration changes propagate without any action on the device itself.

Webhook configuration (company-owned, reusable)
└─ linked device(s)
└─ scope(s): Device | Connection | Measurement

Headers

Every endpoint lives under https://portal.captureplatform.com and shares the headers below.

HeaderValue
AuthVersionV0.0.1 (only value accepted; others return 401)
Authorization[Bearer / ApiToken] <Received auth token>
companyView(Optional) restricts the call to a specific company view

See Authentication for how to obtain a token.

tip

The caller needs the Device Overview module right on the owning company: View for the GET endpoints, Manage for everything that creates, updates, links, or deletes. Missing rights return 403.

Endpoints at a glance

MethodPathPurpose
POST/v2/webhooks?companyId={companyId}Create a webhook configuration
GET/v2/webhooks?companyId={companyId}List all webhook configurations of a company
GET/v2/webhooks/{id}Get a single webhook configuration
PUT/v2/webhooks/{id}Update a webhook configuration
DELETE/v2/webhooks/{id}Delete a webhook configuration
POST/v2/webhooks/{id}/devices/{deviceId}Link a device to a configuration
GET/v2/webhooks/{id}/devicesList devices linked to a configuration
DELETE/v2/webhooks/{id}/devices/{deviceId}Unlink a device
POST/v2/webhooks/{id}/devices/{deviceId}/routesAdd a scope (data route) to a device link
GET/v2/webhooks/{id}/devices/{deviceId}/routesList scopes on a device link
PUT/v2/webhooks/{id}/devices/{deviceId}/routes/{routeId}Edit a scope
DELETE/v2/webhooks/{id}/devices/{deviceId}/routes/{routeId}Remove a scope

Webhook configurations

Create a configuration

POST /v2/webhooks?companyId={companyId}

{
"name": "My integration endpoint",
"url": "https://example.com/capture-ingest",
"method": "POST",
"deliveryMode": "Realtime",
"authType": "Bearer",
"authPayload": "{\"token\": \"my-secret-token\"}",
"customHeaders": "{\"X-Environment\": \"production\"}",
"includeMetadataHeaders": true,
"includeRetryHeader": true
}
FieldTypeNotes
namestringDisplay name. Required, max 255 characters.
urlstringThe endpoint Capture will call. Required, max 2048 characters. Use HTTPS.
methodstringPOST (default), PUT, or PATCH.
deliveryModestringRealtime (one request per data point) or Batch (multiple points per request, batched per measurement).
authTypestringNone, Bearer, ApiKey, Basic, or Custom.
authPayloadstring(Optional) JSON string with the credentials for authType — see below. Stored encrypted, never returned.
customHeadersstring(Optional) JSON string of static header key/value pairs added to every request (non-secret context, e.g. an environment tag).
includeMetadataHeadersboolAdds User-Agent and X-Webhook-Timestamp headers to deliveries (default: true).
includeRetryHeaderboolAdds an X-Webhook-Retry-Count header to deliveries (default: true).

The authPayload per auth type

authPayload is a JSON object serialized as a string. Its expected keys depend on authType:

authTypeauthPayloadWhat your endpoint receives
NoneomitNo authentication header.
Bearer{"token": "<token>"}Authorization: Bearer <token>
ApiKey{"apiKeyValue": "<key>"}X-API-Key: <key>
Basic{"username": "<user>", "password": "<pass>"}Authorization: Basic <base64(user:pass)>
Custom{"headers": {"X-My-Header": "value", ...}}Each entry as its own request header.
Credentials are write-only

authPayload is encrypted at rest and never returned by any GET endpoint — responses only expose authType. On update (PUT), leave authPayload out (null) to keep the stored credentials, send a new value to replace them, or send an empty string ("") to clear them.

Response

Create, get, and update return the configuration without credentials:

{
"id": "3f1c2b4e-…",
"companyId": 42,
"name": "My integration endpoint",
"url": "https://example.com/capture-ingest",
"method": "POST",
"deliveryMode": "Realtime",
"authType": "Bearer",
"customHeaders": "{\"X-Environment\": \"production\"}",
"includeMetadataHeaders": true,
"includeRetryHeader": true
}

GET /v2/webhooks?companyId={companyId} returns a list of the same shape. DELETE /v2/webhooks/{id} returns true and immediately stops delivery for all devices linked to that configuration.


Linking devices

POST /v2/webhooks/{id}/devices/{deviceId} — link a device (no body). GET /v2/webhooks/{id}/devices — list linked devices. DELETE /v2/webhooks/{id}/devices/{deviceId} — unlink a device.

{
"deviceId": 1234,
"webhookConfigId": "3f1c2b4e-…",
"createdAt": "2026-07-09T08:00:00Z"
}

A configuration can be linked to many devices, and a device can be linked to many configurations. Linking the same device twice returns 409. The device and the configuration must belong to the same company.

info

A link alone does not forward any data yet — add at least one enabled scope (below) to start delivery for that device.


Scopes (data routes)

Scopes control which portion of a linked device's data is forwarded. A link can carry multiple scopes; a data point is delivered when any enabled scope matches it.

Add a scope

POST /v2/webhooks/{id}/devices/{deviceId}/routes

{
"scopeLevel": "Connection",
"connectionTag": "PLC-1",
"measurement": null,
"enabled": true
}
scopeLevelconnectionTagmeasurementForwards…
Devicemust be emptymust be emptyEverything the device sends.
Connectionrequiredmust be emptyEverything on one connection.
MeasurementrequiredrequiredOne measurement on one connection.

Violating these rules returns 400; an identical scope on the same link returns 409. connectionTag and measurement are limited to 255 characters.

The response (and each item of the GET …/routes list) is the scope with its generated id:

{
"id": "9a7d5e21-…",
"scopeLevel": "Connection",
"connectionTag": "PLC-1",
"measurement": null,
"enabled": true
}

Use PUT …/routes/{routeId} with the same body shape to edit a scope (e.g. toggle enabled), and DELETE …/routes/{routeId} to remove it. Routing activates and deactivates automatically — no device restart or manual sync is needed.


What your endpoint receives

Deliveries use the configured HTTP method with Content-Type: application/json.

Request headers

HeaderWhenValue
User-AgentincludeMetadataHeaders: trueCapture.Cloud.Stream.WebhookHttpClient/<version>
X-Webhook-TimestampincludeMetadataHeaders: trueISO 8601 timestamp of when the delivery was sent.
X-Webhook-Retry-CountincludeRetryHeader: true0 on the first attempt, incremented per retry.
(custom headers)customHeaders configuredEach configured key/value pair.
(auth header)authTypeNoneSee authPayload per auth type.

Real-time payload

One request per data point:

{
"Timestamp": "2026-07-09T12:00:00.000Z",
"Metadata": {
"Company": "your-company",
"GatewayId": "source-gateway-id",
"Measurement": "sensor_reading"
},
"Point": {
"Measurement": "sensor_reading",
"Fields": { "temperature": 22.5, "humidity": 45.0 },
"Tags": { "location": "room-1" },
"UtcTimestamp": "2026-07-09T11:59:58.000Z"
}
}

Batch payload

Same envelope, but points are grouped per measurement into a Points array:

{
"Timestamp": "2026-07-09T12:00:00.000Z",
"Metadata": {
"Company": "your-company",
"GatewayId": "source-gateway-id",
"Measurement": "sensor_reading"
},
"Points": [
{
"Measurement": "sensor_reading",
"Fields": { "temperature": 22.5 },
"Tags": { "location": "room-1" },
"UtcTimestamp": "2026-07-09T11:59:58.000Z"
},
{
"Measurement": "sensor_reading",
"Fields": { "temperature": 22.6 },
"Tags": { "location": "room-1" },
"UtcTimestamp": "2026-07-09T12:00:08.000Z"
}
]
}

Responses and retries

Return a 2xx status to acknowledge a delivery. Other responses are handled as follows:

Your responseBehavior
2xxDelivered — done.
4xx (except 429)Dropped immediately — treated as a permanent misconfiguration.
429Retried with exponential backoff.
5xx / timeoutRetried with exponential backoff.

Endpoints should respond quickly (connect/request timeouts are in the order of 10s/30s). Do heavy processing asynchronously after acknowledging.


Error responses

StatusWhen
400Validation failed (missing name/url, invalid enum value, scope rule violation, …).
401AuthVersion is not V0.0.1, or the token is missing/invalid/expired.
403Valid token but missing Device Overview → View/Manage on the owning company.
404Company, configuration, device, link, or route not found (or not within your company).
409Device already linked, or an identical scope already exists on the link.

Example: from zero to data on your endpoint

BASE="https://portal.captureplatform.com"
AUTH=(-H "AuthVersion: V0.0.1" -H "Authorization: Bearer <token>")

# 1. Create the webhook configuration
WEBHOOK_ID=$(curl -s "$BASE/v2/webhooks?companyId=42" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-d '{
"name": "My integration endpoint",
"url": "https://example.com/capture-ingest",
"method": "POST",
"deliveryMode": "Realtime",
"authType": "Bearer",
"authPayload": "{\"token\": \"my-secret-token\"}"
}' | jq -r '.id')

# 2. Link a device
curl -s -X POST "$BASE/v2/webhooks/$WEBHOOK_ID/devices/1234" "${AUTH[@]}"

# 3. Add a scope — forward everything the device sends
curl -s "$BASE/v2/webhooks/$WEBHOOK_ID/devices/1234/routes" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-d '{ "scopeLevel": "Device", "enabled": true }'

# Data now flows to https://example.com/capture-ingest