Projects & source

Discover and read projects.

Enumerate your organization's projects, read one project's metadata, and pull its source in whichever representation you need.

List & get

GET /projects needs only a valid key (no extra scope) — even an ai_explain-only key can discover which projects exist. Results are filtered to the key's project scope and paginated with an opaque cursor.

bash
curl -s "https://app.plcs.ai/api/v1/projects?limit=50" \
  -H "Authorization: Bearer $PLCS_API_KEY"
python
page = client.list_projects(limit=50)
for p in page.projects:
    print(p.project_id, p.name, p.vendor)

detail = client.get_project(page.projects[0].project_id)
print(detail.analysis_status)  # queued | running | complete | error | None
csharp
var page = await client.ListProjectsAsync(limit: 50);
foreach (var p in page.Projects)
    Console.WriteLine($"{p.ProjectId} {p.Name} {p.Vendor}");

var detail = await client.GetProjectAsync(page.Projects[0].ProjectId);
Console.WriteLine(detail.AnalysisStatus);

Read source

GET /projects/{id}/source requires code_read and serves the project two ways — and nothing in between:

FormatReturns
parsed (default)The vendor-neutral parsed model as JSON (programs, routines, tags, dataTypes…), served from storage. Gzipped on the wire — advertise Accept-Encoding: gzip and you get it verbatim; the SDKs do.
rawThe original uploaded file (L5X XML, Siemens ZIP, or CODESYS .export) as a binary download.

There are no per-routine or aggregated-text reads. To ask a question about the project, use interpret rather than pulling the model and reasoning over it yourself — that is the capability the API is for.

No size ceiling. The parsed model is handed over from storage rather than assembled in the response, so a real project — a few MB into the tens of MB — comes back whole. Hold it deliberately rather than logging or echoing it.

python
src = client.get_source("prj_…")            # the parsed model
print(src.parsed["controller"]["name"])

raw_bytes = client.download_source("prj_…")   # original L5X / ZIP / .export bytes
csharp
var src = await client.GetSourceAsync("prj_…");
Console.WriteLine(src.Parsed);

byte[] rawBytes = await client.DownloadSourceAsync("prj_…");

Live HMI values

With hmi_view, GET /projects/{id}/hmi/values returns the latest tag snapshot pushed by a Desktop Companion App (DCA) session, and /hmi/history?tag=… returns recent history for one tag. These return data only while a DCA is serving the project; with no live session you get a normal 200 with live: false and an empty tags map (not an error).

To ask for tags the operator never started streaming, POST /projects/{id}/hmi/request-tags records the names for the DCA to read on its next poll and returns { project_id, live, requested }. It needs hmi_edit, and it is a request signal rather than a read — call /hmi/values a few seconds later for the values. The list is de-duped and not truncated, so a whole-project ask is one call.

Reads need no Idempotency-Key. Listing is scoped to the key's projects; a request for a project outside the scope returns 403 project_scope_forbidden.