Skip to content
Updated Aug 28, 2026 by barcadvorakova-starkys · Owner: analysisactiveconceptgenericapi-a Edit on GitHub

Scope Administration — API Analysis (API-A) ​

Concept layer — frozen. The Multi-Organizations / tenant scoping generic. Nothing here is written by a normal playbook run; a project's own feature analysis is the live document and takes every edit. This layer names no project and links to none — the dependency runs one way, from an application to its concept.

Concept: Multi-Organizations / Tenant Scoping · Surface: Scope Administration

Paths below are written without any version or gateway prefix: how a project versions and mounts the routes is an application decision, not part of the concept. Success envelope: { data, status, message, requestId }.

These are the only endpoints the concept defines, because scoping is otherwise a precondition on every other endpoint rather than an endpoint of its own. The scope entity is one of the few things in the system that is addressed by its identifier from outside its own scope — which is why every one of these routes needs an explicit administrative permission rather than the ambient scope guard.

Endpoint index: the git host renders an outline from the ## headings below — there is no hand-maintained endpoint list to fall out of date.


1. List scopes ​

Endpoint: GET /admin/scopes?textQuery=$textQuery&active=$active&page=$page&pageSize=$pageSize

Description ​

Search and page the scopes the caller may administer, each with its live child collections.

Authorization ​

An administrative read permission over scopes. This read deliberately crosses scopes, so it is authorised by permission alone — there is no scope guard to fall back on.

Request Headers ​

text
Authorization: Bearer <access_token>
Content-Type: application/json

Query Parameters ​

ParameterTypeRequiredDescription
textQueryStringNoFree-text search across the scope's name and business identifiers
activeBooleanNoFilter by the active flag
pageNumberNoPage number
pageSizeNumberNoItems per page

Request Logic ​

Returns each scope together with its live (non-soft-deleted) child records, loaded for the whole page in one batched query rather than per row. A per-row load here is the classic N+1: invisible with ten scopes, and the reason the administration screen is slow with two hundred.

Success Response (200 OK) ​

json
{
  "data": {
    "scopes": [
      {
        "id": "018fa51f-fda1-79f4-8461-2cb8f1cabc10",
        "name": "Example Group",
        "logoBase64": null,
        "active": true,
        "children": [
          {
            "id": "018fa520-1c33-7a90-9d21-9f0b2a44e8c1",
            "label": "Primary",
            "active": true,
            "createdAt": "2026-05-12T14:34:00Z",
            "updatedAt": "2026-06-01T09:20:00Z"
          }
        ],
        "createdAt": "2026-05-12T14:34:00Z",
        "updatedAt": "2026-06-01T09:20:00Z",
        "createdBy": "user:c2d3f586-1c9a-4f5f-b9ae-45f2c4f69f7e",
        "updatedBy": "user:c2d3f586-1c9a-4f5f-b9ae-45f2c4f69f7e"
      }
    ],
    "pagination": { "page": 1, "pageSize": 20, "totalPages": 14 }
  },
  "status": 200,
  "message": "Scopes loaded successfully.",
  "requestId": "a8123fa1-2045-42b4-83fc-97dafe11f4a5"
}

children is a placeholder. A project names its scope-owned collections and their fields — the concept only fixes that such a collection is returned with its parent, and how it is written back (endpoint 3).

Error Responses ​

StatusCondition
403Caller lacks the administrative read permission

2. Create a scope ​

Endpoint: POST /admin/scopes

Description ​

Create a scope. Takes the same body as the update below; a supplied child collection becomes the scope's initial set.

Authorization ​

An administrative create permission over scopes — distinct from the update permission. Creating a scope is a platform capability; editing one is not necessarily.

Success Response (201 Created) ​

The created scope, in the same shape as the update response.

Error Responses ​

StatusCondition
400Invalid scope field, or an invalid or duplicated child item
403Caller lacks the permission
409A uniqueness constraint on the scope's identifiers is violated

3. Update a scope ​

Endpoint: PUT /admin/scopes/{id}

Description ​

Update the scope's own fields and reconcile its child collections.

Authorization ​

An administrative update permission over scopes. A scope's own administrators reach the same reconciliation through their scope-bound settings endpoint, with a permission that does not let them address another scope's identifier.

Request Headers ​

text
Authorization: Bearer <access_token>
Content-Type: application/json

Request Body ​

json
{
  "name": "Example Group",
  "logoBase64": null,
  "active": true,
  "children": [
    { "id": "018fa520-1c33-7a90-9d21-9f0b2a44e8c1", "label": "Primary", "active": true },
    { "label": "Secondary" }
  ]
}

