TutorialWMSintegrationREST API

WMS integration guide: REST, webhooks and WebSockets for a live warehouse map

REST, webhook or WebSocket? How to feed WMS data into a live 3D warehouse map: REST polling, file upload, and a table write API your relay pushes to.

11 min readPublished Updated By Spatial Map team
Acme Warehouse in 3D preview: rack bins with stock counts, worker markers, dock doors with trucks and fire extinguishers — the live map a WMS integration feeds

The short answer

REST vs webhook vs WebSocket: what you are really choosing

Search for “WMS integration” and you land in a protocol debate. For a live warehouse map the useful question is narrower: how does your WMS hand out data, and how fresh does the map need to be? The WMS may expose REST endpoints, fire webhooks, publish to a queue or stream over a socket. Spatial Map accepts three kinds of input — it pulls REST endpoints, it imports files, and it receives pushes as writes to a table’s API. So, do you need webhooks or WebSockets? Your WMS may emit them; Spatial Map receives them as table API writes, sent by a relay you own.

Where your WMS data starts → the path into Spatial MapEndpoints you can GET go through the REST API integration or a table’s server‑side Pull from a REST API; pushed events (webhooks, queues, sockets) go through your relay into the table write API; exports go through Upload file. Everything ends as rows that a data link draws on the map.
Warehouse dataFreshness you needPath into Spatial Map
Bin occupancy and stock per locationabout a minuteTable with Pull from a REST API (every 30 s or more), or relay pushes on change
Pick, move and return eventssecondsRelay → `POST /rows/bulk` every few seconds
Bin status changes (blocked, reserved, picking)secondsRelay → `PATCH /rows/bulk` on existing rows
Forklift or worker positions from an RTLSa few secondsRelay buffers the stream and sends batches — never one request per position fix
Cycle count or end‑of‑day snapshotdailyUpload file and Re-import, or a nightly script calling the table API
Reference data (SKUs, zones, dock doors)when it changesUpload file, or a one‑off bulk write

The four ways data gets in

Open Integrations → Add integration and you get three choices: REST API, Upload file and Table. A table then adds a fourth route — its own write API — plus an optional server‑side poller. They differ mainly in where they run.

PathWhere it runsHow freshWatch out for
REST API integrationIn the browser of whoever has a map (or the Integrations screen) open. Sync Manually, When a map opens or On a timerEach sync; the timer only repeats while a map is openThe WMS API must allow the app’s origin (CORS), otherwise you see “Your browser is blocked from this API”
Table · Pull from a REST APIOn Spatial Map’s servers, set up on the table’s API & access tabInterval of 30 s or more; the poller checks once a minute, so expect about a minuteUp to 1,000 rows, 10 MB and 30 s per poll; the URL must be reachable from the internet
Upload fileYou, in the import dialog (File, Mode, Match, Mapping, Preview)Whenever you re‑importCSV, Excel, JSON or GeoJSON — good for snapshots, not for events
Table write APIYour systems call `https://app.spatialmap.io/api/v1/public/tables/…` with a `sgv_tbl_` keySeconds — every write refreshes open maps60 requests a minute per key (burst 30); up to 1,000 rows per bulk call

Which path fits your WMS

You don’t need a vendor‑specific connector — Spatial Map has none. What matters is the kind of interface your WMS offers. Check the integration docs for your own version; the patterns below hold for most products.

Kind of WMSWhat it usually exposesPath that fits
Enterprise suite with an event or integration layerRead APIs (REST or OData) plus business events published to an integration platform or message busServer‑side Pull from a REST API for slow‑moving state; a relay subscribed to the events writes changes with `/rows/bulk`
Cloud WMS with REST and outbound webhooksREST endpoints and a webhook per event type (task done, bin blocked, order released)Point the webhooks at your relay — a serverless function is enough — and batch into the table API; use REST pulls to reconcile
On‑premises WMS behind a firewallAPIs reachable only on the internal networkRun the relay inside the network; it only needs outbound HTTPS to the table API. Server‑side polling can’t reach an internal URL
Legacy or self‑builtA database, scheduled flat‑file exports, sometimes a thin REST layerUpload file with Re-import for exports, or a scheduled script that reads the database and writes to the table API

