Building Fulfillment with Webhooks
How to use Webhooks v2 shipment events and the Vrio API to build your own fulfillment workflow.
This guide walks through using Webhooks v2 shipment events to build a custom fulfillment workflow. Instead of relying on a built-in fulfillment connection, you can receive shipment webhooks, handle fulfillment on your end, and post tracking information back to VRIO via the API.
Overview
The workflow is:
- Subscribe to shipment events in Webhooks v2
- Receive
shipment_scheduledwebhooks when a shipment is ready to fulfill - Fulfill the order in your system (pick, pack, ship)
- Post tracking back to VRIO via the API
- Monitor for cancellations, address changes, and item updates
Step 1: Configure Webhooks v2
Set up a Webhooks v2 connection and subscribe to the following events:
shipment_scheduled— tells you a shipment is ready to be fulfilledshipment_cancelled— tells you to stop fulfillment if in progressshipment_address_updated— tells you the shipping address changed (update the label)shipment_item_updated— tells you the items changed (update the packing list)shipment_skipped— tells you this cycle was skipped (no fulfillment needed)
You may also want to subscribe to:
shipment_error— a previous fulfillment attempt failedshipment_rma— a return has been authorized
Step 2: Handle shipment_scheduled
When you receive a shipment_scheduled webhook, a shipment is paid for and ready to fulfill. The payload contains everything you need:
- Shipping address in
webhook_data.shipment.customer_address_shipping - Items to ship in
webhook_data.shipment.shipment_items(with SKUs and quantities) - Shipment ID in
webhook_data.shipment.shipment_id(you'll need this to post tracking back)
{
"webhook_type": "shipment",
"webhook_event": "shipment_scheduled",
"webhook_event_id": 10001,
"webhook_event_date": "2026-03-22 14:30:00",
"webhook_data": {
"shipment": {
"shipment_id": 251606,
"customer_id": 71135,
"order_id": 135123,
"shipment_status_id": "Pending Post",
"date_scheduled": "2026-03-22 14:30:00",
"shipment_items": [
{
"shipment_item_id": 270243,
"quantity": 1,
"item_id": 1282,
"item_name": "Monthly Supplement Box",
"item_sku": "SUP-001"
}
],
"customer_address_shipping": {
"fname": "John",
"lname": "Doe",
"address1": "123 Main St",
"address2": null,
"city": "Denver",
"country": "US",
"state": "CO",
"zipcode": "80014"
}
}
}
}Important: Pay attention to the date_scheduled field. For recurring subscriptions with prepaid cycles, multiple shipments may be created at once with staggered scheduled dates (e.g. one shipment per month). You should not fulfill all shipments immediately — use date_scheduled to determine when each shipment should actually be picked, packed, and shipped.
Your system should:
- Store the
shipment_idanddate_scheduledfor reference - When the scheduled date arrives, create a fulfillment order in your warehouse/shipping system using the items and address
- When the package ships, post the tracking number back to VRIO (Step 3)
Step 3: Post Tracking to VRIO
Once your shipment has shipped, post the tracking number back to VRIO using the Track Shipment API:
POST /shipments/{shipment_id}/track
Request body:
{
"shipment_tracking_id": "1Z999AA10123456784",
"carrier_id": 1,
"date_complete": "2026-03-23 10:00:00"
}| Field | Required | Description |
|---|---|---|
shipment_tracking_id | Yes | The tracking number from the carrier |
carrier_id | No | The carrier ID in VRIO (e.g. 1 = USPS, 2 = UPS, etc.) |
date_complete | No | The ship date. Defaults to current time if not provided |
This will mark the shipment as Shipped in VRIO, which will also trigger any shipped responders (email notifications, etc.) configured on the offer.
Step 4: Handle Changes
Your endpoint should also handle these events to keep your fulfillment system in sync:
shipment_cancelled
If a shipment is cancelled after you received shipment_scheduled, cancel the fulfillment in your system if it hasn't shipped yet.
{
"webhook_event": "shipment_cancelled",
"webhook_data": {
"shipment": {
"shipment_id": 251606,
"shipment_status_id": "Cancelled",
"date_cancel": "2026-03-22 16:00:00",
"shipment_notes": "Shipment cancelled."
}
}
}shipment_address_updated
If the address changes before you ship, update the shipping label.
{
"webhook_event": "shipment_address_updated",
"webhook_data": {
"shipment": {
"shipment_id": 251606,
"customer_address_shipping": {
"fname": "John",
"lname": "Doe",
"address1": "456 Oak Ave",
"address2": "Apt 3B",
"city": "Boulder",
"state": "CO",
"zipcode": "80302"
}
}
}
}shipment_item_updated
If items change before you ship, update the packing list.
{
"webhook_event": "shipment_item_updated",
"webhook_data": {
"shipment": {
"shipment_id": 251606,
"shipment_items": [
{
"shipment_item_id": 270244,
"quantity": 2,
"item_id": 1283,
"item_name": "Vitamin D Supplement",
"item_sku": "VIT-D-001"
}
]
}
}
}shipment_skipped
If a shipment is skipped, no fulfillment is needed for this cycle.
Best Practices
- Use
webhook_event_idfor idempotency — store processed event IDs and skip duplicates - Process
shipment_scheduledas the trigger — this is your "go" signal for fulfillment - Check
shipment_idwhen handling cancellations and updates — match them to your internal fulfillment records - Post tracking promptly — once the carrier provides a tracking number, post it to VRIO so customers receive shipping notifications
- Handle events out of order — you may receive a
shipment_cancelledbefore you've processed theshipment_scheduledif events arrive quickly. Always check the current status before acting - Verify webhook signatures — use the HMAC signature in the
X-Webhook-Signatureheader to confirm webhooks are authentic
Updated about 1 hour ago
