Appearance
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
| Name | In | Type | Required | Default | Description | Validation / Notes | DB Mapping |
|---|---|---|---|---|---|---|---|
q | Query | String | No | — | Name contains (case-insensitive) or IČO equals. | Max 200 chars. | organisation.name, organisation.ico |
role | Query | Enum, repeatable | No | all | Subject has at least one of the listed roles. | owner, manager, delegatedManager, subscriber, self. | derived |
legalForm | Query | String, repeatable | No | all | Legal form equals one of the values; - matches an empty legal form. | Max 100 chars each. | organisation.legal_form |
state | Query | Enum | No | all live | ok, toResolve. | Live subjects only; toResolve = the subject has an open alarm or notification about its data (FR-12). | derived |
active | Query | true/false | No | true | Existing parameter; false lists deleted subjects (not used by the overview). | — | organisation.deleted_at |
sort | Query | String | No | name | name, buildingCount, supplyPointCount, gaugeCount, contactEnms, legalForm, ico; prefix - for descending. | Ties broken by id. | — |
page | Query | Integer | No | 1 | Page number. | ≥ 1. | — |
pageSize | Query | Integer | No | 20 | Page size. | 1–100. | — |
Request Logic
- One statement: the filtered, sorted page of
organisationjoined 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
| Field | Source |
|---|---|
roles | self 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 |
state | deleted when deleted_at is set; toResolve when an alarm or notification is open for the subject (FR-12); else ok |
contactEnms.name | User directory by contact_enms_user_id; null when unset |
❌ Error Responses
| Status | errorCode | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Unknown 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
| Name | In | Type | Required | Default | Description | Validation / Notes | DB Mapping |
|---|---|---|---|---|---|---|---|
q, role, legalForm, state, sort | Query | — | No | as list | Same meaning as the list; always live subjects. | — | — |
columns | Query | String, repeatable | No | user's visible columns | Columns 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.
rolesas 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
| Status | errorCode | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Unknown column key or filter value |
| 403 | FORBIDDEN_COLUMN | A 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.
| Name | In | Type | Required | Default | Description | Validation / Notes | DB Mapping |
|---|---|---|---|---|---|---|---|
subscriber | Query | UUID | No | — | 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.