> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Sight Machine

> Client-mode REST API connector for Sight Machine: read machines, schemas, cycles, downtime, parts, line and raw data, KPIs and Data Viz results, cookbook recipes and dashboard values from MQTT on your Sight Machine tenant

Technical Preview

## Overview

[Sight Machine](https://sightmachine.com) is a Manufacturing Data Platform. It models plant data as machines, cycles, downtime, parts and production lines, and computes KPIs such as OEE, quality and availability. This connector connects the Coreflux broker to the REST API of your Sight Machine tenant, so plant logic on MQTT can read production data, KPI results, recipe recommendations and dashboard values without custom code.

* **SightMachineApi** (`SightMachineRestApi.lot`) is a client connector on `https://YOUR_TENANT.sightmachine.io` with 24 events.

> [!WARNING]
>
> Sight Machine does not publish its API reference. Each tenant serves it to logged-in users at `https://<tenant>.sightmachine.io/#/api-docs`. These events come only from public first-party sources: the [API Keys and Dashboard API page](https://docs.sightmachine.com/docs/apis) and the [Sight Machine Python SDK](https://github.com/sightmachine/sightmachine-sdk) (`smsdk`), which calls these endpoints. Request bodies follow the SDK. Check the response shapes against your tenant's API reference before you rely on them.

Every path and method was checked on a live Sight Machine tenant. Each event is rejected for an invalid key, while unknown paths return `404` and wrong methods return `405` (see Status).

## Tenant host

Each customer gets its own tenant at `https://<tenant>.sightmachine.io`. Replace `YOUR_TENANT` in `BASE_ADDRESS` with your tenant name, with no trailing slash. For a private or dedicated deployment, use the host Sight Machine gave you.

## Authentication

> [!WARNING]
>
> The API key and secret act as your Sight Machine user. Keep them in Hub secrets, map them in with `GET SECRET`, and never publish them on a topic other clients can read.

1. In your tenant, open **User Profile** (top right), scroll to **Security** and click **Create API Key**. Copy the API key and secret.
2. Every event takes `"api_key_id"` and `"api_secret"` in its MQTT payload. The connector sends them as `X-SM-API-Key-Id` and `X-SM-API-Secret`. `ListFactories` (legacy `/api`) also sends `X-SM-API-Key` with the secret, like the SDK does.

Suggested secrets: `SIGHTMACHINE_API_KEY_ID` and `SIGHTMACHINE_API_SECRET`. A missing or wrong key returns `401` with `{"error":"unauthenticated","description":"Not logged in or invalid API key provided"}`.

> [!NOTE]
>
> Edge API keys (the **CONNECT** menu) are a different credential. FactoryTX uses them to upload data. If your tenant uses workspaces, the SDK selects one with an `X-Sm-Workspace-Id` header. This template does not send that header, so add it to the events if you need it.

## Payload conventions

* **Queries.** The `/v1/datatab/...`, estimate, KPI, Data Viz, line Data Viz and cookbook current-value events are read-only `POST` queries. Each one sends `"body"` unchanged. The body is the SDK query object: `asset_selection`, `select`, `where` (`name`, `op`, `value`), `order_by`, `time_selection` (relative or absolute), `offset`, `limit` and `db_mode` (`"sql"`). See the [SDK reference](https://github.com/sightmachine/sightmachine-sdk/blob/master/docs/README.md) for every field.
* **Names.** Use system names in queries: `machine_source` (for example `Oven_1`), `machine_type` (`Oven`), field names from `GetMachineTypeFields`, and part types from `ListPartTypes`. `ListMachines` and `ListMachineTypes` map display names to system names.
* **Query strings.** Events with `?{query}` take a pre-encoded query string. Send `""` for none. `ListMachines` takes `limit` plus `where`, `order_by` and `select` as URL-encoded JSON.
* **Paging.** Data table events return up to `limit` rows (at most 50000) under `results`. Page with `offset`.
* **Asynchronous Data Viz.** `SubmitDataVizTask` and `SubmitLineVizTask` return `response.task_id`. Poll `GetDataVizTask` or `GetLineVizTask` until `response.state` is `SUCCESS`, then read `response.meta.results`. `FAILURE` and `REVOKED` mean the query failed.

## Assets and metadata

| Event | Request | Topic | Notes |
| - | - | - | - |
| ListAssets | `GET /v1/selector/assets` | `mes/sight-machine/asset/list` | Auth smoke: machines, lines and KPIs the key can see (the SDK's own login check). Response has machine, line and kpi lists |
| ListMachines | `GET /v1/obj/machine?{query}` | `mes/sight-machine/machine/list` | Machines with metadata (source, source\_clean, source\_type, timezone ...); query limit, where, order\_by, select (send "" for none) |
| ListMachineTypes | `GET /v1/obj/machine_type?{query}` | `mes/sight-machine/machine-type/list` | Machine types (source\_type, source\_type\_clean, stats ...); send query "" for none |
| GetMachineTypeFields | `GET /v1/selector/datatab/cycle/{machine_type}/field?{query}` | `mes/sight-machine/machine-type-field/list` | Cycle schema of a machine type: fields with name, display\_name, type, data\_type, unit, stream\_types, raw\_data\_field |
| ListPartTypes | `GET /v1/obj/part_type?{query}` | `mes/sight-machine/part-type/list` | Part types configured on the tenant |
| GetPartTypeFields | `GET /v1/selector/datatab/part/{part_type}/field?db_mode=sql&strip_aliases=false` | `mes/sight-machine/part-type-field/list` | Part schema of a part type (fields, types, units) |
| ListFactories | `GET /api/factory?{query}` | `mes/sight-machine/factory/list` | Factories (sites) with timezone and metadata. Legacy /api surface: also sends X-SM-API-Key like the SDK |

**ListMachines** (`mes/sight-machine/machine/list`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "query": "limit=100"
}
```

**GetMachineTypeFields** (`mes/sight-machine/machine-type-field/list`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "machine_type": "Oven",
  "query": "_offset=0&_limit=2000"
}
```

## Production data (cycles, downtime, parts, lines, raw data)

| Event | Request | Topic | Notes |
| - | - | - | - |
| QueryCycles | `POST /v1/datatab/cycle` | `mes/sight-machine/cycle/query` | Catalog: cycles of a machine (one row per production cycle with its fields); read-only query, page with offset / limit (max 50000) |
| QueryDowntimes | `POST /v1/datatab/downtime` | `mes/sight-machine/downtime/query` | Downtime events of a machine (start, end, duration, reason, category, type); read-only query |
| QueryParts | `POST /v1/datatab/part` | `mes/sight-machine/part/query` | Parts of a part type (one row per serialized part, joined across machines); read-only query |
| QueryLineData | `POST /v1/datatab/line` | `mes/sight-machine/line-data/query` | Line data: one unified table across the machines of a production line; read-only query |
| QueryRawData | `POST /v1/datatab/raw_data` | `mes/sight-machine/raw-data/query` | Catalog: raw sensor records of a raw data table (before cycle modelling); read-only query, not an ingest |

**QueryCycles** (`mes/sight-machine/cycle/query`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "body": {
    "asset_selection": {
      "machine_source": "Oven_1",
      "machine_type": "Oven"
    },
    "select": [
      {
        "name": "endtime"
      },
      {
        "name": "machine__source"
      },
      {
        "name": "total"
      }
    ],
    "where": [
      {
        "name": "endtime",
        "op": "gte",
        "value": "2026-10-05T00:00:00"
      },
      {
        "name": "endtime",
        "op": "lt",
        "value": "2026-10-06T00:00:00"
      }
    ],
    "order_by": [
      {
        "name": "endtime",
        "order": "desc"
      }
    ],
    "offset": 0,
    "limit": 400,
    "db_mode": "sql"
  }
}
```

**QueryDowntimes** (`mes/sight-machine/downtime/query`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "body": {
    "asset_selection": {
      "machine_source": "Oven_1",
      "machine_type": "Oven"
    },
    "select": [
      {
        "name": "machine__source"
      },
      {
        "name": "starttime"
      },
      {
        "name": "endtime"
      },
      {
        "name": "total"
      },
      {
        "name": "reason"
      },
      {
        "name": "category"
      },
      {
        "name": "downtime_type"
      }
    ],
    "where": [
      {
        "name": "starttime",
        "op": "gte",
        "value": "2026-10-05T00:00:00"
      },
      {
        "name": "endtime",
        "op": "lt",
        "value": "2026-10-06T00:00:00"
      }
    ],
    "offset": 0,
    "limit": 400,
    "db_mode": "sql"
  }
}
```

**QueryRawData** (`mes/sight-machine/raw-data/query`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "body": {
    "asset_selection": {
      "raw_data_table": "oven_sensors"
    },
    "select": [
      {
        "name": "temperature"
      },
      {
        "name": "pressure"
      }
    ],
    "time_selection": {
      "time_type": "absolute",
      "start_time": "2026-10-05T00:00:00.000Z",
      "end_time": "2026-10-06T00:00:00.000Z",
      "time_zone": "Europe/Lisbon"
    },
    "db_mode": "sql",
    "offset": 0,
    "limit": 400
  }
}
```

## Counts

| Event | Request | Topic | Notes |
| - | - | - | - |
| EstimateCycleCount | `POST /v1/selector/datavis/estimate/cycle` | `mes/sight-machine/cycle-count/get` | Estimated cycle count of a machine type in a time window (read-only) |
| EstimatePartCount | `POST /v1/selector/datavis/estimate/part` | `mes/sight-machine/part-count/get` | Estimated part count of a part type in a time window (read-only) |

**EstimateCycleCount** (`mes/sight-machine/cycle-count/get`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "body": {
    "model": "cycle:Oven",
    "asset_selection": {
      "machine_source": [
        "Oven_1"
      ]
    },
    "time_selection": {
      "time_type": "absolute",
      "start_time": "2026-10-01T00:00:00",
      "end_time": "2026-10-06T00:00:00"
    },
    "limit": 1,
    "db_mode": "sql"
  }
}
```

## KPIs and Data Viz

| Event | Request | Topic | Notes |
| - | - | - | - |
| ListKpisForAsset | `POST /v1/selector/datavis/kpi/y_axis` | `mes/sight-machine/kpi-asset/list` | Catalog: KPIs available for machines / machine types (quality, oee, availability, performance ...) |
| SubmitDataVizTask | `POST /v1/datavis/task/async` | `mes/sight-machine/datavis-task/create` | Catalog: start a Data Viz KPI query (OEE, quality, availability ... aggregated over time); returns response.task\_id. Read-only compute |
| GetDataVizTask | `GET /v1/datavis/task/async/{task_id}` | `mes/sight-machine/datavis-task/get` | Poll a Data Viz task: response.state PENDING / SUCCESS / FAILURE / REVOKED; results in response.meta.results |
| SubmitLineVizTask | `POST /v1/linevis/task/async` | `mes/sight-machine/linevis-task/create` | Start a line Data Viz query across the machines of a line; returns response.task\_id. Read-only compute |
| GetLineVizTask | `GET /v1/linevis/task/async/{task_id}` | `mes/sight-machine/linevis-task/get` | Poll a line Data Viz task (state, meta.results) |
| CreateShareLink | `POST /v1/obj/ui_share_state` | `mes/sight-machine/share-link/create` | Save a Data Viz chart state and get a shareable link (for alerts and shift reports); state\_hash is any 8-char id **(write)** |

**ListKpisForAsset** (`mes/sight-machine/kpi-asset/list`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "body": {
    "asset_selection": {
      "machine_type": [
        "Oven"
      ],
      "machine_source": [
        "Oven_1"
      ]
    },
    "db_mode": "sql"
  }
}
```

**SubmitDataVizTask** (`mes/sight-machine/datavis-task/create`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "body": {
    "model": "kpi",
    "asset_selection": {
      "machine_source": [
        "Oven_1"
      ],
      "machine_type": [
        "Oven"
      ]
    },
    "d_vars": [
      {
        "name": "oee",
        "aggregate": [
          "avg"
        ]
      },
      {
        "name": "quality",
        "aggregate": [
          "avg"
        ]
      }
    ],
    "i_vars": [
      {
        "name": "endtime",
        "time_resolution": "hour",
        "query_tz": "Europe/Lisbon",
        "output_tz": "Europe/Lisbon",
        "bin_strategy": "user_defined2",
        "bin_count": 50
      }
    ],
    "time_selection": {
      "time_type": "relative",
      "relative_start": 1,
      "relative_unit": "day",
      "ctime_tz": "Europe/Lisbon"
    },
    "where": [],
    "db_mode": "sql"
  }
}
```

**GetDataVizTask** (`mes/sight-machine/datavis-task/get`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "task_id": "<task id>"
}
```

## Cookbooks (recipes)

| Event | Request | Topic | Notes |
| - | - | - | - |
| ListCookbooks | `GET /v1/obj/recipe/cookbook?{query}` | `mes/sight-machine/cookbook/list` | Cookbooks (recipe optimisation): recipe groups, levers, constraints, KPI |
| GetCookbookTopResults | `GET /v1/recipe/top_results/{recipe_group_id}?limit={limit}` | `mes/sight-machine/cookbook-top-results/get` | Best recipe runs of a recipe group (lever settings that gave the best KPI) |
| GetCookbookCurrentValues | `POST /v1/recipe/current_value` | `mes/sight-machine/cookbook-current-value/get` | Catalog: current value of levers / fields per machine (latest value within the last N minutes); read-only |

**GetCookbookCurrentValues** (`mes/sight-machine/cookbook-current-value/get`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "body": {
    "variables": [
      {
        "asset": "Oven_1",
        "name": "Temperature"
      },
      {
        "asset": "Oven_1",
        "name": "Pressure"
      }
    ],
    "minutes": 60
  }
}
```

## Dashboards

| Event | Request | Topic | Notes |
| - | - | - | - |
| GetDashboardValues | `GET /v1/obj/dashboard/{dashboard_id}/full_values` | `mes/sight-machine/dashboard-values/get` | Catalog: resolved values of every background-loading widget of a dashboard (panels with id, name, type single \| table, status, data) |

**GetDashboardValues** (`mes/sight-machine/dashboard-values/get`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key_id": "<api key id>",
  "api_secret": "<api secret>",
  "dashboard_id": "65552e878f90924c54a3de7f"
}
```

The response has one entry per widget under `panels`, with `id`, `name`, `type` (`single` or `table`), `status` and `data`. Only Data Visualization and Single Value widgets set to [background loading](https://docs.sightmachine.com/docs/background-loading-for-widgets) return data. The others report `unsupported type`, `background loading not enabled` or `no completed jobs found`. The dashboard id is the last part of the dashboard URL.

## Sending plant data to Sight Machine

Sight Machine takes plant data in through FactoryTX, its edge data layer, not through a public REST write. Its upload endpoints are not published, so this template has no ingest event. To send Coreflux data to Sight Machine, point a FactoryTX [MQTT Data Receiver](https://docs.sightmachine.com/docs/mqtt-data-receiver) at the Coreflux broker. Sight Machine documents no webhooks, so this template has no receiver connector either.

## Examples

Helper actions (`docs-helper-actions.lot`): an OEE Data Viz query every 15 minutes with polling of the task, the dashboard values every hour, sent on to a plant topic, and current lever values every 5 minutes.

```
DEFINE ACTION SightMachinePollLineOee
ON EVERY 15 MINUTES DO
    SET "kid" WITH GET SECRET "SIGHTMACHINE_API_KEY_ID"
    SET "sec" WITH GET SECRET "SIGHTMACHINE_API_SECRET"
    PUBLISH TOPIC "mes/sight-machine/datavis-task/create" WITH { "api_key_id": {kid}, "api_secret": {sec}, "body": { "model": "kpi", "asset_selection": { "machine_source": ["Oven_1"], "machine_type": ["Oven"] }, "d_vars": [ { "name": "oee", "aggregate": ["avg"] } ], "i_vars": [ { "name": "endtime", "time_resolution": "hour", "query_tz": "Europe/Lisbon", "output_tz": "Europe/Lisbon", "bin_strategy": "user_defined2", "bin_count": 24 } ], "time_selection": { "time_type": "relative", "relative_start": 1, "relative_unit": "day", "ctime_tz": "Europe/Lisbon" }, "where": [], "db_mode": "sql" } }

DEFINE ACTION SightMachinePollDataVizTask
ON TOPIC "mes/sight-machine/datavis-task/create/response" DO
    SET "kid" WITH GET SECRET "SIGHTMACHINE_API_KEY_ID"
    SET "sec" WITH GET SECRET "SIGHTMACHINE_API_SECRET"
    SET "task" WITH (GET JSON "response.task_id" IN PAYLOAD AS STRING)
    KEEP TOPIC "internal/sight-machine/datavis-task" WITH {task}
    PUBLISH TOPIC "mes/sight-machine/datavis-task/get" WITH { "api_key_id": {kid}, "api_secret": {sec}, "task_id": {task} }

DEFINE ACTION SightMachineShiftDashboard
ON EVERY 1 HOUR DO
    SET "kid" WITH GET SECRET "SIGHTMACHINE_API_KEY_ID"
    SET "sec" WITH GET SECRET "SIGHTMACHINE_API_SECRET"
    SET "dash" WITH GET SECRET "SIGHTMACHINE_DASHBOARD_ID"
    PUBLISH TOPIC "mes/sight-machine/dashboard-values/get" WITH { "api_key_id": {kid}, "api_secret": {sec}, "dashboard_id": {dash} }

DEFINE ACTION SightMachineDashboardToPlant
ON TOPIC "mes/sight-machine/dashboard-values/get/response" DO
    PUBLISH TOPIC "plant/porto/sight-machine/dashboard" WITH PAYLOAD

DEFINE ACTION SightMachineCurrentLevers
ON EVERY 5 MINUTES DO
    SET "kid" WITH GET SECRET "SIGHTMACHINE_API_KEY_ID"
    SET "sec" WITH GET SECRET "SIGHTMACHINE_API_SECRET"
    PUBLISH TOPIC "mes/sight-machine/cookbook-current-value/get" WITH { "api_key_id": {kid}, "api_secret": {sec}, "body": { "variables": [ { "asset": "Oven_1", "name": "Temperature" }, { "asset": "Oven_1", "name": "Pressure" } ], "minutes": 60 } }
```

> [!NOTE]
>
> The helper actions read the key and secret with `GET SECRET` on every call. Restrict the `mes/sight-machine/#` request topics with broker ACLs, because the payloads carry the secret.

## Writes

Write events: CreateShareLink. `CreateShareLink` saves a Data Viz chart state on the tenant and returns a share link. Every other event is read-only, including the `POST` query events. No preset deletes anything.

## Not packaged

Data ingest (FactoryTX upload and RDPv3 endpoints are not published), password and SSO login (`/auth/password/login` sets a browser session cookie, so use API keys), the ODBC / JDBC connector (SQL, not REST), legacy `/api` cycle, part, machine and downtime endpoints (replaced by `/v1`), workspace selection (`X-Sm-Workspace-Id`), and any endpoint that appears only in the tenant's login-gated API reference.

## Status

Technical preview, blocked on the authenticated live test. On the live tenant `demo.sightmachine.io`, all 24 events return `401 unauthenticated` for an invalid key, unknown paths return `404` and wrong methods return `405` before auth. A tenant API key is still needed to check real responses.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.