Przejdź do głównej zawartości

API reference

Ta treść nie jest jeszcze dostępna w Twoim języku.

Overvis HMI has a typed tRPC API. The web app, local workspaces, the hmi CLI, and your own scripts and integrations all use it, so almost anything you can do in the app, a script can do with an API key. The procedure inventory is the authoritative list of supported procedures, with the input, output, access rule, and errors of each.

The web app authenticates with your signed-in session. Other clients send an API key in the x-api-key header. There are two kinds of key:

  • A personal API key acts as you: with your role in every workspace you belong to, and with your role in each billing account. It also reaches your account’s own data: billing accounts, notification settings, Helpdesk tickets, assistant chats, and the security overview of your sign-in methods and signed-in devices. You create one on Account settings → API keys.
  • A workspace API key reaches only one workspace, with your role there. The hmi pull command that Develop locally in Workspace settings gives you creates one for the local workspace. It cannot list or create workspaces or reach account data such as assistant chats, notification settings, or Helpdesk tickets. Of billing, it reads only its workspace’s billing status, and which billing account pays for it when you are an admin or owner of that workspace. Account settings → API keys lists your workspace API keys as Local workspace keys, and each workspace’s settings list them on the CLI keys tab.

Neither kind of key signs in to the web app, and a suspended account cannot use either. A key stops working when it is revoked.

A few things work only in the web app with a signed-in session: creating and revoking personal API keys, signing out devices, deleting the account, opening a device’s web interface, linking Telegram, and answering the assistant’s confirmation cards and questions. The procedures behind them are not in the inventory and may change without notice. Procedures in the inventory that accept only a signed-in session say so in their Access entry and refuse both kinds of key.

The API is tRPC over HTTP. Queries generally retrieve data and mutations perform actions, but live reads such as parameter.readValues and parameter.readValuesStream are also mutations. Subscriptions stream updates; the live-read stream is returned by a mutation. The generated PublicAppRouter declaration in @overvis/hmi-shared/app-router declares each procedure’s kind and return type for a compatible tRPC client.

Clients may name themselves and their version in the x-hmi-client header as <name>/<version>, for example cli/0.1.0. The header is optional; requests without it, or from a client name the API does not track, are accepted. When a tracked client is older than the API supports, every tRPC procedure and HTTP route answers PRECONDITION_FAILED (HTTP 412) with the code client_outdated and details: { client, minimum }, where minimum is the oldest version the API accepts.

A tRPC request body may be at most 4 MB. workspace.push accepts up to 32 MB. Procedures that carry encoded content (report render uploads, problem reports with a screenshot, assistant messages with images) or bulk rows (readings.insertExact) accept larger bodies up to the limits of their inputs. A larger body fails with PAYLOAD_TOO_LARGE (HTTP 413), the code request_body_too_large, and details: { limitBytes }. A push too large for one request is sent in parts of at most 4 MB each (see Large pushes).

Workspace procedures apply the caller’s membership role:

RoleTypical API access
View onlyRead setup metadata and live parameter values; no live writes
OperatorView-only access plus live writes, acknowledging and shelving alarms, quick reports, report comments and signatures, and report renders
EditorOperator access plus setup, dashboards, reports, files, and local sync
AdminEditor access plus members, invitations, workspace name, branding, limits, retention, stored-data deletion, and everyone’s workspace API keys
OwnerAdmin access plus deleting the workspace and moving its billing

The procedure-specific pages call out stricter requirements. The full capability matrix is in Workspace roles. The server checks every call, also for actions a UI hides.

  • Input schemas are closed: an argument name the procedure does not declare is rejected. Configuration maps accept keys according to their declared schema.
  • IDs are UUID strings. Input accepts them in any letter case. IDs the server assigns or reads from its records are lowercase; IDs inside stored configuration keep the case they were saved with.
  • Dates and times are ISO strings. Setup and dashboard-owned UI display them in the browser timezone as YYYY-MM-DD HH:mm:ss. Report widgets use the configured report timezone.
  • Localized text is either one string or an object whose keys are locale codes.
  • Procedures that search by name (parameter.searchByName, parameter.searchBranchesByName, endpoint.searchByName, dashboard.searchByName) take a query of 2–100 characters; a shorter one fails with input_invalid. They return at most 25 matches and set hasMore when there are more.
  • Optional fields omitted from an update keep their existing value unless the procedure says otherwise.

