Skip to Content

Webhooks

Receive real-time HTTP notifications when events occur in your VoxBurst account.

Base URL

https://api.voxburst.io/v1/webhooks

Events

EventDescription
post.createdA new post has been created (draft or scheduled)
post.scheduledPost has been scheduled for future publishing
post.publishedPost finished publishing — check data.status to distinguish "PUBLISHED" (all platforms succeeded) from "PARTIAL" (some platforms failed)
post.failedPost failed to publish on all platforms
post.draft.approvedPost draft was approved at the final approval workflow step and is now queued for publishing
account.connectedA new social account was connected
account.disconnectedA social account was disconnected
account.errorA connected account has an error (e.g. token expired or revoked)
media.uploadedA media file has been uploaded and passed validation

There is no separate post.partial webhook event. When publishing results in a partial failure, VoxBurst fires post.published with status: "PARTIAL" in the payload. Always check data.status in your post.published handler to detect partial failures and trigger remediation.


Webhook Payload

All webhook events share this envelope format:

{ "id": "evt_abc123", "type": "post.published", "createdAt": "2026-02-20T14:00:00Z", "data": { "id": "post_xyz789", "workspaceId": "ws_abc123", "content": "Hello from VoxBurst!", "status": "PARTIAL", "publishedAt": "2026-02-20T14:00:00Z", "platforms": [ { "platform": "TWITTER", "status": "PUBLISHED", "platformPostId": "1234567890", "platformPostUrl": "https://x.com/user/status/1234567890", "publishedAt": "2026-02-20T14:00:01Z" }, { "platform": "INSTAGRAM", "status": "FAILED", "error": { "code": "PUBLISH_ERROR", "message": "Media format not supported: image must be JPEG or PNG" } } ] } }

Use data.status to branch your handler logic: "PUBLISHED" means all platforms succeeded; "PARTIAL" means at least one platform failed and the post needs remediation via POST /v1/posts/:id/retry or POST /v1/posts/:id/platforms/:platformId/fix.

Note: platform and status values in the webhook payload are uppercase (e.g. "TWITTER", "PUBLISHED", "FAILED"). This is different from the REST API responses which return lowercase strings.


Delivery Headers

Every webhook request VoxBurst delivers includes the following headers:

HeaderDescription
X-VoxBurst-SignatureHMAC-SHA256 signature — see Signature Verification below
X-VoxBurst-TimestampUnix timestamp of the delivery (same value as t= in the signature)
X-VoxBurst-Delivery-IdUnique ID for this delivery attempt — useful for deduplication
X-VoxBurst-EventEvent type, e.g. post.published
Content-TypeAlways application/json

Signature Verification

All webhook requests include a signature header for verification:

X-VoxBurst-Signature: t=1707753600,v2=xxxxxxxxxxxxxxxx

Verify signatures to ensure requests come from VoxBurst:

import { createHmac, timingSafeEqual } from 'crypto' function verifyWebhookSignature( payload: string, signature: string, secret: string ): boolean { const parts = Object.fromEntries( signature.split(',').map(p => p.split('=', 2) as [string, string]) ) const timestamp = parts['t'] const signatureValue = parts['v2'] if (!timestamp || !signatureValue) return false // Reject requests older than 5 minutes to prevent replay attacks const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10) if (age > 300) return false const expected = createHmac('sha256', secret) .update(`${timestamp}.${payload}`) .digest('hex') // Use timing-safe comparison to prevent timing attacks try { return timingSafeEqual( Buffer.from(expected, 'hex'), Buffer.from(signatureValue, 'hex') ) } catch { return false } } // In your webhook handler: app.post('/webhook', (req, res) => { const signature = req.headers['x-voxburst-signature'] as string const isValid = verifyWebhookSignature( req.rawBody, signature, process.env.WEBHOOK_SECRET! ) if (!isValid) return res.status(401).send('Invalid signature') const event = req.body console.log(`Received event: ${event.type}`) res.status(200).send('OK') })
import hmac import hashlib import time def verify_webhook_signature(payload: str, signature: str, secret: str) -> bool: parts = dict(part.split("=", 1) for part in signature.split(",")) timestamp = parts.get("t") sig = parts.get("v2") if not timestamp or not sig: return False # Reject requests older than 5 minutes to prevent replay attacks age = int(time.time()) - int(timestamp) if age > 300: return False expected = hmac.new( secret.encode(), f"{timestamp}.{payload}".encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(sig, expected)

Always verify webhook signatures in production to prevent spoofed requests.

Requests with a timestamp older than 5 minutes (300 seconds) are automatically rejected. Ensure your server clock is synchronized via NTP.


Retry and Backoff

VoxBurst automatically retries failed webhook deliveries to ensure reliability.

Retry schedule

AttemptDelay before retry
1st retry (after 1st failure)1 minute
2nd retry (after 2nd failure)5 minutes
3rd retry (after 3rd failure)15 minutes
After 3rd retryPermanent failure — no more attempts

Total time from first attempt to final failure: ~21 minutes.

Retry triggers

A delivery is retried when:

  • The endpoint returns a non-2xx HTTP status code
  • The request times out (see Request timeout below)
  • The connection fails or returns a network error

Delivery status flow

PENDING → DELIVERING → DELIVERED (success) ↓ (non-2xx, timeout, or connection error) PENDING (scheduled for retry) ↓ (after 3rd retry) FAILED

Request timeout

VoxBurst enforces a 30-second timeout per delivery attempt. If your endpoint does not respond within 30 seconds, the delivery is marked as a failure and scheduled for retry. Return 200 immediately and process the event asynchronously to avoid timeouts.

Delivery ordering

Webhook events are delivered asynchronously via a queue. No ordering is guaranteed. A post.published event may arrive before a post.scheduled event from the same workflow if the scheduled event was delayed. Always treat each event independently and use GET /v1/posts/:id to confirm current state before acting.

Duplicate deliveries

VoxBurst guarantees at-least-once delivery — your endpoint may receive the same event more than once. Use X-VoxBurst-Delivery-Id to deduplicate. Store processed delivery IDs and skip re-processing if the ID has been seen.

SSRF protection

Delivery targets must be public HTTPS endpoints. VoxBurst blocks delivery to:

  • Private IP ranges (10.x.x.x, 172.16.x.x–172.31.x.x, 192.168.x.x)
  • Loopback addresses (127.x.x.x, ::1)
  • Link-local addresses (169.254.x.x)

Blocked deliveries are permanently failed — they are not retried. Use the test endpoint (POST /v1/webhooks/test) with a tunnel service like ngrok for local development.

Response body capture

VoxBurst captures up to 10 KB of your endpoint’s response body for debugging. Responses larger than 10 KB are truncated. The captured body is visible in GET /v1/webhooks/:id/deliveries/:deliveryId.

Endpoint Failure Tracking

Failed deliveries increment the endpoint’s failureCount and consecutiveFailures. After consecutive failures:

  • The delivery status is marked as failed
  • The endpoint statistics are updated
  • At 5 consecutive failures: the workspace owner receives an email warning that the endpoint is having delivery problems.
  • At 10 consecutive failures: the endpoint is automatically disabledenabled is set to false and autoDisabledAt is stamped with the current timestamp. The workspace owner receives a second email notifying them of the auto-disable.

Auto-Disable and Re-Enable

When an endpoint is auto-disabled:

  • enabled becomes false
  • autoDisabledAt is set to the timestamp of the disable event
  • VoxBurst stops delivering events to the endpoint

To re-enable a disabled endpoint, PATCH /v1/webhooks/:id with { "enabled": true }. Re-enabling also resets failure tracking: consecutiveFailures is set to 0, autoDisabledAt is cleared to null, and lastFailureAt is cleared to null. The auto-disable clock starts fresh from zero consecutive failures.

curl -X PATCH https://api.voxburst.io/v1/webhooks/wh_abc123 \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "enabled": true }'

Delivery Record

Each webhook delivery creates a record with:

FieldDescription
successtrue if the delivery was accepted (2xx response), false otherwise
retryCountNumber of retry attempts made (0 = first attempt succeeded or failed, no retries yet)
statusCodeHTTP status code from the endpoint (if any)
responseBodyResponse body captured for debugging
latencyMsTime taken for the HTTP request in milliseconds
errorError message if delivery failed
deliveredAtTimestamp of successful delivery

Deliveries may be retried and your endpoint should be idempotent — processing the same event multiple times should have no side effects.


Webhook Object

All webhook endpoint responses share the following fields:

FieldTypeDescription
idstringUnique endpoint identifier (wh_…)
urlstringThe HTTPS URL VoxBurst delivers events to
eventsstring[]Array of subscribed event types
enabledbooleanWhether the endpoint is active and receiving deliveries
failureCountnumberCumulative count of all failed deliveries (never resets)
consecutiveFailuresnumberCount of consecutive failed deliveries. Resets to 0 when the endpoint is re-enabled or a delivery succeeds.
autoDisabledAtstring | nullISO 8601 timestamp when the endpoint was automatically disabled after reaching 10 consecutive failures. null if not auto-disabled.
lastDeliveryAtstring | nullISO 8601 timestamp of the most recent successful delivery
createdAtstringISO 8601 timestamp when the endpoint was created
updatedAtstringISO 8601 timestamp when the endpoint was last updated


Payload versioning

Webhook payloads are versioned via the signature scheme. The current scheme is v2 (HMAC-SHA256, t=timestamp,v2=hex). The signing string is ${timestamp}.${rawBody}.

VoxBurst will introduce a new scheme prefix (e.g. v3) if the signature algorithm changes. Old and new schemes will be sent in parallel during a transition window — your verification code should accept either scheme until you confirm migration is complete.

Payload shape changes: additive changes (new fields) are made without a version bump. Breaking changes (field removals, type changes) trigger an advance notice period and are announced in the changelog.


Operational checklist

Before going to production, verify your webhook endpoint:

  • Returns 2xx within 30 seconds
  • Verifies X-VoxBurst-Signature on every request
  • Rejects requests with a timestamp older than 5 minutes (replay protection)
  • Deduplicates on X-VoxBurst-Delivery-Id
  • Handles post.published with data.status: "PARTIAL" (not just "PUBLISHED")
  • Does not block on post.published — calls GET /v1/posts/:id to re-confirm current state if needed
  • Is reachable from public IPs (no private/loopback addresses)

List Webhooks

GET /v1/webhooks

curl https://api.voxburst.io/v1/webhooks \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "data": [ { "id": "wh_abc123", "url": "https://yourapp.com/webhooks/voxburst", "events": ["post.published", "post.failed"], "enabled": true, "failureCount": 0, "consecutiveFailures": 0, "autoDisabledAt": null, "lastDeliveryAt": null, "createdAt": "2026-02-20T10:00:00Z", "updatedAt": "2026-02-20T10:00:00Z" } ] }

Create Webhook

POST /v1/webhooks

Returns HTTP 201 Created.

Required scopes: webhooks:write

Required feature: custom_integrations (Pro plan or higher)

curl -X POST https://api.voxburst.io/v1/webhooks \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://yourapp.com/webhooks/voxburst", "events": ["post.published", "post.failed"] }'

VoxBurst generates a unique signing secret for each webhook endpoint automatically. The secret is returned once in the creation response — copy it immediately and store it securely. It cannot be retrieved again.

Response

{ "id": "wh_abc123", "url": "https://yourapp.com/webhooks/voxburst", "events": ["post.published", "post.failed"], "enabled": true, "failureCount": 0, "consecutiveFailures": 0, "autoDisabledAt": null, "lastDeliveryAt": null, "createdAt": "2026-02-20T10:00:00Z", "updatedAt": "2026-02-20T10:00:00Z", "secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }

Rotate Webhook Secret

POST /v1/webhooks/:id/rotate-secret

Rotate the signing secret for a webhook endpoint. The old secret becomes invalid immediately. The new secret is returned once in the response — copy and store it securely.

Required scopes: webhooks:write

curl -X POST https://api.voxburst.io/v1/webhooks/wh_abc123/rotate-secret \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "success": true, "secret": "whsec_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "message": "Secret rotated successfully. Store the new secret securely - it will not be shown again." }

Get Webhook

GET /v1/webhooks/:id

curl https://api.voxburst.io/v1/webhooks/wh_abc123 \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Update Webhook

PATCH /v1/webhooks/:id

Update webhook URL, events, or status.

Required scopes: webhooks:write

Request Body

FieldTypeRequiredDescription
urlstringNoNew HTTPS delivery URL
eventsstring[]NoReplacement list of subscribed event types
enabledbooleanNoEnable or disable the endpoint
curl -X PATCH https://api.voxburst.io/v1/webhooks/wh_abc123 \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "events": ["post.published", "post.failed", "account.error"] }'

Setting enabled: true resets failure tracking so the auto-disable clock starts fresh: consecutiveFailures is set to 0, autoDisabledAt is cleared to null, and lastFailureAt is cleared to null. This applies whether the endpoint was auto-disabled or manually disabled.


Test Webhook

POST /v1/webhooks/test

Send a test event to a webhook endpoint to verify it is reachable and responding correctly.

Required scopes: webhooks:write

Request Body

FieldTypeRequiredDescription
webhookIdstringYesID of the webhook endpoint to test
curl -X POST https://api.voxburst.io/v1/webhooks/test \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "webhookId": "wh_abc123" }'

Delete Webhook

DELETE /v1/webhooks/:id

Required scopes: webhooks:write

curl -X DELETE https://api.voxburst.io/v1/webhooks/wh_abc123 \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

List Deliveries

GET /v1/webhooks/:id/deliveries

List delivery attempts for a webhook endpoint.

curl https://api.voxburst.io/v1/webhooks/wh_abc123/deliveries \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "data": [ { "id": "del_xyz789", "webhookId": "wh_abc123", "eventType": "post.published", "url": "https://yourapp.com/webhooks/voxburst", "success": true, "retryCount": 0, "statusCode": 200, "latencyMs": 143, "responseBody": null, "error": null, "createdAt": "2026-04-09T12:00:05Z", "deliveredAt": "2026-04-09T12:00:05Z" } ], "pagination": { "hasMore": false } }

Get Delivery Stats

GET /v1/webhooks/:id/deliveries/stats

Get aggregate delivery statistics for a webhook endpoint.

Query Parameters

ParameterTypeDescription
sincestringISO 8601 date — only include deliveries after this timestamp
curl "https://api.voxburst.io/v1/webhooks/wh_abc123/deliveries/stats?since=2026-04-01T00:00:00Z" \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "data": { "total": 142, "delivered": 139, "failed": 3, "successRate": 97.9 } }

Get Delivery

GET /v1/webhooks/:id/deliveries/:deliveryId

Get the full detail for a single delivery attempt, including request and response bodies.

curl https://api.voxburst.io/v1/webhooks/wh_abc123/deliveries/del_xyz789 \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Retry Delivery

POST /v1/webhooks/:id/deliveries/:deliveryId/retry

Manually re-deliver a specific event to the webhook endpoint.

Required scopes: webhooks:write

curl -X POST https://api.voxburst.io/v1/webhooks/wh_abc123/deliveries/del_xyz789/retry \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"
Last updated on