Request Parameters ​

NameInTypeRequiredDescription
idPathUUIDYesScope to update

Request → entity mapping ​

FieldBehaviour
name, logoBase64, active and the project's own scope fieldsWritten directly; validated per field
childrenReplace-set. Omit the field to leave the collection unchanged; send an array — including an empty one — to replace the whole set. Items with a known id are updated, items without an id are inserted, and existing items absent from the list are soft-deleted
createdBy / updatedByThe acting administrator from the authenticated context, never a constant

Omitted and empty must not mean the same thing. Omitted means leave it alone; [] means remove everything. Collapsing them makes every partial save from a client that does not know about the collection wipe it. This is the single most consequential line in this document, and it is worth an explicit test in both directions.

Request Logic ​

Validate the scope fields and every child item before writing anything, then update the scope and reconcile the collection in one transaction. Validation is all-or-nothing: a request with one invalid child persists no part of itself, including the valid parent fields. A partially applied save on a screen with a single save button is indistinguishable from data corruption to the person using it.

Transactional Operations ​

The scope update, the child reconciliation and the audit entry commit together. If the audit write fails, the change rolls back with it.

Success Response (200 OK) ​

json
{
  "data": {
    "id": "018fa51f-fda1-79f4-8461-2cb8f1cabc10",
    "name": "Example Group",
    "logoBase64": null,
    "active": true,
    "children": [
      {
        "id": "018fa520-1c33-7a90-9d21-9f0b2a44e8c1",
        "label": "Primary",
        "active": true,
        "createdAt": "2026-05-12T14:34:00Z",
        "updatedAt": "2026-06-01T09:20:00Z"
      },
      {
        "id": "018fa520-7a01-7e22-9f4b-2c1d77ce0a31",
        "label": "Secondary",
        "active": true,
        "createdAt": "2026-06-01T09:20:00Z",
        "updatedAt": "2026-06-01T09:20:00Z"
      }
    ],
    "createdAt": "2026-05-12T14:34:00Z",
    "updatedAt": "2026-06-01T09:20:00Z",
    "createdBy": "user:c2d3f586-1c9a-4f5f-b9ae-45f2c4f69f7e",
    "updatedBy": "user:c2d3f586-1c9a-4f5f-b9ae-45f2c4f69f7e"
  },
  "status": 200,
  "message": "Updated the scope.",
  "requestId": "3ab34d88-65d1-4c10-897a-237c9a5b116f"
}

The response returns the re-read state, including identifiers assigned to newly inserted children, so the client can save again without first reloading.

Error Responses ​

StatusCondition
400An invalid scope field
400An invalid child item — one error code per validation rule, so the interface can point at the field
400Two child items in the same submission resolve to the same natural key
403Caller lacks the permission
404Scope does not exist or is already deleted
json
{
  "errorCode": "ERR_SCOPE_CHILD_DUPLICATE",
  "errorMessage": "One or more child records are invalid.",
  "status": 400,
  "requestId": "16ddc487-1092-496e-b9a4-97d3e3082ee6"
}

A distinct code per rule and a single human-readable message is the right split here: the code is for the interface, which must highlight a field, and the message is for the person, who does not need the taxonomy. Any code a project introduces must also exist in whatever maps codes to friendly messages — an unmapped code degrades the message to the raw code, which is how internal identifiers end up on screen.


4. Delete a scope ​

Endpoint: DELETE /admin/scopes/{id}

Description ​

Soft-delete a scope. Hard deletion is not offered: everything the scope owns references it, and the audit trail must keep resolving after the fact.

Authorization ​

An administrative delete permission over scopes.

Request Body ​

None.

Request Logic ​

Mark the scope deleted and cascade the soft delete to its owned children in the same operation. The scope's data is not deleted — this ends access, and any genuine erasure obligation is a separate, deliberate process.

Transactional Operations ​

The soft delete, the cascade and the audit entry commit together.

Success Response (204 No Content) ​

Empty body.

Error Responses ​

StatusCondition
403Caller lacks the permission
404Scope does not exist or is already deleted

What this API does not include ​

No endpoint accepts a scope identifier as a way of choosing which scope to act in. Scope context is resolved from the authenticated user (see the concept page). The identifiers in the paths above address the scope as a record being administered, which is a different thing and is why each route carries its own administrative permission.

Switching scope is a session concern belonging to whichever feature owns the application shell: what it changes is which scope subsequent requests resolve to, not any row here.