Closed Loop Callbacks
Simple device callbacks, structured business callbacks, and delivery rules for Big Block integrations.
What Closed Loop Means
Closed loop callbacks let your application see what happened after Big Block drove work to a device. A callback can report a raw device event such as an acknowledgement or timeout, or a higher-level fulfillment event after a picker action changes order state.
Big Block supports two callback families. Simple device callbacks report command acknowledgements, timeouts, unsolicited idle button presses, and sequence outcomes that reuse the same acknack payload shape. Structured business callbacks report higher-level item, shipment, and order events. Both callback families use the same configured callback URL and the same callback security settings.
If you already work with webhooks, the callback URL is the same basic idea. Big Block sends an HTTP request to your endpoint when something happens, so your application gets the event immediately instead of having to keep asking whether anything changed.
Simple device callbacks
Most customers only need this callback family. It gives you direct visibility into what happened on the device with acknack results such as ack, nack, call , call_up, and call_down.
Structured business callbacks
Use these when you need stable WMS or ERP-facing events for item, shipment, and order progress. They carry an order_event field and stable business-level nonce values.
Callback Types at a Glance
| Callback Type | Look For | Typical Trigger | Nonce To Track | Best Fit |
|---|---|---|---|---|
| Simple device | acknack | A device command is acknowledged or times out, or an idle button press occurs. | nonce for commands, or the device's unsolicitedNonce for idle button presses. | Most integrations that need direct device visibility. |
| Sequence step | acknack | A sequence step is acknowledged or times out. | The step's stepnonce appears in nonce. | Per-step sequence tracking. |
| Sequence complete | acknack = ack | The final sequence step is acknowledged. | The sequence's sequencenonce appears in nonce. | Whole-sequence completion detection. |
| Structured item | order_event | An order item is incremented, decremented, or acknowledged. | nonce and voodoo_nonce. | WMS or ERP item-level state sync. |
| Structured shipment or order | order_event | A shipment or order completes. | shipmentnonce or ordernonce, plus nonce and voodoo_nonce. | Business-level completion tracking. |
Starting the Loop
For direct device commands, closed-loop tracking starts when you send a unique transaction ID with the command. In the REST API, this field is called a nonce. Big Block later sends that value back in the simple device callback so your receiver can match the callback to the work it created.
REST API Example
import requests, json
session = requests.Session()
url = "https://www.voodoodevices.com/api/"
# Login
x = session.post(url + "user/login/",
json={'username': 'yourusername', 'password': 'yourpassword'})
z = json.loads(x.text)
# Send command with nonce for callback tracking
session.post(
url + "device/D4F660:AFA0CB/",
headers={'referer': url, 'x-csrf-token': z['token']},
json={
'command': 'flash',
'line1': 'Jessica',
'line2': 'Pick 3',
'line3': 'SKU:001256',
'nonce': 'transactionID001',
'seconds': 120
}
)How Callbacks Are Delivered
Big Block can deliver callbacks as either formatted GET requests or JSON POST requests. If your configured callback URL contains replacement fields such as {deviceid}, {nonce}, or {acknack}, Big Block formats those values into the URL and sends a GET request. If the callback URL contains no replacement fields, Big Block sends a JSON POST body instead.
{
"acknack": "ack",
"deviceid": "D4F660:AFA0CB",
"location": "A1-SHELF002",
"nonce": "transactionID001",
"qty": 3
}This rule applies to all callback families, including device callbacks, sequence callbacks, and order callbacks. For structured business callbacks, any field present in the callback payload can be used as a replacement field in a formatted GET URL.
Payload Fields at a Glance
| Payload | Expected Fields | Fast Notes |
|---|---|---|
| Simple device callback | acknack, deviceid, location, nonce, qty | This is the common command or idle-button callback shape. |
| Sequence step callback | The same fields as a simple device callback. | The step's stepnonce is returned in nonce. |
| Sequence-complete callback | acknack, deviceid, location, nonce, qty | acknack = ack, deviceid = sequenceid, location = "", and qty = 0. |
| Structured item callback | order_event, order, shipment, item, SKU, UPC, location, device, deviceid, quantity, ordered_quantity, quantity_fulfilled, quantity_remaining, nonce, and voodoo_nonce | This is the full item-level business state snapshot. |
| Structured shipment-complete callback | order_event, order, shipment, nonce, voodoo_nonce, and shipmentnonce | Only emitted when shipment completion is configured. |
| Structured order-complete callback | order_event, order, nonce, voodoo_nonce, and ordernonce | Only emitted when order completion is configured. |
Callback URL Configuration
Big Block uses one configured callback URL for simple device callbacks, sequence callbacks, and structured business callbacks. The URL can be on any port, use HTTP or HTTPS, and deliver either GET or POST based on whether your URL contains replacement fields. Many programmers would describe this callback URL as a webhook endpoint.
This callback design exists to avoid polling. Polling means your application keeps sending requests such as "did anything happen yet?" on a timer. That may sound simple at first, but across many devices, orders, and active workflows it creates a large stream of repetitive requests, most of which return no useful new information.
Those extra polling requests consume CPU time, database work, and network bandwidth on both sides. As scale increases, that wasted work can slow down the server and the integration around it. Callbacks are the opposite model: Big Block only sends a request when an event actually occurs, which gives you faster updates with much less load.
Inbound Connectivity Required
www.voodoodevices.com, the callback is sent from Big Block's cloud server on the public Internet to the URL you provide. If that destination is a server inside your corporate firewall, most firewalls will block the connection by default because it is an inbound request from the Internet into your private network. For that reason, you must allow inbound messages from www.voodoodevices.com to reach that internal server. If your callback URL points to a public cloud endpoint instead, no firewall change is needed. If your environment cannot permit that inbound access, you may be a better fit for a self-hosted server deployment. See the Server Deployment guide.Simple Device Callbacks
When you send a device command with a nonce, Big Block can later report the result of that command using a simple device callback. In POST callbacks, the acknack field is always lower case.
| acknack | Meaning | When It Happens |
|---|---|---|
ack | Acknowledged | The operator acknowledged an active command. |
nack | Timed Out | The command timed out before it was acknowledged. |
call | Front-Button Call | A long front-button press while the device is idle and showing statics. |
call_up | Plus-Button Call | A plus-button press while a multi-button device is idle and showing statics. |
call_down | Minus-Button Call | A minus-button press while a multi-button device is idle and showing statics. |
In simple command callbacks, nonce echoes the command nonce you originally sent. In unsolicited idle-button callbacks,nonce uses the device's configured unsolicitedNonce value instead.
| Field | Expected Meaning |
|---|---|
acknack | The device event result, always in lower case. |
deviceid | The external device identifier. |
location | The location associated with the device. |
nonce | The original command nonce, or unsolicitedNonce for idle button presses. |
qty | The quantity reported by the device at acknowledgement or timeout time. |
{
"acknack": "ack",
"deviceid": "D4F660:AFA0CB",
"location": "A1-SHELF002",
"nonce": "transactionID001",
"qty": 3
}In this payload, qty is the quantity reported by the device at the time of acknowledgement or timeout. On multi-button devices, this can reflect quantity changes made on the device before the final acknowledgement.
Idle Buttons vs Active Commands
call_up and call_down are only generated while the device is idle and showing statics. If the plus or minus button is pressed while a command is active, Big Block treats that input as a quantity adjustment for the active command rather than as an unsolicited callback.Lost Condition
Sequence Callbacks
Sequences build on top of the simple device callback model. Each step in a sequence can define its own stepnonce. When a step is acknowledged or times out, Big Block emits a normal simple device callback using that step nonce as the callback nonce.
A sequence can also define a sequencenonce. After the final step is acknowledged, Big Block emits one additional sequence-complete callback. This callback is synthetic and always uses acknack = ack .
| Sequence Callback | Payload Shape | How To Read It |
|---|---|---|
| Step callback | The same payload shape as a simple device callback. | The step's stepnonce is returned in nonce. |
| Sequence-complete callback | acknack, deviceid, location, nonce, qty | acknack is always ack, deviceid is the sequenceid, location is empty, and qty is 0. |
{
"acknack": "ack",
"deviceid": "SEQ003",
"location": "",
"nonce": "sequence-complete-003",
"qty": 0
}In the final sequence callback, nonce is the sequencenonce , deviceid contains the sequenceid, location is empty, and qty is 0.
No Separate Sequence NACK
nack callback. If a step times out, Big Block emits a normal step-level nack using that step's stepnonce, and sequence progression stops until the issue is resolved.Order Callbacks
Orders add a second callback family: structured business callbacks. These callbacks are designed for WMS, ERP, and fulfillment integrations that need stable external identifiers and a higher-level event model than raw device acknowledgements.
| order_event | Meaning |
|---|---|
item_added | An item's fulfilled count was incremented. |
item_removed | An item's fulfilled count was decremented. |
device_ack | An active order item was acknowledged through the device workflow. |
shipment_fulfilled | All active item workflows in a shipment are complete. |
order_fulfilled | All active item workflows in an order are complete. |
{
"order_event": "device_ack",
"order": "SO-12345",
"shipment": "SHIP-001",
"item": "Widget A",
"SKU": "SKU-A",
"UPC": "UPC-A",
"location": "Aisle 3",
"device": "D4F660:AFA0CB",
"deviceid": "D4F660:AFA0CB",
"quantity": 6,
"ordered_quantity": 10,
"quantity_fulfilled": 6,
"quantity_remaining": 4,
"nonce": "item-callback-123",
"voodoo_nonce": "item-callback-123"
}This payload is a state snapshot. The order_event tells you what changed, and the quantity fields tell you the current item state after that change.
| Field | Expected Meaning |
|---|---|
order_event | The business event that changed item state. |
order | The order identifier. |
shipment | The shipment identifier. |
item | The item description or item identifier. |
SKU | The SKU associated with the item. |
UPC | The UPC associated with the item. |
location | The physical or logical pick location. |
device and deviceid | The external device identifier, exposed under both field names. |
quantity | The current fulfilled count for the item. |
ordered_quantity | The requested total quantity. |
quantity_fulfilled | The current fulfilled total after this event. |
quantity_remaining | The open quantity remaining after this event. |
nonce | The stable item callback token. |
voodoo_nonce | A generic alias for the same stable business nonce. |
nonceis the stable item callback token.voodoo_nonceis a generic alias for the same stable token.deviceanddeviceidboth contain the external device identifier.quantityis the current fulfilled count for the item.ordered_quantityis the requested total.quantity_fulfilledis the current fulfilled total.quantity_remainingis the remaining open quantity.
Shipment and Order Completion Callbacks
Shipment and order completion callbacks are only emitted when a completion nonce is explicitly configured.
| Completion Callback | Expected Fields | Emitted When |
|---|---|---|
| Shipment complete | order_event, order, shipment, nonce, voodoo_nonce, and shipmentnonce | Only when shipmentnonce is configured. |
| Order complete | order_event, order, nonce, voodoo_nonce, and ordernonce | Only when ordernonce is configured. |
{
"order_event": "shipment_fulfilled",
"order": "SO-12345",
"shipment": "SHIP-001",
"nonce": "shipment-complete-001",
"voodoo_nonce": "shipment-complete-001",
"shipmentnonce": "shipment-complete-001"
}If shipmentnonce is blank, Big Block does not emit shipment_fulfilled . If ordernonce is blank, Big Block does not emit order_fulfilled . This behavior lets you opt into shipment-level and order-level completion callbacks independently.
Nonce Reference
The most important distinction is that simple device callbacks use command or device nonces, while structured business callbacks use stable business-level nonces.
| Nonce Field | Used For |
|---|---|
nonce | In simple device callbacks, this is the original command nonce or unsolicitedNonce for idle button presses. |
stepnonce | The nonce used for step-level sequence callbacks. |
sequencenonce | The nonce used for the final sequence-complete callback. |
callback_nonce | The stable nonce configured for item-level business callbacks. |
shipmentnonce | The nonce used for shipment-complete callbacks. |
ordernonce | The nonce used for order-complete callbacks. |
voodoo_nonce | A generic alias in structured business callbacks so integrations can consume one consistent nonce field across item, shipment, and order events. |
Pick the Right Identifier
nonce or voodoo_nonce in the structured business callback payload. If you are integrating directly with device commands, use the simple callback nonce.Delivery Semantics and Security
Callbacks are dispatched asynchronously. Your receiver should treat each callback as an independent event and should not assume that multiple related callbacks will arrive in a guaranteed order.
One Action Can Produce More Than One Callback
A single physical acknowledgement can generate more than one callback request. For example, if an operator acknowledges the last active item in a shipment, Big Block can emit:
- the simple device
ackcallback - the structured
device_ackitem callback - the structured
shipment_fulfilledcallback - the structured
order_fulfilledcallback, if that shipment completed the entire order
For reliable integrations:
- make your callback endpoint idempotent
- key your processing on the appropriate nonce for the callback family
- treat simple device callbacks and structured business callbacks as separate event streams
- be prepared for a single picker action to fan out into multiple callback requests
Callback Security
All callback families use the same callback security configuration. Depending on your server configuration, Big Block can send callbacks with:
- no additional security headers
- HTTP Basic Authentication
- an
X-API-Keyheader - an OAuth bearer token for supported flows
- an Odoo session-authenticated POST flow
These settings apply equally to simple device callbacks, sequence callbacks, and structured business callbacks.
Receiving the Callback
Your endpoint needs to accept both delivery modes. If your callback URL uses replacement fields, you will receive a GET request whose path matches your template. If your callback URL contains no replacement fields, you will receive a JSON POST body instead.
This minimal Python example prints both kinds of callbacks and branches on acknack versus order_event so you can see whether the payload is a simple device callback or a structured business callback.
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
class CallbackHandler(BaseHTTPRequestHandler):
def do_GET(self):
print(f"GET {self.path}")
print("Parse this path according to your configured replacement fields")
self.send_response(200)
self.end_headers()
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length)
data = json.loads(body or b"{}")
acknack = data.get("acknack")
order_event = data.get("order_event")
print(f"POST {self.path}")
print(json.dumps(data, indent=2))
if acknack is not None:
print(f"Simple device callback: {acknack}")
print(f"Simple nonce: {data.get('nonce')}")
elif order_event is not None:
print(f"Structured business callback: {order_event}")
print(f"Business nonce: {data.get('nonce')}")
else:
print("Unknown callback payload")
self.send_response(200)
self.end_headers()
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", 8000), CallbackHandler)
print("Listening on port 8000 ...")
server.serve_forever()Recommended Mental Model
Related Pages
Commands vs. Statics
Understand when button presses are callbacks versus quantity adjustments
Sequence API
See how stepnonce and sequencenonce are configured in sequences
Orders API
Configure order, shipment, and item workflows that emit structured callbacks
Authentication
Match callback security settings to the authentication model your server expects
REST API Overview
Full API reference for device, sequence, and order endpoints
