7 min read Engineering

Connecting the robot to your WMS: what the API integration looks like

Haruki Tanaka
Haruki Tanaka
Hardware Engineer, Closer Robotics
API diagram showing robot task queue connected to warehouse management system

The first time we integrated Closer's task dispatch system with an operator's warehouse management system (WMS), we expected the hardest part to be the network layer. It was not. The network layer was fine. The hardest part was agreeing on what "task complete" meant.

This post covers the practical side of connecting a mobile robot to a WMS over MQTT and REST, with notes from our first two integration projects. We are not writing documentation for a specific WMS product. We are writing about the integration patterns and decision points that come up regardless of which WMS is on the other end.

The two integration surfaces: task dispatch and status feedback

A mobile robot integration with a WMS involves two distinct communication flows in opposite directions. The WMS sends task dispatch instructions to the robot: "move to location X, wait for scan confirmation, proceed to location Y." The robot sends status feedback to the WMS: current position, task state, battery level, sensor alerts, and task completion events.

These two flows have different latency and reliability requirements. Task dispatch can tolerate several hundred milliseconds of delivery latency without operational impact: a task that starts 300ms later than scheduled makes no practical difference. Status feedback latency matters more when WMS workflows depend on real-time position visibility for picker routing or load tracking. If the WMS uses robot position to route pickers away from aisles the robot is traversing, a 10-second status update lag creates real pick efficiency degradation.

We use MQTT for status feedback because its publish/subscribe model handles the continuous low-frequency telemetry stream efficiently without the overhead of polling a REST endpoint every few seconds. We use REST for task dispatch because REST's request/response model gives synchronous confirmation of task receipt, which is important when the WMS needs to know that a dispatch instruction was actually received before marking the task as in-progress.

The MQTT topic structure we use

The fleet API publishes to a topic hierarchy that follows a per-unit, per-message-type structure:

crb/fleet/{unit_id}/status
crb/fleet/{unit_id}/position
crb/fleet/{unit_id}/alert
crb/fleet/{unit_id}/task_event

The WMS subscribes to the topics it needs. A WMS that only needs to know when tasks complete subscribes only to crb/fleet/+/task_event, using the MQTT wildcard to receive events from all units. A WMS that wants real-time position for map display subscribes to crb/fleet/+/position separately, since position publishes at 2Hz and a WMS that does not need live position tracking does not need to process 7,200 position messages per hour per unit.

The QoS level matters for reliability. We publish task_event messages at MQTT QoS 1 (at-least-once delivery), which means the broker guarantees delivery but may deliver duplicates in network error conditions. The WMS consumer needs to handle deduplication on its side using the task ID included in every event payload. Status and position messages publish at QoS 0 (at-most-once): losing an occasional position message is acceptable; retransmitting it creates unnecessary load and the value of a stale position message is low.

The task dispatch REST endpoint and payload structure

Task dispatch uses a POST endpoint at /v1/tasks on the fleet management server. A minimal dispatch payload looks like this:

{
  "task_id": "wms-2025-09-021-00412",
  "unit_id": "crb-unit-04",
  "waypoints": [
    {"label": "pickup-A7", "x": 12.4, "y": 8.1, "action": "wait_scan"},
    {"label": "dropoff-dock2", "x": 3.2, "y": 22.7, "action": "wait_scan"}
  ],
  "priority": 2,
  "expires_at": "2025-09-21T14:45:00+09:00"
}

The task_id field is the WMS's identifier for the task, passed through to all status events so the WMS can correlate robot events back to its own records. This is the field that resolves the "what does task complete mean" problem: the WMS defines the task ID and the robot returns it on completion. Whether task completion means the waypoints are reached, the scan actions are confirmed, or the full round trip is logged, the WMS decides from its own logic using its own task ID.

The expires_at field is important for operational correctness. If the robot receives a dispatch instruction but cannot begin execution before the expiry time, it rejects the task and returns an EXPIRED status event. This prevents a scenario where a task dispatch queued during a robot maintenance window executes hours later when the operational context has changed.

Where our first integration ran into trouble

The first full integration we did with an operator's WMS hit a problem we should have anticipated. The WMS's task dispatch logic was built around a polling model from its previous AGV system: it sent a dispatch instruction, then polled a status endpoint every 10 seconds to check task state. When we switched to MQTT for status, the WMS integration team kept the polling loop running in parallel as a fallback. The result was two competing views of task state: MQTT events updated the WMS task record in real time, but the polling loop was writing stale status readings over the MQTT-updated values on its polling cycles.

The fix was straightforward: remove the polling loop and commit fully to event-driven status updates from MQTT. But the underlying issue was an architectural mismatch between the WMS's AGV-era integration assumptions (polling as primary) and the event-driven model we had built. The lesson: when integrating with an existing WMS, ask specifically whether the integration team has worked with MQTT-based systems before, and if not, plan time to walk through the event model before writing any code.

Fleet-level task routing versus single-unit dispatch

Most WMS products that support mobile robot integration dispatch tasks to a specific unit ID. This is fine for single-unit deployments. In multi-unit deployments, the WMS typically does not have information about which unit is closest to the task origin or has capacity to accept a new task. If the WMS dispatches all tasks to unit 1 and unit 2 sits idle, the integration is working technically but operationally wrong.

For operators running more than one unit, we recommend dispatching tasks to the fleet endpoint rather than to a specific unit ID. The fleet management layer handles unit assignment based on current position, battery level, and task queue state. The WMS sends one dispatch instruction per task; the fleet manager decides which unit executes it.

The WMS still receives task events with a specific unit ID, so it knows which unit completed the task for audit and reporting purposes. The unit assignment decision is just moved from the WMS to the fleet manager. This avoids the problem of a WMS that picks unit assignment based on round-robin logic or fixed rules becoming a bottleneck when unit states are unequal.

Practical notes on commissioning

Before going live with a WMS integration, run through three checks that consistently surface issues in our commissioning process. First, confirm that the WMS can correctly parse the task_event MQTT payload schema and that no WMS-side field name mappings are silently discarding data. We have seen WMS integrations where task completion events were being received but the WMS was not updating task state because the completion status field name used a different capitalization convention than the WMS expected.

Second, test the expired task path explicitly. Configure an expiry time in the past, dispatch the task, and confirm the WMS receives and correctly handles the EXPIRED event. This path is rarely tested and is the most common failure mode when a robot goes into maintenance and task backlog builds up.

Third, validate position coordinate alignment. The robot's coordinate system origin needs to match the WMS's map reference point. A coordinate system offset of a few centimeters causes no practical navigation error but causes the WMS position display to show the robot in the wrong aisle, which confuses operators who use the WMS map for visual fleet tracking. Run the robot to a known landmark position, compare the robot's reported coordinates to the WMS map coordinates for that landmark, and correct any offset in the coordinate transform configuration before go-live.

None of these are complex problems. They all take less than an hour to diagnose and fix if you find them during commissioning. They take considerably longer to diagnose in production when an operator is asking why their WMS shows the robot in aisle B when a picker can see it in aisle D.

More from Field Notes

Depth camera vs ultrasonic proximity

Depth camera and ultrasonic proximity: why we use both

Floor contact force sensing

Why we added floor-contact force sensing as a third proximity layer

Proximity-aware robot design principles

Three design principles for a robot that works arm-to-arm with people