Bridging webhooks, queues and sockets with a small relay

A relay is any small program between the WMS and Spatial Map: a serverless function behind the WMS webhook, a consumer on the message queue, or a process that holds the RTLS socket open. Its job never changes — turn each event into a table row and send rows in batches.

  1. 1
    Create the table

    Under Tables, create one table per kind of thing (bins, pick events, workers) and pick its kind — Marker / POI, Asset (mobile), Shipment, Polygon / area and so on. Each field has an API key such as `bin_code` or `status`: those are the payload keys the relay sends. See Tables.

  2. 2
    Create a key

    On the table’s API & access tab, an Owner or Admin creates a key. It starts with `sgv_tbl_` and is shown once. One key writes to one table, so there is no table id in the URL. Give every system its own key so you can revoke one without breaking the rest, and copy the exact base URL from the same tab.

  3. 3
    Map each event to a row

    Choose a stable `entityKey` — the bin code, the event id, the badge id. Rows are upserted by that key, so a repeated delivery overwrites instead of duplicating.

  4. 4
    Send batches, not single events

    Buffer for a few seconds, then send one `POST /rows/bulk` (full rows) or `PATCH /rows/bulk` (changed fields only) with up to 1,000 rows. Valid rows commit even if others fail; rejected ones come back in `errors[]`.

  5. 5
    Retry the right errors

    `429` means the key went over 60 requests a minute; `401` means the key is wrong or revoked; `400` means a row broke the table’s schema (a required or unique field). Keep unsent rows and retry on the next tick.

bash·Upsert one row — a new pick event in Pick Events
curl -X POST https://app.spatialmap.io/api/v1/public/tables/rows \
  -H "Authorization: Bearer $SPATIALMAP_TABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"entityKey":"EV-20261011-0412","payload":{"event_id":"EV-20261011-0412","event_type":"pick","item_id":"B-B86-L2-01","from_bin_code":"B-B86-L2","quantity":2,"worker_id":"W-04","ts":"2026-10-11T09:14:05Z"}}'
bash·Upsert a batch with POST /rows/bulk (up to 1,000 rows)
curl -X POST https://app.spatialmap.io/api/v1/public/tables/rows/bulk \
  -H "Authorization: Bearer $SPATIALMAP_TABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rows":[
        {"entityKey":"EV-20261011-0413","payload":{"event_id":"EV-20261011-0413","event_type":"pick","item_id":"A-B07-L1-02","from_bin_code":"A-B07-L1","quantity":1,"worker_id":"W-02","ts":"2026-10-11T09:14:09Z"}},
        {"entityKey":"EV-20261011-0414","payload":{"event_id":"EV-20261011-0414","event_type":"move","item_id":"B-B99-L1-03","from_bin_code":"B-B99-L1","to_bin_code":"B-B51-L1","quantity":1,"worker_id":"W-05","ts":"2026-10-11T09:14:11Z"}}
      ]}'
bash·Change fields on an existing bin in Rack Inventory
curl -X PATCH https://app.spatialmap.io/api/v1/public/tables/rows/A-B01-L1 \
  -H "Authorization: Bearer $SPATIALMAP_TABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"blocked","hold_reason":"Damaged pallet"}'
js·relay.mjs — a minimal webhook‑to‑table relay (sketch)
// relay.mjs (Node 18+) - receives bin-status webhooks from your WMS and
// forwards them in batches to the "Rack Inventory" table. A sketch, not a product.
import http from 'node:http';

const BASE = 'https://app.spatialmap.io/api/v1/public/tables'; // copy yours from API & access
const KEY = process.env.SPATIALMAP_TABLE_KEY;                  // sgv_tbl_... (this table's key)
const pending = new Map();                                     // bin_code -> latest changes

