Device Ingest API#

Self Hosted Cloud Hosted

This page covers the public device-ingest endpoints only — the ones a device firmware author or integrator needs. The full internal API (batches, taps, vessels, devices, auth, admin, etc.) is not repeated here.

On the hosted service, devices post to ingest.brewgraph.com. Use the URL the app shows when you add a device (for example https://ingest.brewgraph.com/ingest/ispindel); the /api/ingest/... paths below work there too. HTTPS is the default; plain http:// is accepted for firmware that cannot do TLS.

Authentication#

Each device is issued its own token at registration time. The token is included in the request payload (field name varies slightly per endpoint — see below), not as a bearer header — this lets devices authenticate without implementing OAuth/JWT in constrained firmware.

Ingest endpoints#

Endpoint

Device

Notes

POST /api/ingest/ispindel

iSpindel

Native iSpindel JSON payload

POST /api/ingest/tiltbridge

TiltBridge (Tilt Hydrometer)

TiltBridge generic JSON payload; Cloud Hosted only. The token goes in the address

POST /api/ingest/gravitymon

GravityMon

GravityMon-native payload format

POST /api/ingest/pressuremon

PressureMon

Pressure, temperature, battery, and signal strength; available on Cloud Hosted

POST /api/ingest/kegmon

KegMon

Pour detection pulses. Coming soon to Cloud Hosted; not available in the beta

POST /api/ingest/kegmon/beer

KegMon

Beer-assignment and pour-report payloads. Coming soon to Cloud Hosted; not available in the beta

POST /api/ingest/dispatch

Any supported ingest device

Detects the compatible payload shape and routes it to the appropriate handler

Request examples#

These gravity device examples work in the Cloud Hosted beta. Replace YOUR-DEVICE-TOKEN with the token shown when you registered the device, and use the exact URL the app shows for that device. On the hosted service the URL starts with https://ingest.brewgraph.com. Every request returns 200 with no body on success. An unknown token returns 401, a request sent too soon after the device’s plan interval returns 429, and a value that cannot be stored returns 422.

Measurements are stored as sent: numeric values are not range-checked or clamped, so negative pressure is accepted. A request returns 422 when its body cannot be parsed, a required value is missing, a number is not finite, or a unit is unsupported. Unit names are case-insensitive. An omitted optional field is shown as empty, not as zero. Negative volume is rejected with the field name in the ingestion log. On Self Hosted, a KegMon volume above vessel capacity is stored and raises the vessel capacity to match.

Battery voltage and percentage are stored independently and shown independently when present; neither is calculated from the other. Reading responses expose them as batteryVoltage and batteryPercent. When enabled, a native RAPT Pill reports gravity as specific gravity (SG) and battery as a percentage.

The token can go in the JSON body as token, in ?token=, or in the path (/ingest/<device>/<token>) for gravity, pressure, and temperature devices (iSpindel, GravityMon, PressureMon). If the body also has a token, the body wins. TiltBridge has no token field in its payload, so it takes the token only as ?token= or in the path. The examples use a placeholder, never a real token. The device ID is accepted but can never authenticate a request on the hosted service. Fields not listed as stored are ignored.

GravityMon#

curl -X POST https://ingest.brewgraph.com/ingest/gravitymon \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Gravitymon_01",
    "ID": "12345678",
    "token": "YOUR-DEVICE-TOKEN",
    "angle": 42.15,
    "temperature": 19.5,
    "temp_units": "C",
    "battery": 4.12,
    "gravity": 1.045,
    "corr-gravity": 1.044,
    "gravity-unit": "G",
    "velocity": -1.5,
    "interval": 900,
    "RSSI": -65,
    "run-time": 2.14
  }'
  • gravity-unit must be G (specific gravity). P (Plato) is rejected with 422.

  • temp_units must be C or F; any other value is rejected with 422. Fahrenheit is converted to Celsius. If you leave it out, Celsius is assumed.

  • corr-gravity is deliberately ignored: BrewGraph records the raw gravity value. name and interval are accepted but not stored.

iSpindel#

curl -X POST https://ingest.brewgraph.com/ingest/ispindel \
  -H "Content-Type: application/json" \
  -d '{
    "name": "[SG] iSpindel",
    "ID": 13065051,
    "token": "YOUR-DEVICE-TOKEN",
    "angle": 66.79,
    "temperature": 20.56,
    "temp_units": "C",
    "battery": 4.13,
    "gravity": 1.045,
    "interval": 900,
    "RSSI": -77
  }'
  • In the iSpindel firmware, set the device name to start with the exact prefix [SG]. The iSpindel’s calibration must output specific gravity. BrewGraph does not guess the scale from the number.

  • temp_units follows the same rule as for GravityMon.

  • name and interval are accepted but not stored.

TiltBridge (Tilt Hydrometer)#

Cloud Hosted only. TiltBridge posts its generic JSON payload. The token is part of the address, as ?token= or as the last path segment.

curl -X POST https://ingest.brewgraph.com/ingest/tiltbridge/YOUR-DEVICE-TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Red",
    "temp": "68.5",
    "temp_unit": "F",
    "gravity": "1.050",
    "gravity_unit": "G",
    "device_source": "TiltBridge"
  }'
  • gravity is required, as a number or a numeric string. gravity_unit is G (specific gravity) or P (Plato, converted to specific gravity); if you leave it out, G is assumed.

  • temp is optional. temp_unit is C or F (Fahrenheit is converted to Celsius); if you leave it out, Celsius is assumed.

  • Unit letter case is ignored. Any other unit is rejected with 422.

  • name (the Tilt’s colour) and device_source are accepted and ignored: the device is the one the token belongs to.

  • A request without a valid token returns 401.

KegMon#

Coming soon to Cloud Hosted; not available in the beta. KegMon sends a pour event, or a level event after the measured volume is stable, to /ingest/kegmon. Volumes are in litres.

{
  "token": "YOUR-TAP-TOKEN",
  "pour": 0.35,
  "volume": 14.2,
  "maxVolume": 19.0,
  "temperature": 4.2
}
  • temperature (°C) is optional on both the pour and the level event. On Cloud Hosted it is stored as the keg’s temperature and shows in the keg’s temperature chart. It never changes what the event does: the pour and the level are stored whether it is present, missing, or invalid.

  • On Cloud Hosted, a pour or level report for a tap with no keg on it is accepted and ignored: nothing is stored.

  • On Cloud Hosted, a tap accepts at most 20 pour reports per minute.

See Devices for the full per-device setup guide (calibration for gravity sensors, etc.) — this page is just the wire protocol reference.