tRPC failures include a standard error category in data.code and a machine-readable code in message. Common categories are:

  • UNAUTHORIZED: no valid cookie session or API key (plain HTTP routes such as file, blob and image downloads send authentication_required);
  • FORBIDDEN: the caller is authenticated but lacks the required workspace role, the workspace is suspended (workspace_suspended, with details.reasons: admin, balance, or both), or the account is not admitted (admission_required) or has not finished onboarding in the web app (onboarding_required);
  • NOT_FOUND: the object does not exist or is outside the caller’s accessible workspace;
  • BAD_REQUEST: malformed input, a failed validation rule, or a conflicting object state;
  • CONFLICT: revision or uniqueness conflict that requires fresh data before retrying;
  • PRECONDITION_FAILED: the request needs something first, such as uploading missing blobs (push_blob_missing) or updating an outdated client (client_outdated).

data.code is the error category and the documented message code the domain-specific reason. Validation failures use message: "input_invalid" and include data.issues with path, keyword, and optional params. Domain failures may include data.details; when a failure concerns one input, data.details.field names its input path (for example source.formula). Each domain code is always sent with the same data.code: @overvis/hmi-shared/api-errors exports every public code with its status (publicApiErrorStatus, and per domain in publicApiErrorStatusTables) and the isPublicApiErrorCode guard. Unexpected failures use message: "internal_error"; stack traces are not part of the response.

The formatter also substitutes internal_error when an error has no public message code, including default tRPC messages such as UNAUTHORIZED and FORBIDDEN. This fallback preserves data.code; it does not mean that every such response is an internal server error. data.code identifies authentication and permission failures. message is a code, not text for display; the app translates domain codes and validation fields into UI messages.

Parameters, parameter branches, endpoints, dashboards, report presets, automations, alarm rules, and edge programs carry a revision that changes whenever the entity does. These procedures take an optional baseRevision, the revision the caller loaded, and refuse to replace a change made since then:

  • parameter.update, parameter.updateBranch, endpoint.update, dashboard.update, automation.update, alarmRule.update, edgeProgram.update, edgeProgram.updateSettings (with the endpoint’s revision), report.preset.update, and report.preset.saveTemporary (a stale save of a quick report fails instead of returning the report someone else saved);
  • endpoint.delete, endpoint.enable, endpoint.disable, dashboard.delete, dashboard.enable, dashboard.disable, automation.delete, automation.setDisabled, alarmRule.delete, alarmRule.setDisabled, edgeProgram.delete, and report.preset.delete;
  • parameter.deleteSelection, which takes paramBases and branchBases: the revision of each selected parameter and branch, by id. Entities deleted with a selected branch are not checked.

When the stored entity has another revision, the call changes nothing and fails with CONFLICT, the code setup_conflict, and details: { entityType, entityId, serverRevision }. A deleted entity fails with its not-found code (for example parameter_not_found), and so does parameter.deleteSelection when a selected entity is gone. A call without a base is not checked: the last write wins. Changes from every source move the revision, including CLI pushes, other users’ edits, and toggles, so enabling or disabling an entity also changes it.

workspace.watchChanges({ workspaceId }) is a subscription that tells a client when to refresh what it shows of one workspace. Every event carries the workspace’s whole current state as the caller sees it: setupRevision, name, disabled, suspensionReasons, and the caller’s access. setupRevision moves whenever setup changes: endpoints, parameters, dashboards and drawings, report presets and comments, automations, alarm rules, edge programs, files, or branding. Events carry the state, not a list of what changed.

The first event has type: "resync" and reports the state when the subscription starts. Later events have type: "changed", except after the server may have missed changes, when it sends another resync. A client that reconnects gets a new first event, so comparing states covers anything missed in between. The subscription accepts the same callers as workspace.get, including suspended workspaces and API keys scoped to that workspace. It ends when the caller loses access or the workspace is deleted. Live connection status of endpoints and device values have their own subscriptions and are not part of this feed.

Endpoint connection status comes from the live Modbus TCP connection, not from setup. Any workspace member can read it:

  • endpoint.status({ workspaceId }) returns every endpoint of the workspace with its status: state (connecting, connected, disconnected, or error), the active remote address, connectedAt, and lastError. status is null until the first status for that endpoint arrives.
  • endpoint.watchStatus({ workspaceId }) is a subscription: one snapshot event with the same list, then a change event whenever one endpoint’s status changes. A reconnecting subscriber gets a new snapshot.
  • endpoint.traffic({ endpointId, channels? }) is a subscription of live Modbus frames of one endpoint: tx (requests queued for sending), sent and rx (Modbus TCP ADUs as hex), and events (connection changes and request errors). There is no history; frames start when the subscription opens.

