Shelfless Logo

Webhook Service Documentation

No search results found for ""

Webhook Service

The Shelfless Webhook Service allows you to receive real-time notifications about events in your Shelfless integration. By subscribing to webhooks, your application can automatically receive updates about order statuses, inventory changes, and other important events.

Getting Started

  1. Create a webhook endpoint in your application that can receive POST requests
  2. Subscribe to events using the Shelfless API
  3. Verify webhook signatures to ensure message authenticity
  4. Process the webhook payloads in your application

Subscription Management

You can manage your webhook subscriptions through the Shelfless API. Available operations include:

  • Create new subscriptions
  • Update existing subscriptions
  • List active subscriptions
  • Activate/deactivate subscriptions
  • Delete subscriptions

A subscription can contain one or more events to be forwarded to one endpoint. Multiple subscriptions can be created, however each event can only be present in one of those subscription. If the receiver needs to duplicate the message to multiple endpoints thathas to be done by the receiver, Shelfless does not support that scenario. The possibility to create multiple subscriptions is intended for the scenario where the receiver needs to receive different events/messages on different endpoints.

Important: To modify a subscription, use the update endpoint rather than unsubscribing and resubscribing. Unsubscribing may result in missed messages.

Shelfless ensures reliable message delivery through an automated retry system:

Feature Description
Retry Mechanism If a webhook request fails (non-2XX response), Shelfless will automatically retry with an exponential backoff pattern
Dead-Letter Queue If all retries are exhausted, messages are stored in a dead-letter queue
Requeuing Messages can be requeued for delivery at any time, with no limit on retry attempts
Delivery Confirmation Successfully delivered messages (2XX response) are confirmed and cannot be replayed
Message Persistence No messages are lost as long as a subscription is active, even if the receiving endpoint is unreachable for an extended period
Message Ordering Messages will be processed as soon as possible, but order is not guaranteed. Use the timestamps in the messages instead of relying on delivery order
Subscription Status If a subscription is deleted, no messages are generated. There is no possibility of generating messages for events that occurred during an inactive subscription period

For detailed API documentation on managing webhook subscriptions, visit the Shelfless API Documentation.

These events/messages are currently available for subscription: The messages are kept lightweight as in most cases the receiver knows what was ordered etc. For details if needed it is possible to act on a received message and poll details from the Shelfless API.

These events are available for webhook subscription. The webhook messages are intentionally lightweight, containing only essential information. For detailed data, you can query the Shelfless API using the information provided in the webhook payload.

Event Type Description
sales_order.status Reports status updates for sales orders
purchase_order.status Reports status updates for purchase orders
article_master.updated Reports changes to master data
return_order.status Reports status updates for return orders
stock.adjustment Reports non-operational stock level changes (e.g., inventory checks, damaged goods)
stock.adjustment.v2 Same as stock.adjustment but supports multiple stock type changes per event

Event Envelope

All webhook events share the same outer envelope. The event-specific payload is nested inside data.event. Fields marked When available are omitted entirely when they contain no data — they will not appear as null. Not all fields will be present in every event.

Field Type Description
id string Unique identifier for the event. Use this to prevent duplicate processing.
version string API version identifier. Currently always “1”.
timestamp number Unix timestamp when the event was sent.
type string Event type identifier (e.g., “sales_order.status”).
data.event object Contains the event-specific payload. Fields vary by event type; see below.

sales_order.status

Sent whenever a sales order changes status — for example when it is picked, packed, or shipped. Use order_number to correlate with your own order, and status_code / status_text to determine the new state. Tracking information is included when the order has been handed to a carrier.

