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:

  1. Subscribe to shipment events in Webhooks v2
  2. Receive shipment_scheduled webhooks when a shipment is ready to fulfill
  3. Fulfill the order in your system (pick, pack, ship)
  4. Post tracking back to VRIO via the API
  5. 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 fulfilled
  • shipment_cancelled — tells you to stop fulfillment if in progress
  • shipment_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 failed
  • shipment_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:

  1. Store the shipment_id and date_scheduled for reference
  2. When the scheduled date arrives, create a fulfillment order in your warehouse/shipping system using the items and address
  3. 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"
}
FieldRequiredDescription
shipment_tracking_idYesThe tracking number from the carrier
carrier_idNoThe carrier ID in VRIO (e.g. 1 = USPS, 2 = UPS, etc.)
date_completeNoThe 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_id for idempotency — store processed event IDs and skip duplicates
  • Process shipment_scheduled as the trigger — this is your "go" signal for fulfillment
  • Check shipment_id when 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_cancelled before you've processed the shipment_scheduled if events arrive quickly. Always check the current status before acting
  • Verify webhook signatures — use the HMAC signature in the X-Webhook-Signature header to confirm webhooks are authentic