Skip to content
Updated Sep 17, 2026 by Pablo Coufal · Owner: analysisactiveapi-a Edit on GitHub

Organisation & Distribution Registry — API Analysis ​

The subject registry is served by the tenant application under v1/organisations. Reads use tenant.organisations.read, writes tenant.organisations.write, deletion tenant.organisations.delete. This page documents the endpoints the Subjekty tab uses; POST /v1/organisations, GET /v1/organisations/:id, DELETE /v1/organisations/:id and GET /v1/organisations/autocomplete keep their current contract apart from the email field noted below.

Paginated success envelope:

json
{
  "data": [],
  "meta": { "total": 120, "page": 1, "pageSize": 20 }
}

Error envelope (flat):

json
{
  "errorCode": "ORGANISATION_ICO_CONFLICT",
  "errorMessage": "A live organisation with this IČO already exists.",
  "status": 409,
  "requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b21",
  "details": { "existingId": "018ed0b3-c298-7c7a-96d5-8b36f5a7f8d2" }
}

Authentication and permission failures are answered by the tenant application's shared handling.


📥 GET /v1/organisations ​

The subject overview: one page of subjects with their derived roles and link counts.

Authorization ​

Requires permission code tenant.organisations.read, tenant-scoped.

Request Headers ​

None beyond the tenant application's standard bearer authentication and tenant context.

Request Body ​

None.

Request Parameters ​

NameInTypeRequiredDefaultDescriptionValidation / NotesDB Mapping
qQueryStringNo—Name contains (case-insensitive) or IČO equals.Max 200 chars.organisation.name, organisation.ico
roleQueryEnum, repeatableNoallSubject has at least one of the listed roles.owner, manager, delegatedManager, subscriber, self.derived
legalFormQueryString, repeatableNoallLegal form equals one of the values; - matches an empty legal form.Max 100 chars each.organisation.legal_form
stateQueryEnumNoall liveok, toResolve.Live subjects only; toResolve = the subject has an open alarm or notification about its data (FR-12).derived
activeQuerytrue/falseNotrueExisting parameter; false lists deleted subjects (not used by the overview).—organisation.deleted_at
sortQueryStringNonamename, buildingCount, supplyPointCount, gaugeCount, contactEnms, legalForm, ico; prefix - for descending.Ties broken by id.—
pageQueryIntegerNo1Page number.≥ 1.—
pageSizeQueryIntegerNo20Page size.1–100.—

Request Logic ​

  • One statement: the filtered, sorted page of organisation joined to grouped reference counts.
  • Only live references count (building.deleted_at IS NULL, gauge.deleted_at IS NULL).
sql
WITH b AS (
  SELECT org_id, role, COUNT(*) AS n FROM (
    SELECT owner_id AS org_id, 'owner' AS role FROM building WHERE deleted_at IS NULL AND owner_id IS NOT NULL
    UNION ALL SELECT manager_id, 'manager' FROM building WHERE deleted_at IS NULL AND manager_id IS NOT NULL
    UNION ALL SELECT delegated_manager_id, 'delegatedManager' FROM building WHERE deleted_at IS NULL AND delegated_manager_id IS NOT NULL
  ) r GROUP BY org_id, role
), g AS (
  SELECT subscriber_id AS org_id, COUNT(*) AS gauges, COUNT(DISTINCT supply_point_id) AS supply_points
  FROM gauge WHERE deleted_at IS NULL AND subscriber_id IS NOT NULL GROUP BY subscriber_id
)
SELECT o.id, o.name, o.ico, o.legal_form, o.is_self, o.deleted_at, o.contact_enms_user_id, …,
       COALESCE(owned.n, 0), COALESCE(managed.n, 0), COALESCE(delegated.n, 0),
       COALESCE(g.gauges, 0), COALESCE(g.supply_points, 0)
FROM organisation o
LEFT JOIN b owned ON owned.org_id = o.id AND owned.role = 'owner'
LEFT JOIN b managed ON managed.org_id = o.id AND managed.role = 'manager'
LEFT JOIN b delegated ON delegated.org_id = o.id AND delegated.role = 'delegatedManager'
LEFT JOIN g ON g.org_id = o.id
WHERE … -- q, role, legalForm, state
ORDER BY … , o.id
LIMIT @pageSize OFFSET (@page - 1) * @pageSize;

