Skip to content

Webhooks

Webhooks let VastVision tell your server the moment something you care about happens, so your application does not have to poll. You decide what should trigger a webhook by building an automation recipe in the VastVision web app. The most common recipes for this integration fire when an item leaves the inventory area (moves from an interior zone to an exit zone) and when it returns.

Direct API calls Automation webhooks
Direction Pull: your server asks VastVision. Push: VastVision POSTs to your server.
Timing Whenever you call. Within seconds of the automation completing.
What you get Current state (catalog, counts, zones, stock alerts) or a recent window of movements. Only the movements that match your recipe, with asset, zone, location, and time.
Delivery You control retries and timing. Each message is delivered once and not retried.
Writes Required for printing and creating assets. Read-only notifications.

When to use each

  • Use API calls to load state when your application starts, for on-demand views such as the catalog and stock alerts, and for every print or create action.
  • Use automation webhooks to react in real time to the movements that matter to your business, such as an item leaving through the Exit.
  • Use both together: load state with API calls, keep it current with webhooks, and periodically call GET /mqtt-change-messages/ to backfill anything your endpoint missed (for example, during a deploy).

An automation recipe is a sequence of movement steps followed by an action. Recipes are built on the Automation Recipes page of the VastVision web app, by your administrator or with VastVision’s help. For this integration, we recommend two recipes:

Recipe Steps Action
Leaves inventory Asset is in an interior zone, then is read in an exit zone. Send webhook to your out of inventory endpoint.
Returns to inventory Asset is in an exit zone, then is read in an interior zone. Send webhook to your back in inventory endpoint.

You choose where each webhook goes. Every recipe’s webhook action has a Webhook URL field, and VastVision sends the message to exactly the URL entered there. Enter the address of an endpoint on your own server. For example, if your server is your-app.example.com, the two recipes might use:

Recipe Webhook URL (example)
Leaves inventory https://your-app.example.com/webhooks/vast/out-of-inventory
Returns to inventory https://your-app.example.com/webhooks/vast/in-inventory

Giving each recipe its own path makes every message unambiguous: anything posted to /out-of-inventory means an item left, and anything posted to /in-inventory means it came back. Because the signature covers the path, the path cannot be altered in transit. You can change a recipe’s Webhook URL at any time, for example to move from a test endpoint to production.

When you add a webhook action to a recipe, the web app shows a signing secret once. Store it in your secrets manager right away; your endpoint needs it to verify every message.

You do not need to move a real tag to test your endpoint. On the Automation Recipes page, each webhook action shows an example payload and a Send test webhook button.

  1. In the recipe’s Webhook URL field, enter your development or test endpoint. A temporary HTTPS tunnel to a developer’s machine works well for this.
  2. Click Send test webhook. VastVision sends a real, signed POST to your URL in the same format your production messages will use.
  3. Confirm your endpoint verifies the signature, logs the body, and returns 200.
  4. Compare the logged body with the example payload shown on the page.

A test send looks like this. It has the same structure as a real message, plus "is_test": true at the top level, and uses placeholder values: asset Test Asset, EPC TESTEPC00000001, two steps in Test Zone 1 and Test Zone 2, and the same timestamp on both steps.

Send test webhook
{
"is_test": true,
"data": [
{
"asset": { "name": "Test Asset", "epc_hex": "TESTEPC00000001" },
"zone": { "id": 1, "name": "Test Zone 1" },
"location": { "id": 1, "name": "Test Location" },
"timestamp": "2026-10-28T16:50:36.126482+00:00"
},
{
"asset": { "name": "Test Asset", "epc_hex": "TESTEPC00000001" },
"zone": { "id": 2, "name": "Test Zone 2" },
"location": { "id": 1, "name": "Test Location" },
"timestamp": "2026-10-28T16:50:36.126482+00:00"
}
]
}

Your endpoint should acknowledge test sends with 200 but not change inventory state.

Every automation webhook is an HTTP POST with a JSON body. data is an array with one entry per recipe step, in order. For the Leaves inventory recipe, the first entry is the interior zone the item was in, and the last entry is the exit zone where it was read.

Using the sample store, when tag A004 moves from the Sales Floor to the Exit, your /out-of-inventory endpoint receives:

POST /webhooks/vast/out-of-inventory
{
"data": [
{
"asset": { "name": "Peter Millar Polo", "epc_hex": "E2801170000002000000A004" },
"zone": { "id": 12, "name": "Sales Floor" },
"location": { "id": 3, "name": "Downtown Store" },
"timestamp": "2026-10-28T13:47:05+00:00"
},
{
"asset": { "name": "Peter Millar Polo", "epc_hex": "E2801170000002000000A004" },
"zone": { "id": 14, "name": "Exit" },
"location": { "id": 3, "name": "Downtown Store" },
"timestamp": "2026-10-28T14:52:10+00:00"
}
]
}
Field Type Description
data array One entry per recipe step, in order.
data[].asset {name, epc_hex} The asset that moved. Key your records on epc_hex.
data[].zone {id, name} The zone the asset was read in for that step.
data[].location {id, name} The location the zone belongs to.
data[].timestamp datetime When that step happened (ISO-8601, UTC).
is_test bool Present and true only on test sends.

Handling messages

  • The last entry in data is the asset’s newest position. Use its zone and timestamp.
  • Messages can arrive out of order. Ignore a message whose last timestamp is older than the one you already stored for that EPC.
  • Use epc_hex plus the last timestamp as an idempotency key, so a repeated message has no effect.
  • Timestamps are ISO-8601 in UTC and may or may not include fractional seconds (e.g. 2026-10-28T14:52:10+00:00 or 2026-10-28T16:50:36.126482+00:00). Use a standard ISO-8601 parser rather than a fixed format string.

Next: verify every message’s signature.