Skip to main content
Technical Preview

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 as read, 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)

Set WITH 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:
  1. Client sends a hello (username) to a protected URI.
  2. Server answers 401 with WWW-Authenticate: SCRAM ....
  3. After the SCRAM exchange the server returns authToken in Authentication-Info.
  4. Subsequent calls send Authorization: BEARER authToken=<token>.
On nhaystack, operators often enable 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 uses Accept / 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):
Read by filter:
HisRead today:
PointWrite level 16:

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

  1. WatchSub with watchDis (+ optional lease) and id rows → response meta.watchId.
  2. Poll with WatchPoll (meta.watchId; add refresh Marker for a full refresh).
  3. WatchUnsub with close Marker (or remove individual ids).
Lease expiry on the server closes the watch if you stop polling.

Not packaged

SkySpark / Haxall extensions such as eval 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-run smoke_haystack.py with HAYSTACK_BASE_URL and HAYSTACK_AUTHORIZATION, or complete a broker MQTT round-trip.
Last modified on October 21, 2026