Field Type Presence Notes
order_number string Always
warehouse_id string When available
warehouse_name string When available
status_code number When available Integer status code
status_text string When available Human-readable status label
updated_at number When available Unix timestamp of the status change
items array When available Per-article fulfillment detail
shipments array When available Present once a shipment has been created
serviceCode string When available Carrier service code
consignment array When available Consignment and package level detail
ignoreSLA boolean When available When true, this status falls outside the SLA agreement
{
    "id": "45cf7e84-842a-4e3e-b3cf-cc466ddcd953",
    "version": "1",
    "timestamp": 1732019759,
    "type": "sales_order.status",
    "data": {
        "event": {
            "order_number": "20240903-9",
            "warehouse_id": "BERGER",
            "warehouse_name": "Bring Bergen",
            "status_code": 230,
            "status_text": "Shipped",
            "updated_at": 1732019758,
            "items": [
                {
                    "article_id": "SKU1",
                    "quantity": 1,
                    "line_reference": "1",
                    "line_id": "1",
                    "detailed_items": [
                        {
                            "batchNumber": "L0343-Y",
                            "expiryDate": "2021-12-08",
                            "quantity": 1
                        }
                    ]
                }
            ],
            "shipments": [
                {
                    "carrier": "BPN",
                    "shipmentNumber": "70707730149100260",
                    "tracking": [
                        {
                            "number": "70707730149100260",
                            "link": "https://tracking.bring.com/tracking/70707730149100260"
                        }
                    ]
                }
            ],
            "serviceCode": "SERVICEPAKKE"
        }
    }
}

purchase_order.status

Sent when a purchase order changes status — for example when it is acknowledged, received, or fully put away. Use order_number to correlate with your purchase order. The status object contains the current status and a per-line breakdown of received quantities.

Field Type Presence Notes
order_number string Always
warehouse_id string When available
warehouse_name string When available
reference_order string When available Supplier or customer reference on the order
status object When available Current status detail
status.status string When available Status label
status.status_code number When available Integer status code
status.last_updated string When available ISO 8601 timestamp
status.items array When available Per-line received quantity detail
status.items[].sku string When available
status.items[].quantity number When available
status.items[].line_id string When available
status.items[].remaining_inbound_quantity number When available Quantity not yet received
{
    "id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
    "version": "1",
    "timestamp": 1732019759,
    "type": "purchase_order.status",
    "data": {
        "event": {
            "order_number": "PO-20241103-1",
            "warehouse_id": "BERGER",
            "warehouse_name": "Bring Bergen",
            "reference_order": "SUPPLIER-REF-001",
            "status": {
                "status": "Received",
                "status_code": 100,
                "last_updated": "2024-11-19T14:22:39Z",
                "items": [
                    {
                        "sku": "SKU1",
                        "quantity": 50,
                        "line_id": "1",
                        "remaining_inbound_quantity": 0
                    }
                ]
            }
        }
    }
}

article_master.updated

Sent when article master data for one of your SKUs is created or updated. The update_type field indicates the origin of the change: 1 means the update came from the warehouse management system (WMS), 2 means it came from a customer (API) update. Use the sku field to look up the full updated article via the Master Data API if you need the new attribute values.

Field Type Presence Notes
customer_number string When available
sku string When available The updated article SKU
update_type number When available 1 = WMS update, 2 = customer (API) update
unix_timestamp number When available Unix timestamp of the change
timestamp string When available ISO 8601 timestamp of the change
{
    "id": "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f",
    "version": "1",
    "timestamp": 1732019759,
    "type": "article_master.updated",
    "data": {
        "event": {
            "customer_number": "12345",
            "sku": "SKU1",
            "update_type": 2,
            "unix_timestamp": 1732019758,
            "timestamp": "2024-11-19T14:22:38Z"
        }
    }
}

return_order.status

Sent when a return order changes status. The status field is an integer code representing the internal state; the status_text field provides the corresponding external status label visible to end users. Only events with a mapped external status are forwarded — internal-only transitions are suppressed.

Field Type Presence Notes
customer_number string When available
return_order_id string When available
timestamp number When available Unix timestamp of the status change
warehouse_id string When available
status number When available Internal status code
status_text string When available External status label; see values below

Possible status_text values:

status_text Meaning
WAITING_FOR_CONFIRMATION Return acknowledged by Shelfless, pending arrival
INSPECTED Items inspected
COMPLETED Return fully processed
BOOKED Return booked into stock
{
    "id": "d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8a",
    "version": "1",
    "timestamp": 1732019759,
    "type": "return_order.status",
    "data": {
        "event": {
            "customer_number": "12345",
            "return_order_id": "RO-20241103-7",
            "timestamp": 1732019758,
            "warehouse_id": "BERGER",
            "status": 5,
            "status_text": "COMPLETED"
        }
    }
}

stock.adjustment

