Skip to content
SBC-Standard The data standard for libraries

Message structure

Every message is a single line of text and contains exactly one JSON object. The controlling program sends a request, the display sends exactly one response.

→ {"v":1,"id":17,"cmd":"session.open","customer":{"masked":"B. L."}}
   │      │       │                    │
   │      │       │                    └─ data of the command
   │      │       └────────────────────── command
   │      └────────────────────────────── freely chosen request identifier
   └───────────────────────────────────── version of the protocol

← {"v":1,"id":17,"ok":true,"result":{"state":"active"}}

Connection#

The connection is an ordinary TCP connection. The display accepts it, the controlling program opens it.

By default the display listens on 127.0.0.1, that is, on the same computer only. If it sits on another computer on the library's local network it must require authentication; the interface has no business on the internet at all (see operation).

The default port is 7207. A display that uses a different one announces it in its identification file, which also states the address at which it can be reached.

Two connection models#

A display must handle both:

  1. One connection per request – connect, send, read the response, close. Stateless and robust; a controlling program needs little more than a socket. The display closes the connection once it has sent the response.
  2. Persistent connection – several requests one after another over the same connection. The display answers them in the order received; anyone wanting several requests in flight at once matches them up using the id field.

The display may close an idle persistent connection after a while (ten minutes is recommended). The controlling program must expect this and reconnect. A dropped connection does not end a running session: what is on the screen stays there.

Line and character set#

  • Every message ends with a line feed (LF, 0x0A). A carriage return before it must be tolerated by the other side, but not produced.
  • The character set is UTF-8 without a byte order mark. A BOM sent regardless is discarded by the display.
  • A line should not exceed 8 KiB; a display must process at least 8 KiB.

No checksum, no sequence number#

Anyone who knows the circulation interface will miss both. There is neither here, and that is deliberate:

  • The checksum in SIP2 dates from the days of the serial line, where a single flipped character passed unnoticed. TCP already secures the path and restores the order. If a line arrives incomplete it is not valid JSON – the display answers 400 bad_request instead of processing half a message.
  • The sequence number in SIP2 serves to request a lost response again. Nothing can be lost here without being noticed: every request receives exactly one response, and if the connection drops the controlling program knows at once.

The id field is therefore expressly not a sequence number: it only matches responses to requests when several are in flight. It need be neither gap-free nor ascending, and the display does not remember it.

If a controlling program repeats a request because no response arrived, the display may carry it out a second time. That is harmless here: every command describes a state, not a movement – session.update sets the state of the session, it does not increment anything.

The request#

Field Required Content
v yes version of the protocol, currently 1
cmd yes command, lower case, full stop as separator
id no number or string; the response returns it unchanged

All other fields belong to the command in question.

The response#

{"v":1,"id":17,"ok":true,"result":{"state":"active"}}
{"v":1,"id":18,"ok":false,"error":{"code":404,"name":"unknown_command","text":"…"}}

ok says whether the request was carried out. result is absent when there is nothing to report.

error.text is a diagnostic message for the log – not intended for display, and in no defined language. Anyone wanting to tell the staff something evaluates code and name.

Error codes#

Code name Occasion
400 bad_request line unreadable, required field missing, value unusable
401 unauthorized authentication required or failed
404 unknown_command command unknown
404 unknown_key key unknown to config.get/config.set
409 wrong_state command not meaningful in the current state
500 internal internal error of the display
501 not_supported command known but not present on this device

A controlling program must treat an unknown code like the next lower hundred.

How data is written#

Kind Notation Example
Date ISO 8601, YYYY-MM-DD "2026-08-29"
Point in time ISO 8601 with time zone "2026-08-29T17:30:00+02:00"
Time of day HH:MM local time, 24 hours "15:00"
Weekday ISO 8601: 1 Monday to 7 Sunday 2
Duration whole seconds as a number 10
Amount decimal string, full stop as separator "-3.50"
Currency ISO 4217 "EUR"
Language BCP 47 "de", "de-AT", "uk"
Boolean genuine JSON true/false
Identifiers string, even when it looks like a number "10245"

Identifiers are always transmitted as strings, even when they consist of digits alone. In some libraries patron numbers and barcode numbers begin with zeros or contain letters. Written as a JSON number both would be lost: "00123" would become 123, and "A4711" could not be represented at all.

Ready-made text as an exception#

For every displayable field x a field x_text carrying ready-made wording may be added. If it is present the display shows that text unchanged and does not word anything itself.

This is the bridge for existing programs and for libraries with wording of their own – not the regular route. Anyone using x_text gives up the display's choice of language and its authority over the design.

Extensibility#

So that the protocol can grow without breaking existing integrations, five rules apply:

  1. Unknown fields are silently ignored. Never an error. Sending new data disturbs no older display.
  2. Unknown commands yield 404 unknown_command, known but absent ones 501 not_supported. Either lets the controlling program fall back to a simpler route.
  3. Manufacturer-specific things carry a prefix: x-<manufacturer>. for commands and fields, e.g. x-ibtc.gate_alarm. Such names can never collide with the standard, now or later.
  4. Capabilities instead of a version number: what a device can do is asked with hello, not read from a version number.
  5. Data without effect is allowed. A display that does not show covers still answers ok to a request containing one. It has done nothing wrong, and the controlling program need do nothing differently.

This page was translated automatically from the German original. If anything reads oddly or looks wrong, please let us know — the German edition prevails.