Skip to Content

Webhook Ingestion

Webhook ingestion is the simplest Data Ingest API entry point for external systems that can send a single JSON payload over HTTP. It is ideal for third-party products, automation tools, and lightweight integrations that do not need the full batched POST /api/ingest contract.

Endpoint

Each webhook connector posts to:

POST https://inspire.yourcompany.com/api/webhook/{stream}

Example:

POST https://inspire.yourcompany.com/api/webhook/sales

Authentication

Authenticate with the connector’s data ingest credential:

Authorization: Bearer ik_<key_id>_<secret>

Example:

curl -X POST https://inspire.yourcompany.com/api/webhook/sales \ -H "Authorization: Bearer ik_5f8d4c2a91be_pULjS4J8VbD2WXQYjM4f9m1i3p4lQ7mN1k2sT6uR" \ -H "Content-Type: application/json" \ -d '{ "region": "North", "sales": 142000, "target": 130000 }'

Request model

The webhook API accepts one JSON object per request.

{ "region": "North", "sales": 142000, "target": 130000, "updated": "2026-04-09T10:30:00Z" }

Current behavior:

  • Top-level properties become DataPool fields.
  • String, number, boolean, and null values are stored directly.
  • Nested objects and arrays are stored as raw JSON strings.
  • The item ID defaults to the id property if present.
  • If no matching ID property exists, Inspire generates one automatically.

If you need batched writes, explicit item lists, or richer record shaping, use the full Data Ingestion API instead of the webhook surface.

Choosing the item ID

By default, Inspire looks for an id property in the payload.

To use a different property, pass idField in the query string:

curl -X POST "https://inspire.yourcompany.com/api/webhook/sales?idField=region" \ -H "Authorization: Bearer $INSPIRE_INGEST_KEY" \ -H "Content-Type: application/json" \ -d '{ "region": "North", "sales": 142000 }'

That request will upsert the item keyed by region.

Signature Verification

When a connector has a Webhook Signing Key configured, Inspire requires every incoming request to carry a valid HMAC-SHA256 signature. This prevents forged payloads from being written to the DataPool.

Required headers

HeaderFormatDescription
X-Webhook-Signaturesha256=<hex>HMAC-SHA256 of the raw request body, hex-encoded, prefixed with sha256=
X-Webhook-TimestampUnix epoch secondsTimestamp of the request. Must be within 5 minutes of the server’s clock to prevent replay attacks.

Computing the signature

The signature is computed over the timestamp concatenated with the raw request body, using the connector’s webhook signing key as the HMAC key:

signature = HMAC-SHA256(key, timestamp + "." + body)

Bash example

TIMESTAMP=$(date +%s) BODY='{"region":"North","sales":142000}' SIGNING_KEY="whsk_your_signing_key_here" SIGNATURE=$(printf '%s.%s' "$TIMESTAMP" "$BODY" \ | openssl dgst -sha256 -hmac "$SIGNING_KEY" | awk '{print $2}') curl -X POST "https://inspire.yourcompany.com/api/webhook/sales" \ -H "Authorization: Bearer $INSPIRE_INGEST_KEY" \ -H "Content-Type: application/json" \ -H "X-Webhook-Signature: sha256=$SIGNATURE" \ -H "X-Webhook-Timestamp: $TIMESTAMP" \ -d "$BODY"

Node.js example

import { createHmac } from 'node:crypto' const timestamp = Math.floor(Date.now() / 1000).toString() const body = JSON.stringify({ region: 'North', sales: 142000 }) const signingKey = process.env.WEBHOOK_SIGNING_KEY const signature = createHmac('sha256', signingKey) .update(`${timestamp}.${body}`) .digest('hex') await fetch('https://inspire.yourcompany.com/api/webhook/sales', { method: 'POST', headers: { Authorization: `Bearer ${process.env.INSPIRE_INGEST_KEY}`, 'Content-Type': 'application/json', 'X-Webhook-Signature': `sha256=${signature}`, 'X-Webhook-Timestamp': timestamp, }, body, })

When no webhook signing key is configured on the connector, signature headers are ignored and requests are authenticated by the ingest key alone.

Response

Successful requests return 202 Accepted with a compact response body:

{ "status": "accepted", "itemId": "North", "fieldsUpdated": 2 }

Typical statuses:

StatusMeaning
202 AcceptedPayload accepted and written
400 Bad RequestInvalid or missing JSON body
401 UnauthorizedMissing or invalid data ingest credential

Example integrations

CI/CD pipeline

curl -X POST "$INSPIRE_WEBHOOK_URL?idField=build" \ -H "Authorization: Bearer $INSPIRE_INGEST_KEY" \ -H "Content-Type: application/json" \ -d "{ \"build\": \"$BUILD_NUMBER\", \"status\": \"deployed\", \"durationSeconds\": $BUILD_DURATION }"

Node.js

await fetch('https://inspire.yourcompany.com/api/webhook/app-metrics?idField=service', { method: 'POST', headers: { Authorization: `Bearer ${process.env.INSPIRE_INGEST_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ service: 'api-gateway', requestsPerMinute: 340, errorRate: 0.02, }), })
Last updated on