Transactional Operations ​

N/A — read-only.

✅ Success Response (200) ​

json
{
  "data": [
    {
      "id": "018ed0b3-c298-7c7a-96d5-8b36f5a7f8d2",
      "name": "Technické služby Polná, s.r.o.",
      "ico": "47468386",
      "dic": "CZ47468386",
      "legalForm": "společnost s ručením omezeným",
      "isSelf": false,
      "registeredCity": "Polná",
      "pxeIdentifier": "POLNA-TS-01",
      "dataBoxId": "7h3m2pa",
      "email": "info@ts-polna.cz",
      "state": "ok",
      "roles": ["manager", "subscriber"],
      "counts": { "owned": 0, "managed": 6, "delegated": 0, "gauges": 28, "supplyPoints": 12 },
      "contactEnms": { "userId": "018fa51f-fda1-79f4-8461-2cb8f1cabc10", "name": "Orel Rádce" }
    }
  ],
  "meta": { "total": 10, "page": 1, "pageSize": 20 }
}

Response Data Mapping ​

FieldSource
rolesself when is_self; each role with a count > 0, in the order self, owner, delegatedManager, manager, subscriber
counts.*Grouped counts above; the overview's spr. figure is managed + delegated
statedeleted when deleted_at is set; toResolve when an alarm or notification is open for the subject (FR-12); else ok
contactEnms.nameUser directory by contact_enms_user_id; null when unset

❌ Error Responses ​

StatuserrorCodeWhen
400VALIDATION_ERRORUnknown role, state or sort value; page size out of range

📥 GET /v1/organisations/export ​

XLSX export of the whole overview: all subjects matching the current search, tab and filters, without paging. There is no row selection and no export from the detail.

Authorization ​

tenant.organisations.read. Columns billing*, bankAccount, invoice* and contact* additionally require tenant.organisations.write.

Request Headers ​

Accept: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.

Request Body ​

None.

Request Parameters ​

NameInTypeRequiredDefaultDescriptionValidation / NotesDB Mapping
q, role, legalForm, state, sortQuery—Noas listSame meaning as the list; always live subjects.——
columnsQueryString, repeatableNouser's visible columnsColumns and their order.Column keys of the organisations table catalogue; unknown keys rejected; columns the caller may not see rejected with 403.—

Request Logic ​

  • Same query as the list without paging.
  • First sheet rows: header row with the Czech column labels; below the table, the export time and the applied filters.
  • roles as one cell, labels joined by ; .
  • File name <client>_Subjekty_<YYYY-MM-DD>.xlsx.

Transactional Operations ​

N/A — read-only.

✅ Success Response (200) ​

Binary XLSX, Content-Disposition: attachment.

❌ Error Responses ​

StatuserrorCodeWhen
400VALIDATION_ERRORUnknown column key or filter value
403FORBIDDEN_COLUMNA requested column needs tenant.organisations.write

📥 GET /v1/organisations/:id/references ​

Existing endpoint: the subject's live references, paginated. Unchanged; used by the detail's Provoz section and the blocked-deletion dialog.

Response items: role, entityType (building | gauge), entityId, entityName.


📥 GET /v1/gauges — subscriber filter ​

Existing endpoint of Gauge Management; the Odběrná místa drill-down needs one more query parameter.

NameInTypeRequiredDefaultDescriptionValidation / NotesDB Mapping
subscriberQueryUUIDNo—Only gauges whose subscriber is this subject.Must be a subject of the tenant; unknown id returns an empty page. Combined with a new hasSupplyPoint=true flag to list supply points only.gauge.subscriber_id, gauge.supply_point_id IS NOT NULL

✏️ POST /v1/organisations and PATCH /v1/organisations/:id — email ​

Both accept email: string, nullable, optional, max 254 chars, e-mail format; mapped to organisation.email. GET /v1/organisations/:id returns it. Other fields and errors are unchanged, including ORGANISATION_ICO_CONFLICT (409) for an IČO held by another live subject — its details.existingId carries the holder's id so the UI can link it — SELF_ORGANISATION_PROTECTED (409) and ORGANISATION_REFERENCED (409) on delete.