Integrate

Weighing

Synchronous weigh and tare, watching a scale live, and what an unstable reading means.

Weighing is synchronous, and that is the main thing to know. Unlike printing, which is accepted now and delivered eventually, a weigh request holds the HTTP connection open until the scale answers or the request times out. A cashier is standing there; a weight that arrives later is not a weight.

A scale can also be watched live, which is a different thing for a different purpose: the stream is what you put on a screen, the weigh is what you charge from.

Le Comptoir · Counter scale · online
0.000kgempty

steady · sent to your screen

A weigh request, from your call to the number on the display.

Before you can weigh

The scale needs a manufacturer and model set in the dashboard. Scales do not identify themselves the way printers do, so Raven does not guess: an unconfigured scale rejects weigh and tare with 409 rather than returning a number it cannot vouch for. See Scale settings.

Taking a reading

POST /scales/{scaleId}/weigh
curl -X POST https://raven-api.formfl.be/scales/{scaleId}/weigh \
  -H "Authorization: ApiKey $RAVEN_KEY"

A successful call returns the reading directly:

200 OK
{
  "value": 0.842,
  "unit": "kg",
  "stable": true
}

Check stable before you use the number. An unstable reading is a scale still settling, or something being leaned on. What to do about it is a product decision: most integrations show the value greyed out and refuse to commit the sale until it stabilises.

Taring

Zero the scale with whatever is currently on it, so the next reading excludes the container.

POST /scales/{scaleId}/tare
curl -X POST https://raven-api.formfl.be/scales/{scaleId}/tare \
  -H "Authorization: ApiKey $RAVEN_KEY"

Also synchronous, returning success or a timeout.

When it doesn't answer

CodeMeansDo
504 The scale did not answer within 10 seconds: it is off, unplugged, or the connector lost its serial link. Retry once. Weighing is safe to repeat, since it reads state rather than changing it. If it keeps timing out, it is a hardware problem at the venue.
503The connector is offline, or dropped mid-request. The venue is unreachable, not the scale.Retry with backoff. Show the cashier "venue offline", not "scale broken".
429Rate limited; the scale was never asked. Retry with backoff. See Rate limits.
409No model configured, or no hardware assigned to this scale.Not retryable. Someone has to finish setting it up.
404No such scale, or not yours.Check the id.

503 and 504 mean different things and deserve different messages: 503 is the venue's link, 504 is the scale itself.

Set your own integrator timeout comfortably above Raven's ten seconds, or you will abandon requests that were about to succeed and show the cashier an error for a weight that arrived.

Watching a scale live

Everything above is a question you ask. A scale can also be watched: subscribe to one and its readings arrive as the number moves, which is what you want on a screen showing the plate while someone loads it. Raven pushes these over a SignalR connection.

Subscribing to a scale
import { HubConnectionBuilder } from "@microsoft/signalr"

const hub = new HubConnectionBuilder()
  .withUrl("https://raven-api.formfl.be/hubs/events", {
    headers: { Authorization: `ApiKey ${process.env.RAVEN_KEY}` },
  })
  .withAutomaticReconnect()
  .build()

hub.on("ScaleReading", r => {
  // { scaleId, value, unit, stable, timestamp }
  console.log(r.value, r.unit, r.stable ? "steady" : "moving")
})

await hub.start()
await hub.invoke("SubscribeScale", scaleId)

// Re-subscribe after a reconnect: the server keeps no subscription across your dropped connection.
hub.onreconnected(() => hub.invoke("SubscribeScale", scaleId))

SubscribeScale(scaleId) starts it and UnsubscribeScale(scaleId) ends it. Raven runs the scale's poll loop only while somebody is watching, so unsubscribing when your screen closes is not merely tidy.

How often ticks arrive

The cadence adapts to the scale rather than being fixed: on a Kern, roughly ten readings a second while the load is moving, decaying to one a second once it has settled, and snapping back the moment it moves again. Treat those numbers as the current behaviour of one driver, not as a rate to build timing on. Render whatever arrives.

Every tick carries its own stable, read from the scale on that reply. It genuinely flips: false while a load is being placed, true once the reading settles. That is the difference from the synchronous /weigh, which asks for a settled measurement and so answers true.

When the venue drops mid-stream

Ticks stop, and nothing tells you they have. Your connection to Raven is fine; it is the connector that went away, and a scale nobody can reach has no reading to report. When the venue comes back, Raven restarts the loop for every scale still being watched and ticks resume on their own, with no call from you.

So do not treat a gap as a zero, or as a value that stopped changing. Time out on your side after a few seconds of silence and say so on screen, then let the readings replace it when they return. If you need to know the venue's state rather than infer it, a /weigh answers 503 while the connector is offline.

What a live tick is not

  • It is not a record. Ticks are ephemeral: never stored, never metered, absent from the readings history and from interactions. POST /weigh remains the only reading Raven keeps, and it is the one to charge from. Watch with the stream, commit with the weigh.
  • It is not for the browser. The connection authenticates with an API key in the Authorization header, and a browser cannot set headers on a WebSocket handshake. It would be the wrong place for the key regardless: one key reaches every venue in your account, so it belongs on your server, which relays what it needs to the screen. See Your account is your business.
  • It is not guaranteed delivery. A dropped tick is simply a tick you did not get; nothing replays it. That is the correct trade for a number that is superseded fifty milliseconds later, and the reason a sale is committed against a weigh rather than against the last thing you saw.
  • It needs the same configuration. A scale with no manufacturer and model set never emits ticks, exactly as it refuses to weigh.

Your subscription is per connection. If it drops, re-subscribe on reconnect; the sample above does.

Reading history

GET /scales/{id}/readings returns past readings, paginated. Useful for reconciling a disputed sale, not for polling: a single current reading comes from /weigh, and a continuously moving one from the stream. This is the only device type with a key-reachable history list. See Looking back at past jobs for why.

Supported models

Kern devices on serial, either as KCP, which covers any Kern speaking the Kern Communications Protocol, or as the specific KFB-TM. Both drive the device identically; the family entry exists so a Kern that is not a KFB-TM works without waiting for a connector release. GET /scales/models returns the live catalogue and is the authority. Why the model is an operator input rather than a detection is on Scale settings.