Vionlabs Docs

Portal Webhooks

Subscribe to recipe, creative asset, and Smart List events from the Vionlabs app

Portal webhooks notify your systems when work in the Vionlabs app changes: a recipe finishes, a creative asset is ready, or a Smart List's membership updates.

You subscribe in the app. No Vionlabs-side setup ticket is required.

This is separate from processing webhooks, which notify you when catalog asset processing completes.

Subscribe

  1. Open app.vionlabs.comSettings → Webhooks
  2. Click Create Webhook
  3. Enter the HTTPS URL that should receive events
  4. Select the events you want
  5. Copy the signing secret (shown only once)
  6. Enable the webhook

Use Test on an existing webhook to send a sample payload to your endpoint.

Events

EventWhen it fires
recipe.completedA published recipe finishes successfully
recipe.failedA published recipe fails
creative_asset.completedA creative asset is generated and ready
creative_asset.createdA creative asset record is created (files may not be ready yet)
creative_asset.failedCreative asset generation fails
smart_list.updatedA Smart List's membership changes

smart_list.updated fires when titles are added or removed from a list. The usual case is the daily license-window refresh: titles that become active are added, and titles whose license has expired are removed.

Payload format

Every event uses the same envelope:

{
  "event": "smart_list.updated",
  "timestamp": "2025-12-29T02:15:02.693Z",
  "data": {}
}

timestamp is ISO 8601 UTC. data depends on the event.

smart_list.updated

{
  "event": "smart_list.updated",
  "timestamp": "2025-12-29T02:15:02.693Z",
  "data": {
    "smartListId": "9f6555a6-01ad-4d1b-9f99-82a18066e2d8",
    "smartListTitle": "Currently Licensed Movies",
    "addedAssetIds": ["317604421cfcaf61"],
    "removedAssetIds": ["fff926e53030b33e"]
  }
}
FieldDescription
smartListIdSmart List ID (same ID as in the Portal API)
smartListTitleCurrent list title
addedAssetIdsVionlabs asset IDs that joined the list
removedAssetIdsVionlabs asset IDs that left the list

Either array can be empty. After you receive this event, GET /smartlists/{id}/assets returns the current membership.

recipe.completed

{
  "event": "recipe.completed",
  "timestamp": "2025-12-29T10:15:02.693Z",
  "data": {
    "recipeId": "9f6555a6-01ad-4d1b-9f99-82a18066e2d8",
    "recipeTitle": "My new recipe",
    "status": "completed",
    "dataOnly": false,
    "creativeAssetCount": 4,
    "creativeAssetIds": [
      "c1dc10a0-8bdf-4e2b-9d45-4e46fb03a98d",
      "f89cae58-9a4e-4cde-9a85-007be27cf974"
    ]
  }
}

recipe.failed

{
  "event": "recipe.failed",
  "timestamp": "2025-12-29T10:15:02.693Z",
  "data": {
    "recipeId": "9f6555a6-01ad-4d1b-9f99-82a18066e2d8",
    "recipeTitle": "My new recipe",
    "status": "failed",
    "dataOnly": false,
    "errorMessage": "All assets failed during JIT API calls"
  }
}

creative_asset.completed

{
  "event": "creative_asset.completed",
  "timestamp": "2025-12-29T10:15:19.658Z",
  "data": {
    "creativeAssetId": "c1dc10a0-8bdf-4e2b-9d45-4e46fb03a98d",
    "assetId": "317604421cfcaf61",
    "recipeId": "9f6555a6-01ad-4d1b-9f99-82a18066e2d8",
    "recipeTitle": "My new recipe",
    "type": "thumbnail-vertical",
    "url": "317604421cfcaf61_17-vertical-logo/V6VBrT/thumbnail_43355.jpg"
  }
}

creative_asset.created and creative_asset.failed use the same data shape. url may be null when files are not ready yet.

Headers

Each delivery is an HTTP POST with Content-Type: application/json and these headers:

HeaderDescription
X-Vionlabs-SignatureHMAC SHA-256 hex digest of the raw request body, signed with your webhook secret
X-Vionlabs-TimestampISO 8601 time the request was sent
X-Vionlabs-EventEvent type, for example smart_list.updated. Omitted on Test deliveries.

Verify the signature

Compute HMAC SHA-256 over the raw request body (the exact bytes you received, not a re-serialized JSON object) using the secret shown when you created the webhook. Compare it to X-Vionlabs-Signature with a timing-safe comparison.

X-Vionlabs-Timestamp is informational and is not part of the HMAC. If you need freshness or deduplication, use the signed timestamp field in the JSON body.

const crypto = require('crypto')

function verifyWebhookSignature(rawBody, signature, secret) {
  if (typeof signature !== 'string' || !/^[0-9a-f]{64}$/i.test(signature)) {
    return false
  }
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'))
}

Delivery

  • Method: POST
  • Timeout: 30 seconds
  • A 2xx response marks the delivery as successful
  • Failed deliveries are not retried today (single attempt)

Respond quickly. Do heavier work (Portal API fetches, downstream updates) after you return 200.

Test event

The Test action sends this payload. It is not a real product event:

{
  "event": "test",
  "timestamp": "2025-12-29T10:15:02.693Z",
  "data": {
    "message": "This is a test webhook from VionLabs"
  }
}

The test request includes X-Vionlabs-Signature and X-Vionlabs-Timestamp, but not X-Vionlabs-Event.

On this page