When live endpoint status is unavailable, these procedures fail with endpoint_live_unavailable.

A personal API key can list, create, read, rename (assistant.renameChat), and delete your chats, send messages (assistant.addToQueue), follow a run (assistant.watchChat, assistant.runtimeState), continue a run that stopped short, cancel a background job wait, and stop everything a chat runs (assistant.abortTurn). A run started this way acts as you while the key is valid and stops when you revoke the key. Workspace API keys cannot reach chats.

When the run waits for you, the API shows what it waits on (assistant.getPendingProposal, assistant.getPendingQuestion, assistant.getPendingGuard) but cannot answer it: confirmation cards, the assistant’s questions, and the repeated-step and spending-cap prompts are answered only in the app. The run stays paused until you answer there or stop it with assistant.abortTurn. A message sent while the assistant is replying is queued and goes out when the reply completes.

assistant.addToQueue takes a message id you generate (a UUID; a repeat with the same id returns what became of the first call), the chatId, stopGeneration, and the message itself.

  • identity is required on every chat command: { userId, sessionId }, with your own user id and sessionId: null. Any other value is refused with assistant_identity_changed. No procedure returns your user id directly: it is the userId of the owner in billing.members.list for the billing account that billing.accounts.list marks personal: true.
  • stopGeneration is the chat’s Stop count, as assistant.listChats and assistant.runtimeState return it. A value older than the chat’s last Stop stores the message as held, waiting for Send, which only the app offers. A held message is not sent through the API; a new message with the current count is.
  • message.level is the assistant level the run uses; the chat’s current one is level in assistant.listChats. message.content is the text.
  • message.context tells the assistant where the user is: pathname (an app path, or /), locale (the UI language code), and workspaceId, workspaceName, dashboardId, dashboardName, endpointId, endpointName, parameterId, and parameterName, each set to the item the message is about or null. timeZone (an IANA name) is optional. The server stamps now, and the input does not carry it. Messages queued together must name the same workspace.

workspace.push and workspace.pull never carry file bytes. They name each mirrored or built file by { sha256, size }, and the content travels as a content-addressed blob over plain HTTP with the same authentication:

  • PUT /api/workspaces/<workspaceId>/blobs/<sha256> stores the raw request body (any content type, at most 50 MB) for a later push to that workspace. The body must hash to <sha256>. Repeating an upload is harmless. Replies 204. Uploaded content stays available to pushes for 24 hours. Requires editor, admin or owner access.
  • GET /api/workspaces/<workspaceId>/blobs/<sha256> returns the raw bytes of content that one of the workspace’s files references. Requires editor, admin or owner access.

workspace.missingBlobs({ workspaceId, hashes }) takes up to 100 hashes and returns those the workspace cannot reference, which are the ones a push needs uploaded. Content the workspace already references needs no upload; content uploaded to another workspace does not count. A push that references content the workspace lacks fails with push_blob_missing; details.blobs lists the hashes it lacks. Blob errors are blob_invalid_hash, blob_hash_mismatch, blob_too_large (status 413), and blob_not_found, sent as { "message": "<code>" }.

A push whose changes do not fit one request can be staged in parts and applied as one push.

  • Parts. Each part is a push input of its own. Together the parts hold every change exactly once: each upsert, tombstone, file, and built file in one part, with its base in the same part.
  • workspace.pushStage takes one part with its part number, counted from 0, the pushId, and an attempt UUID that identifies one staging attempt. Staging a part again replaces it. Staging a part of another attempt discards the parts of earlier attempts of the push.
  • workspace.pushCommit({ workspaceId, pushId, attempt, parts }) applies that attempt’s parts as one push, with the same checks, conflicts, and result as workspace.push, then discards them. Parts of different attempts are never combined.
  • Limits. The parts of one push may hold at most 64 MB of JSON together (push_stage_too_large). A commit fails with push_stage_incomplete when a part is missing (details.parts lists them), and with push_stage_overlap when two parts hold the same change. A user stages one push per workspace at a time: staging a part of another push discards the earlier one. Parts not committed within a day are discarded.
  • Identity. A staged push has the same identity as the same push sent at once, however it is split. A push sent again under the same pushId, at once or staged in parts under a new attempt, returns the first result when the push was already applied.

The TypeScript router declaration is generated from the running server contract. The procedure inventory matches the @overvis/hmi-shared version of the same release.