Overview
Project Haystack is the industry standard for semantic tagging of equipment, points and histories in buildings and industrial sites. Compliant servers (SkyFoundry SkySpark, J2 Innovations FIN Framework, open-source Haxall, Tridium Niagara with nhaystack, Haystack Connect, and others) expose the same HTTP API: ops such asread, hisRead, hisWrite, pointWrite and watchSub exchange grids encoded as Zinc, JSON or Trio.
The ProjectHaystackApi connector maps 15 MQTT topics onto that standard ops set (4 meta GETs, 11 POSTs including 3 writes). A Coreflux broker can discover tagged points (point and temp), pull or push histories, command writable setpoints through the priority array, and poll watches for live curVal changes. Each event publishes the server reply on <topic>/response.
Endpoints are relative to
BASE_ADDRESS, which already includes the project or service path (see below).
Base URL (hosts differ)
SetWITH BASE_ADDRESS to your server root including the project / service segment. Ops are then /about, /read, /hisRead, …:
The placeholder in the template is
https://haystack.example.com/api/demo. Trailing slash is optional.
Authentication
Haystack uses a pluggable HTTP auth protocol. Compliant servers must support SCRAM with SHA-256:- Client sends a hello (username) to a protected URI.
- Server answers
401withWWW-Authenticate: SCRAM .... - After the SCRAM exchange the server returns
authTokeninAuthentication-Info. - Subsequent calls send
Authorization: BEARER authToken=<token>.
HTTPBasicScheme and send Authorization: Basic <base64(user:pass)> over HTTPS instead.
The multi-step SCRAM hello is not a connector event (the REST_API client does one HTTP call per MQTT publish). Obtain a token with a Haystack client library (phable, haystack-java, pyhaystack, …) or a small helper script, store the current header value in Hub secret HAYSTACK_AUTHORIZATION, and map it with GET SECRET. Call Close to invalidate the token when done.
Every event sends Authorization: {value.json.authorization}.
Formats (JSON grids)
The connector usesAccept / Content-Type: application/json (Haystack JSON grids with "_kind":"grid"). Zinc (text/zinc) is the HTTP API default; Trio and CSV are also standardized. Change the headers if your tooling prefers Zinc.
Request errors that the server can parse still return HTTP 200 with an error grid (meta.err Marker + dis). Always check for err in the reply before treating rows as data.
Request payload
Example grids
Empty grid (Close, Nav root, Defs all):Example actions
Writes
HisWrite, PointWrite (when the body includes level) and InvokeAction change server state. Restrict which MQTT clients may publish to energy/haystack/his/write, energy/haystack/point/write and energy/haystack/action/invoke. PointWrite with an id-only body only reads the priority array.
Watches
WatchSubwithwatchDis(+ optionallease) and id rows → responsemeta.watchId.- Poll with
WatchPoll(meta.watchId; addrefreshMarker for a full refresh). WatchUnsubwithcloseMarker (or remove individual ids).
Not packaged
SkySpark / Haxall extensions such aseval and commit, other vendor ops beyond the standard Ops chapter, and the SCRAM hello handshake as a connector event.
Sources
- Ops · HTTP API · Authentication · Ops appendix
- Hand-curated machine-readable cache:
openapi-cache/project-haystack.json(Project Haystack does not publish an official OpenAPI file)
Status
Blocked beta. Static + mock smoke passed. Unblock with a real host URL + project/service path and SCRAM (or Basic) credentials, then re-runsmoke_haystack.py with HAYSTACK_BASE_URL and HAYSTACK_AUTHORIZATION, or complete a broker MQTT round-trip.