Sent when a non-operational stock level change occurs for a SKU — for example a physical inventory count correction or a damage write-off. A single event always affects one balance type (e.g. PHYSICAL or BLOCKED). Use adjustment_id for deduplication. For events that affect multiple balance types simultaneously, see stock.adjustment.v2.

Field Type Presence Notes
customer_number string Always
sku string Always
warehouse_id string Always
adjustment number Always Positive = stock increase, negative = decrease
unit string Always Unit of measure (e.g. PCE)
balance_type string Always Stock category affected (e.g. PHYSICAL, BLOCKED)
reason string Always Human-readable reason for the adjustment
adjustment_id string Always Use for deduplication
reason_code string When available Short code for the adjustment reason
reference string When available External reference
batch_number string When available
source_created_epoch number When available Unix timestamp from the originating system
event_created number When available Unix timestamp when the event was created
source string When available Originating system identifier
{
    "id": "e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8a9b",
    "version": "1",
    "timestamp": 1732019759,
    "type": "stock.adjustment",
    "data": {
        "event": {
            "customer_number": "12345",
            "sku": "SKU1",
            "warehouse_id": "BERGER",
            "adjustment": -5.0,
            "unit": "PCE",
            "balance_type": "PHYSICAL",
            "reason": "Damaged during storage",
            "reason_code": "DMG",
            "adjustment_id": "ADJ-20241119-42",
            "source_created_epoch": 1732019700,
            "event_created": 1732019758
        }
    }
}

stock.adjustment.v2

An extended version of stock.adjustment that supports multiple stock type changes in a single event. This is used when a single operation affects more than one balance type — for example a SCRAPPED action that simultaneously decreases PHYSICAL stock and increases BLOCKED stock. Each entry in stock_changes represents one balance type change.

Field Type Presence Notes
customer_number string Always
sku string Always
warehouse_id string Always
stock_changes array Always One entry per affected balance type
stock_changes[].stock_type string Always Stock category (e.g. PHYSICAL, BLOCKED)
stock_changes[].adjustment number Always Positive = increase, negative = decrease
created_at number Always Unix timestamp
created_at_readable string Always ISO 8601 timestamp
adjustment_id string When available Use for deduplication
unit string When available Unit of measure
batch_number string When available
expiry_date string When available
reason_code string When available Short code for the adjustment reason
comment string When available Free-text comment
{
    "id": "f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f8a9b0c",
    "version": "1",
    "timestamp": 1732019759,
    "type": "stock.adjustment.v2",
    "data": {
        "event": {
            "customer_number": "12345",
            "sku": "SKU1",
            "warehouse_id": "BERGER",
            "adjustment_id": "ADJ-20241119-43",
            "unit": "PCE",
            "reason_code": "SCRAPPED",
            "comment": "Damaged during storage, moved to blocked",
            "stock_changes": [
                {
                    "stock_type": "PHYSICAL",
                    "adjustment": -5.0
                },
                {
                    "stock_type": "BLOCKED",
                    "adjustment": 5.0
                }
            ],
            "created_at": 1732019758,
            "created_at_readable": "2024-11-19T14:22:38Z"
        }
    }
}

Security

To ensure the authenticity and integrity of webhook messages, each request includes a shelfless-signature header. The header follows this format:

shelfless-signature: t=1731940023,v1=7774b82d476eca1b902d3b47c27bc375d705cba7d8939a26a7e444693e2a3ef3

Where:

  • t: Timestamp when the message was sent
  • v1: HMAC signature of the payload
  1. Extract the timestamp and signature from the header
  2. Construct the signed payload string: {timestamp}.{raw_payload}
  3. Calculate the HMAC-SHA256 of the signed payload using your webhook secret. Please note that the secret is a hexadecimal string. Code example in Go:
    hmacKey, _ := hex.DecodeString("secretKey")
    mac := hmac.New(sha256.New, hmacKey)

    mac.Write([]byte(fmt.Sprintf("%s.%s", timestamp, rawPayload)))
    macSum := mac.Sum(nil)

    calcValue := fmt.Sprintf("%x", macSum)
  1. Compare your calculated signature with the received signature

Best Practices

  1. Always store the webhook secret securely
  2. Implement signature verification for all webhook requests
  3. Respond quickly to webhook requests (under 15 seconds) and process async if needed

Support

For additional support or questions about the webhook service: