← All guides

Secure V2 · SDK 0.3.0

HTTP integration

Updated October 11, 2026 · verified against repository implementation

Signed JSON to a public HTTPS endpoint.

Configure and verify

Create an HTTPS POST receiver. Enter its URL without embedded credentials or query parameters. Configure a signing secret and, optionally, a bearer token. Only nonsecret x-meta-* custom headers are accepted; Host, Authorization and cookie overrides are forbidden. The dedicated bearer token is encrypted. Basic verification validates configuration without contacting the receiver; explicitly confirm a test-send to verify actual acceptance.

A 2xx response means endpoint acceptance. HTTP 408, 429 and 5xx retry; most other 4xx, redirects and invalid certificates are terminal. Requests time out after eight seconds. No redirects are followed and response bodies are discarded.

Verify the raw body and deduplicate

The existing raw-body HMAC algorithm is preserved: x-struct-signature equals sha256= plus the hexadecimal HMAC-SHA256 of exact transmitted bytes. New integrations use the versioned envelope; historical webhooks retain their old body. x-struct-event-id and x-struct-delivery-id identify the event and destination delivery. Do not deduplicate by signature because credentials may rotate.

import {createHmac,timingSafeEqual} from 'node:crypto'
// raw is the bounded, unparsed request Buffer; never reserialize before verification.
const expected=Buffer.from('sha256='+createHmac('sha256',process.env.STRUCT_WEBHOOK_SECRET).update(raw).digest('hex'))
const provided=Buffer.from(request.headers['x-struct-signature'] || '')
if(provided.length!==expected.length || !timingSafeEqual(provided,expected)) throw new Error('Invalid signature')
const event=JSON.parse(raw.toString('utf8'))
// Atomically INSERT event.event_id into your unique inbox before business processing.
// Return 2xx only after your own durable acceptance.

Open device setup · Contact support