http.createServer((req, res) => {
  let body = '';
  req.on('data', (chunk) => (body += chunk));
  req.on('end', () => {
    try {
      // 1. Verify your WMS's webhook signature here (its docs say how).
      const e = JSON.parse(body);
      // 2. Map the WMS event onto your table's field keys.
      const prev = pending.get(e.bin_code)?.patch ?? {};
      pending.set(e.bin_code, {
        entityKey: e.bin_code,
        patch: { ...prev, status: e.status, sku: e.sku, quantity: e.quantity },
      });
      res.writeHead(202).end();
    } catch {
      res.writeHead(400).end();
    }
  });
}).listen(8080);

// 3. Flush every 5 s: one request instead of one per event (60 requests/min per key).
setInterval(async () => {
  const rows = [...pending.values()].slice(0, 1000);
  if (rows.length === 0) return;
  rows.forEach((r) => pending.delete(r.entityKey));
  try {
    const res = await fetch(BASE + '/rows/bulk', {
      method: 'PATCH', // merge changed fields onto existing bins
      headers: { Authorization: 'Bearer ' + KEY, 'Content-Type': 'application/json' },
      body: JSON.stringify({ rows }),
    });
    if (!res.ok) throw new Error('HTTP ' + res.status); // 401 key, 429 rate limit, 5xx
    const { errors } = await res.json();                 // rows the table rejected
    if (errors?.length) console.warn(errors);
  } catch (err) {
    // put the batch back without losing newer events; retry on the next tick
    rows.forEach((r) => {
      const newer = pending.get(r.entityKey);
      pending.set(r.entityKey, newer ? { ...newer, patch: { ...r.patch, ...newer.patch } } : r);
    });
    console.error(err);
  }
}, 5000);

Worked example: Acme Warehouse

The demo Acme Warehouse has 17 racks holding 314 bins, with tables such as Rack Inventory (one row per bin, keyed by `bin_code` like `A-B01-L1`, with `status`, `sku` and `quantity`), Inventory Items, Pick Events and Warehouse Workers. A typical WMS feed for it uses three of the four paths.

  1. 1
    Bin state by polling

    If the WMS has a bin‑inventory endpoint, open Rack Inventory → API & access → Pull from a REST API: the URL, an interval of 30 s or more, Row key field in the response = `bin_code`, Where the items are (JSON path) if the list sits inside an envelope, headers for auth and a field mapping. Press Start polling; the first run lands within a minute. Full recipe: poll a REST endpoint on a schedule.

  2. 2
    Events by push

    Point the WMS’s pick‑confirmed and bin‑status webhooks at your relay. Confirmed picks become new rows in Pick Events (`POST /rows/bulk`); status changes patch the matching bin in Rack Inventory (`PATCH /rows/bulk`). Step by step: push rows with the table write API.

  3. 3
    Snapshots by upload

    The nightly cycle‑count export goes in through Upload file, and Re-import on the source card refreshes it — no code involved.

  4. 4
    Draw it on the map

    In the editor, open Data → Manage data links → New link. A Color a shape link tints each bin by `status` using style rules; a Live location marker link places workers at their latitude and longitude. See the data links overview.

  5. 5
    Check it’s flowing

    The Health tab under Integrations lists each table with when your systems last wrote to it. Every API write is also kept in the row’s History, which a Historical movement link can replay on the map.

Create new table: pick what each record describes
Create new table form with a Table name field and the kinds Polygon / area, Circle, Marker / POI (selected), Asset (mobile), Camera, Sensor, Shipment, RFID tag and Streaming / batch replay, above the field list starting with External code and Longitude
Create new table form with a Table name field and the kinds Polygon / area, Circle, Marker / POI (selected), Asset (mobile), Camera, Sensor, Shipment, RFID tag and Streaming / batch replay, above the field list starting with External code and Longitude

The pitfalls we see most

Where to go next

Try the platform your data belongs on.

Spatial Map turns spreadsheets, sensor streams, and CAD drawings into one live 3D facility view — nothing to install, seven-day free trial.

See it live

Keep reading