Reference
API reference.
A stable, versioned HTTP API for interpreting and analyzing PLC projects.
The PLCs.ai API provides plain-language interpretation, troubleshooting, and analysis of Allen-Bradley and Siemens projects — behind a versioned, externally-authenticated HTTP surface.
Authentication. Every request carries an API key as a bearer token: Authorization: Bearer plck_live_…. A key resolves its organization from the credential itself — there is no organization in the URL. Mint, scope, and revoke keys from Settings → API Keys in the app.
Idempotency. Every write accepts (and requires) an Idempotency-Key header so a retry never creates a duplicate billable unit.
Errors. Every error uses one envelope with a human userMessage, a suggestedAction, and an isRetryable flag. Every response — success or error — carries a unique request-id header; quote it in support requests.
Permissions. A key carries a set of scopes. Each endpoint documents the scope it requires.
Download the OpenAPI 3.1 spec (YAML) — the same file the SDKs use.
Base URL
| Server | Description |
|---|---|
https://app.plcs.ai/api/v1 | Production |
Authentication
An API key minted from Settings → API Keys, sent as Authorization: Bearer plck_live_….
Authenticated health check
Confirms the API is reachable and your credential resolves an organization. Returns the resolved organizationId and actorType.
Responses
| Status | Description |
|---|---|
| 200 | The credential resolved an organization. |
| 401 | No valid credential, or the key was revoked. |
Response · 200
| Field | Type | Description |
|---|---|---|
status required | const "ok" | |
organizationId required | string | — |
actorType required | string enum |
{
"status": "ok",
"organizationId": "string",
"actorType": "api_key"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Interpret a prompt against a project
The headline capability: a prompt in, a cited interpretation out, over the existing assistant. Two response modes off one endpoint:
mode: "sync"(default) — a blocking JSON body{ answer, citations, usage }.mode: "stream"— a Server-Sent Events stream (status/token/citations/done/error). See the Streaming guide.
Requires the ai_explain permission.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Request body required
| Field | Type | Description |
|---|---|---|
prompt required | string | The question to ask about the project. |
mode | string enum | sync returns a blocking JSON body. stream returns an SSE stream. |
include_citations | boolean | Whether to include source citations in the response. |
{
"prompt": "What conditions must be true for the main conveyor to start?",
"mode": "sync",
"include_citations": true
}Responses
| Status | Description |
|---|---|
| 200 | For mode: "sync", the cited interpretation. For mode: "stream", an text/event-stream of SSE events (see the Streaming guide). |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
answer required | string | — |
citations required | array<string> | Source identifiers backing the answer. |
usage required | object | Token counts for this call. Informational only — not a bill. |
usage.input_tokens | integer | — |
usage.output_tokens | integer | — |
{
"answer": "string",
"citations": [
"string"
],
"usage": {
"input_tokens": 0,
"output_tokens": 0
}
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Generate or modify PLC code (proposal)
Ask the assistant to PRODUCE or MODIFY PLC logic and get back a reviewable proposal. Proposes, never deploys: the response is generated routine(s) / SCL text with inline insertion directives — it creates NO version. To persist a proposal, commit it as a new version with POST /projects/{id}/versions (requires the separate code_write scope). Requires the ai_generate permission.
Two response modes off one endpoint:
mode: "sync"(default) — a blocking JSON body{ generated_code, code_blocks, citations, usage }.mode: "stream"— a Server-Sent Events stream (status/token/citations/done/error). See the Streaming guide.
generated_code is the assistant's full markdown response (prose + fenced `ladder / `st / `scl blocks + inline directives such as INSERT_RUNG_AFTER / MARK_DELETE); code_blocks is those fenced blocks lifted out for programmatic use.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Request body required
| Field | Type | Description |
|---|---|---|
prompt required | string | What to generate or change (e.g. "Add a 5-second start-up delay timer"). |
target | object | Optional hint for which program/routine to target. Folded into the prompt as guidance; the assistant decides the final placement. |
target.program | string | — |
target.routine | string | — |
mode | string enum | sync returns a blocking JSON body. stream returns an SSE stream. |
include_citations | boolean | Whether to include source citations in the response. |
{
"prompt": "Add a 5-second start-up delay timer before the conveyor enables.",
"target": {
"program": "MainProgram",
"routine": "ConveyorControl"
},
"mode": "sync",
"include_citations": true
}Responses
| Status | Description |
|---|---|
| 200 | For mode: "sync", the generated proposal. For mode: "stream", a text/event-stream of SSE events (see the Streaming guide). |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 402 | The organization reached its self-set API spend limit for the period and chose to pause. An owner/admin can raise the limit or enable overage billing in Settings → Spending Controls. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
generated_code required | string | The assistant's full proposal: prose + fenced code blocks + inline insertion directives. The same text the in-app editor parses. This is a PROPOSAL — it creates no version. |
code_blocks required | array<object> | Fenced code blocks lifted from generated_code for programmatic use. |
citations required | array<string> | Source identifiers backing the proposal. |
usage required | object | Token counts for this call. Informational only — not a bill. |
usage.input_tokens | integer | — |
usage.output_tokens | integer | — |
{
"generated_code": "string",
"code_blocks": [
{
"language": "string",
"content": "string"
}
],
"citations": [
"string"
],
"usage": {
"input_tokens": 0,
"output_tokens": 0
}
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Create a troubleshooting thread
Open a stateful multi-turn thread scoped to a project. Append turns with POST /conversations/{cid}/messages. Requires ai_explain.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Request body
| Field | Type | Description |
|---|---|---|
name | string | Optional human label for the thread. |
{
"name": "string"
}Responses
| Status | Description |
|---|---|
| 201 | The created thread. |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 201
| Field | Type | Description |
|---|---|---|
conversationId required | string | — |
projectId required | string | — |
{
"conversationId": "string",
"projectId": "string"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Append a turn to a thread
Run one interpret turn inside an existing thread. The thread's prior history is loaded automatically. Requires ai_explain.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
cid required | path | string | The conversation (thread) id. |
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Request body required
| Field | Type | Description |
|---|---|---|
prompt required | string | — |
include_citations | boolean |
{
"prompt": "string",
"include_citations": true
}Responses
| Status | Description |
|---|---|
| 200 | The assistant's answer for this turn. |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
conversationId required | string | — |
answer required | string | — |
citations required | array<string> | — |
usage required | object | Token counts for this call. Informational only — not a bill. |
usage.input_tokens | integer | — |
usage.output_tokens | integer | — |
{
"conversationId": "string",
"answer": "string",
"citations": [
"string"
],
"usage": {
"input_tokens": 0,
"output_tokens": 0
}
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Start an async analysis job
Kick off an analysis run (dead code, missing handshakes, cycle-time) and return a job id immediately. Poll GET /analyses/{analysisId} for status and results. Requires analysis_tab.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Responses
| Status | Description |
|---|---|
| 202 | The analysis job was started (or joined an in-flight run). |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 202
| Field | Type | Description |
|---|---|---|
analysisId required | string | — |
status required | string enum |
{
"analysisId": "string",
"status": "queued"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Poll an analysis job
Return the current status of an analysis job. While running, the response carries a status of queued/running plus a message with poll guidance — never an empty 200. When complete, results is populated; when error, errors is populated. Requires analysis_tab.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
analysisId required | path | string | The analysis job id returned by startAnalysis. |
Responses
| Status | Description |
|---|---|
| 200 | The analysis job status (and results when complete). |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
analysisId required | string | — |
status required | string enum | |
message | string | Present while queued/running — poll-interval guidance. |
results | object | Present when status is complete. |
errors | object | Present when status is error. |
{
"analysisId": "string",
"status": "queued",
"message": "string",
"results": null,
"errors": null
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Export the project to its vendor PLC file
Start an async export of the project's current committed version to its native vendor format (Rockwell .L5X or a Siemens TIA .zip). Returns a job id immediately; poll GET /exports/{exportId} for status, then GET the download_url when complete. Exports the current version as-is (no edits). Requires export_plc.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Responses
| Status | Description |
|---|---|
| 202 | The export job was started. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 202
| Field | Type | Description |
|---|---|---|
exportId required | string | — |
status required | string enum |
{
"exportId": "string",
"status": "pending"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Render a PDF report for the project
Start an async render of a PDF documentation report for the project's current committed version. The report is assembled server-side from the parsed structure + analysis results (a "report-only" PDF; it does not include in-app chat history). Returns a job id immediately; poll GET /exports/{exportId} then GET the download_url. Requires export_pdf.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Responses
| Status | Description |
|---|---|
| 202 | The export job was started. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 202
| Field | Type | Description |
|---|---|---|
exportId required | string | — |
status required | string enum |
{
"exportId": "string",
"status": "pending"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Poll an export job
Return the current status of an export job. While pending/running the response carries a message with poll guidance. When complete, download_url (a relative /exports/{id}/download path) and filename are populated; when error, error carries a short reason. Gated on the export's matching permission (export_plc / export_pdf).
Parameters
| Name | In | Type | Description |
|---|---|---|---|
exportId required | path | string | The export job id returned by startPlcExport / startPdfExport. |
Responses
| Status | Description |
|---|---|
| 200 | The export job status (and download_url when complete). |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
exportId required | string | — |
kind required | string enum | |
status required | string enum | |
download_url | string | Present only when status is complete. A relative path. |
filename | string | Suggested download filename (present when complete). |
error | string | Short failure reason (present when status is error). |
message | string | Poll guidance (present while pending/running). |
{
"exportId": "string",
"kind": "plc",
"status": "pending",
"download_url": "string",
"filename": "string",
"error": "string",
"message": "string"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Download a completed export artifact
Stream the finished artifact (L5X / Siemens ZIP / PDF) for a complete export job, with the right Content-Type and a Content-Disposition filename. Gated on the export's matching permission and scope.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
exportId required | path | string | The export job id returned by startPlcExport / startPdfExport. |
Responses
| Status | Description |
|---|---|
| 200 | The export artifact bytes. |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 429 | Per-key rate limit exceeded. |
Every 4xx/5xx uses the standard error envelope and carries a request-id header.
List projects
Enumerate the organization's active projects, most-recently-touched first. Requires only a valid key (no extra permission) — an ai_explain-only operator key still needs to discover which projects exist. Results are filtered to the key's project scope. Returns identity-level metadata only; read source via GET /projects/{id}/source.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
team_id | query | string | Restrict to a single team. |
limit | query | integer | Page size (default 50, max 200). |
cursor | query | string | Opaque cursor from a previous response's next_cursor. |
Responses
| Status | Description |
|---|---|
| 200 | A page of projects. |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
projects required | array<object> | — |
next_cursor required | string | Pass as ?cursor= to fetch the next page; null on the last page. |
{
"projects": [
{
"project_id": "string",
"identity_id": "string",
"name": "string",
"vendor": "allen-bradley",
"file_type": "string",
"current_version_id": "string",
"updated_at": "2026-05-31T18:15:00.000Z"
}
],
"next_cursor": "string"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Ingest an L5X / Siemens export
Upload an L5X (Rockwell) or Siemens TIA ZIP export to create or update a project. Billed identically to a UI upload.
Files up to ~4.5 MB may be sent inline as base64 in file.inline. Larger files use the large-file flow: upload to the returned blob URL and pass file.blob_url. Requires project_upload.
A project-scoped key may only add a version to an in-scope identity; it can never create a new identity.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Request body required
| Field | Type | Description |
|---|---|---|
originalFilename required | string | The export file name, e.g. "Conveyor.L5X" or "TIA_Export.zip". |
name | string | Optional display name for the project. |
acknowledge_billing | boolean | Acknowledges the per-project charge for an enterprise per-project organization. Only consulted when this upload would create a NEW billable project; ignored for every other org type and for add-version / identical-file. When a new billable project is created without this set to true, the request is rejected with 402 billing_acknowledgement_required (carrying the projected cost); resend with true to confirm. Ignored on POST /projects/{id}/versions. |
file required | object | Exactly one of inline or blob_url must be set. |
file.inline | string | Base64-encoded file bytes (for files up to ~4.5 MB). |
file.blob_url | string (uri) | A blob URL for the large-file flow. |
{
"originalFilename": "string",
"name": "string",
"acknowledge_billing": true,
"file": {
"inline": "string",
"blob_url": "https://…"
}
}Responses
| Status | Description |
|---|---|
| 201 | The resolved project identity and version. |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 402 | An enterprise per-project organization tried to create a NEW billable project over the API. The error code is one of: - billing_setup_required — billing isn't set up yet (e.g. their first project); a charge can't be approved with no payment configured. Set up billing in the PLCs desktop app first, then retry. - billing_acknowledgement_required — billing is set up but the charge wasn't acknowledged. Disclose the cost from the billing block, then resend with acknowledge_billing: true. Only ever returned for enterprise per-project orgs on a new-project create — never for other org types, add-version, or identical-file. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 413 | The inline body exceeded the limit; use the large-file flow. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 201
| Field | Type | Description |
|---|---|---|
identity_id required | string | — |
display_name | string | — |
project_id required | string | — |
version_id | string | — |
version_number | integer | — |
resolution required | string | How the identity was resolved (e.g. created / existing / identical_file). |
vendor required | string enum |
{
"identity_id": "string",
"display_name": "string",
"project_id": "string",
"version_id": "string",
"version_number": 0,
"resolution": "string",
"vendor": "allen-bradley"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Get a project
Identity + current-version metadata for one project, plus an analysis_status summary (not the full results blob). Requires only a valid key (no extra permission); scoped to the key's project scope. Unknown or out-of-scope id → 404.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
Responses
| Status | Description |
|---|---|
| 200 | The project metadata. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
project_id required | string | — |
identity_id required | string | — |
name required | string | — |
display_name | string | — |
vendor | string | — |
file_type | string | — |
file_size_bytes | integer | — |
original_filename | string | — |
current_version_id required | string | — |
version_number | integer | — |
analysis_status | string enum | Summary only — poll GET /analyses/{id} for full results. null = never analyzed. |
created_at required | string (date-time) | — |
updated_at required | string (date-time) | — |
{
"project_id": "string",
"identity_id": "string",
"name": "string",
"display_name": "string",
"vendor": "string",
"file_type": "string",
"file_size_bytes": 0,
"original_filename": "string",
"current_version_id": "string",
"version_number": 0,
"analysis_status": "queued",
"created_at": "2026-05-31T18:15:00.000Z",
"updated_at": "2026-05-31T18:15:00.000Z"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Commit a new version (Save project)
Save a new version of an existing project from an updated vendor file — the headless equivalent of the platform's "Save project". The new version becomes the project's current version (billed identically to a UI save). Requires code_write.
The uploaded file must be the same vendor as the project (an L5X stays L5X, a Siemens ZIP stays Siemens) — a mismatch returns 400. Re-sending the exact bytes of the current version is a no-op: it returns 200 with resolution: identical_file and no new version.
Files up to ~4.5 MB may be sent inline as base64 in file.inline; larger files use the large-file flow via file.blob_url. A project-scoped key may only commit to in-scope projects.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Request body required
| Field | Type | Description |
|---|---|---|
originalFilename required | string | The export file name, e.g. "Conveyor.L5X" or "TIA_Export.zip". |
name | string | Optional display name for the project. |
acknowledge_billing | boolean | Acknowledges the per-project charge for an enterprise per-project organization. Only consulted when this upload would create a NEW billable project; ignored for every other org type and for add-version / identical-file. When a new billable project is created without this set to true, the request is rejected with 402 billing_acknowledgement_required (carrying the projected cost); resend with true to confirm. Ignored on POST /projects/{id}/versions. |
file required | object | Exactly one of inline or blob_url must be set. |
file.inline | string | Base64-encoded file bytes (for files up to ~4.5 MB). |
file.blob_url | string (uri) | A blob URL for the large-file flow. |
{
"originalFilename": "string",
"name": "string",
"acknowledge_billing": true,
"file": {
"inline": "string",
"blob_url": "https://…"
}
}Responses
| Status | Description |
|---|---|
| 200 | The uploaded bytes matched the current version; no new version was created. |
| 201 | A new version was committed. |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 413 | The inline body exceeded the limit; use the large-file flow. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
project_id required | string | — |
identity_id required | string | — |
version_id | string | The new version's id; null when resolution is identical_file. |
version_number required | integer | — |
resolution required | string enum | add_version when a new version was committed; identical_file when the uploaded bytes matched the current version (no-op). |
vendor required | string enum |
{
"project_id": "string",
"identity_id": "string",
"version_id": "string",
"version_number": 0,
"resolution": "add_version",
"vendor": "allen-bradley"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Read a project's source
Return the project's source in one of several representations. Requires code_read.
format=parsed(default): the vendor-neutral parsed structure as JSON. Projects whose parsed JSON exceeds the 4.5 MB inline ceiling return413; useformat=rawfor those.format=raw: the original uploaded file (L5X XML or Siemens ZIP), streamed or 302-redirected as a binary download.format=scl: the aggregated Structured-Text view ({ scl }).format=ladder: the aggregated ladder view ({ ladder }).
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
format | query | string enum | — |
Responses
| Status | Description |
|---|---|
| 200 | The requested representation. JSON for parsed/scl/ladder; a binary stream (or 302 redirect) for raw. |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 413 | The parsed structure is too large to return inline; use format=raw. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
format required | string enum | |
vendor required | string enum | |
parsed | object | Present when format=parsed — the vendor-neutral parsed structure. |
scl | string | Present when format=scl — aggregated Structured Text. |
ladder | array<object> | Present when format=ladder — aggregated ladder rungs. |
{
"format": "parsed",
"vendor": "allen-bradley",
"parsed": null,
"scl": "string",
"ladder": [
{}
]
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Latest live tag values
The most recent tag snapshot pushed by a Desktop Companion App (DCA) session for this project. Requires hmi_view. Returns data only while a DCA is actively streaming; with no live session, live is false and tags is empty (this is a normal 200, not an error).
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
Responses
| Status | Description |
|---|---|
| 200 | The latest snapshot, or an empty live=false body. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
project_id required | string | — |
live required | boolean | True when a DCA session is actively streaming this project. |
message | string | Present when live=false — explains why there are no values. |
device_id | string | — |
timestamp | string (date-time) | — |
controller | object | — |
controller.name | string | — |
controller.type | string | — |
controller.status | string | — |
controller.ipAddress | string | — |
sequence | integer | — |
tags required | map<string, object> | Map of tag name → latest value. |
{
"project_id": "string",
"live": true,
"message": "string",
"device_id": "string",
"timestamp": "2026-05-31T18:15:00.000Z",
"controller": {
"name": "string",
"type": "string",
"status": "string",
"ipAddress": "string"
},
"sequence": 0,
"tags": {
"example_permission": null
}
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Recent value history for a tag
The most-recent-first value history a DCA session pushed for one tag. Requires hmi_view and a tag query parameter. Empty when no live session has streamed that tag.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The project id. |
tag required | query | string | The tag name to fetch history for. |
limit | query | integer | Max entries (default 50, max 600). |
Responses
| Status | Description |
|---|---|
| 200 | The tag's recent history. |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 429 | Per-key rate limit exceeded. |
Response · 200
| Field | Type | Description |
|---|---|---|
project_id required | string | — |
tag required | string | — |
count required | integer | — |
history required | array<object> | Most-recent-first history entries. |
{
"project_id": "string",
"tag": "string",
"count": 0,
"history": [
{
"value": null,
"timestamp": "2026-05-31T18:15:00.000Z",
"sequence": 0
}
]
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.
Mint a read-only embed token
Mint a short-lived, project-scoped token for the embeddable read-only assistant iframe. Regardless of the minting key's scope, the token is intersected down to read-only (ai_explain + hmi_view) — a browser-delivered token can never carry a write scope. The minting key must itself have at least ai_explain.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
Idempotency-Key required | header | string | A unique key (e.g. a UUID) for this write. Retrying with the same key and body replays the original result without double-billing. |
Request body required
| Field | Type | Description |
|---|---|---|
project_id required | string | The project the embed token may access. |
{
"project_id": "string"
}Responses
| Status | Description |
|---|---|
| 201 | The minted read-only embed token. |
| 400 | The request was malformed. |
| 401 | No valid credential, or the key was revoked. |
| 403 | The key lacks the permission (or project scope) this endpoint needs. |
| 404 | The resource was not found, or is not visible to this key's org. |
| 409 | A request with this Idempotency-Key is still being processed. |
| 422 | This Idempotency-Key was already used with a different body. |
| 429 | Per-key rate limit exceeded. |
Response · 201
| Field | Type | Description |
|---|---|---|
token required | string | The signed, read-only embed token to hand to a browser. |
expires_at required | string (date-time) | — |
permissions required | map<string, boolean> | The intersected read-only permissions (only ai_explain / hmi_view). |
project_id required | string | — |
{
"token": "string",
"expires_at": "2026-05-31T18:15:00.000Z",
"permissions": {
"example_permission": true
},
"project_id": "string"
}Every 4xx/5xx uses the standard error envelope and carries a request-id header.