Skip to content

Inventory checker

When your own system holds the real stock — seats, slots, tables — Localoy can ask it before taking a booking, so a customer is never sold something you no longer have.

1Link an item

Give a catalogue item a localoyRef naming the Localoy ticket type, activity item or restaurant it stands for. GET /bookables lists the ids.

2Switch it on

Save your endpoint's URL under Inventory Checker on the Self-Managed page and switch the live one on.

3Answer each question

Before every booking of a linked item, Localoy calls your endpoint and books only if you say yes.

Both switches are needed: an item that is not linked, or a live endpoint that is off, means Localoy books without asking — exactly as before. Linking is part of the item write (see The catalog item), so a sync that already sends your items can send the link too.

What Localoy sends
GET https://yoursite.com/localoy/inventory?external_id=TICKET-VIP&quantity=2&at=2026-10-02T13%3A30%3A00.000Z&module=event
X-Localoy-Signature: t=1791552600,v1=5b1f0c…
X-Localoy-Request-Id: 1d0e7a52-6c2b-4f0e-9d7a-3b9a0c4e8f21
User-Agent: Localoy-Inventory/1.0
Query parameterMeaning
external_idYour item's externalId — the item linked to what is being booked.
quantityHow many the customer is asking for. A whole number, 1 or more.
atThe date or time asked for, as an ISO-8601 UTC timestamp, when the booking has one. Absent otherwise.
moduleevent, activity or dining — which kind of booking is asking.
What to answer — 200, JSON
{ "available": true, "remaining": 12 }

available is required and must be a boolean; remaining is optional, and when present must be a whole number of 0 or more. Anything else — a different status, a body that is not that JSON, more than 64 KB, a redirect — is not an answer.

Verify the signature — over the path and query

It is the webhook scheme with the path and query in place of a body: v1 = HMAC-SHA256(secret, t + "." + path?query), keyed by this endpoint's invsec_… secret. The question lives in the query string, so signing it is what stops someone replaying a signature to ask about a different item or quantity. Check t is recent, and compare in constant time.

An endpoint, end to end (Node.js)
const express = require("express");
const crypto = require("node:crypto");

const app = express();
const SECRET = process.env.LOCALOY_INVENTORY_SECRET; // invsec_…

app.get("/localoy/inventory", (req, res) => {
  const header = req.get("X-Localoy-Signature") ?? "";
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const timestamp = Number(parts.t);
  if (!Number.isFinite(timestamp) || Math.abs(Date.now() / 1000 - timestamp) > 300) {
    return res.status(400).json({ error: "stale" });
  }

  // Signed: "<t>." + the path AND query exactly as requested.
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(timestamp + "." + req.originalUrl)
    .digest();
  const provided = Buffer.from(parts.v1 ?? "", "hex");
  if (provided.length !== expected.length || !crypto.timingSafeEqual(provided, expected)) {
    return res.status(401).json({ error: "bad signature" });
  }

  const left = stockFor(req.query.external_id, req.query.at);
  const wanted = Number(req.query.quantity);
  res.json({ available: left >= wanted, remaining: left });
});

app.listen(8080);

When your system does not answer

You have 1.5 seconds by default — a customer is waiting. A timeout, an error or an unreadable answer is decided by Localoy's policy, which the Inventory Checker panel states: by default the booking is refused, so an outage on your side cannot oversell. After five failures in a row Localoy stops asking for a minute and applies the same policy without a call, so a dead endpoint does not slow every booking down. A "available": false always refuses the booking — it is an answer, not a failure.

Test an endpoint with Send test check on the panel: it makes one real, signed request whether or not the endpoint is switched on, and books nothing. Every check — from bookings and from tests — is listed under Inventory Checks, and failures appear on Integration Management.