Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / API reference

Reference

API conventions

Methods, status codes, pagination, search and filters, ids, money, soft delete, bulk operations, action endpoints, and concurrency in the Cluster ERP API.

This page states the conventions that the Cluster ERP routes share: methods and status codes, list pagination, search and filters, identifiers, money values, deactivation instead of deletion, bulk operations, action endpoints, and optimistic concurrency. Counts on this page cover the 1,364 routes in src/cluster_erp/modules/ and src/cluster_erp/server/routes/, from the same scan as the module pages. The seven routes declared in src/cluster_erp/server/app.py are on System endpoints.

Methods

Method Routes Use Example
GET 510 list, read, and read models src/cluster_erp/modules/inventory/warehouses/routes.py:66
POST 531 create, actions, bulk operations, and computations src/cluster_erp/modules/inventory/warehouses/routes.py:108
PUT 158 update a record src/cluster_erp/modules/inventory/warehouses/routes.py:132
PATCH 8 partial update in sales_orders and in tenant fields, and one MRP item action src/cluster_erp/modules/sales_orders/orders/routes.py (update_order)
DELETE 157 deactivate a record or remove a dependent row src/cluster_erp/modules/inventory/warehouses/routes.py:146

Collection routes end with a slash in 263 route paths, for example /api/v1/inventory/warehouses/ (src/cluster_erp/modules/inventory/warehouses/routes.py:66). The module pages show the exact path of every route.

Status codes

Status Routes that declare it Meaning in this API Example
201 201 a record was created src/cluster_erp/modules/inventory/warehouses/routes.py:108
202 5 work was accepted and continues, for example a signup or a background MRP run src/cluster_erp/server/routes/signup.py:146
204 145 success without a body, mostly deactivation src/cluster_erp/modules/inventory/warehouses/routes.py:146
not declared 1,013 the framework default for a successful response; the module pages show it as 200 (default) see the FastAPI reference

Some handlers return their own Response object and set the status there, for example sign-out (src/cluster_erp/modules/auth/login/routes.py:373-432). Error statuses are listed on Errors.

Pagination

List routes take limit and offset query parameters (111 routes take limit, 105 take offset). Each route sets its own bounds in Query(...); for example the warehouse list uses a default of 50, a minimum of 1, and a maximum of 200 (src/cluster_erp/modules/inventory/warehouses/routes.py:66-73).

A paginated list answers with an envelope. From src/cluster_erp/modules/inventory/warehouses/routes.py:84-96:

    return {
        "items": await redact_extra_items(
            session,
            [WarehouseListRead.model_validate(w) for w in items],
            "warehouse",
            "warehouse",
            await actor_allowed(session),
        ),
        "total": total,
        "total_capped": svc.last_count_capped,
        "limit": limit,
        "offset": offset,
    }
Field Meaning Source
items the page of rows src/cluster_erp/modules/inventory/warehouses/routes.py:85-91
total the row count, counted up to the cap src/cluster_erp/core/pagination.py:33-69
total_capped true when the count stopped at the cap src/cluster_erp/core/pagination.py:65-68
limit, offset the values the request used src/cluster_erp/modules/inventory/warehouses/routes.py:94-95

The cap is the setting list_count_cap; 0 means exact counts (src/cluster_erp/config/settings.py:324, src/cluster_erp/core/pagination.py:27-30). Not every list route uses this envelope: routes with response_model=list[...] return a bare array. The module pages show the response model of each route.

Search and filters

Parameter Routes Meaning Example
search 93 free-text search over the entity's search columns src/cluster_erp/modules/inventory/warehouses/routes.py:66-69
status 46 filter on a status field module pages
active_only 35 only active rows module pages
is_active 6 filter on the is_active flag src/cluster_erp/modules/inventory/warehouses/routes.py:70
include_inactive 6 include deactivated rows module pages
extra.<key> lists that read tenant fields filter on a tenant-defined field value src/cluster_erp/core/tenant_fields.py:2292-2303

No route takes a parameter named for sorting: a scan of all query parameter names found no name that contains sort or order_by. The order of a list is fixed by its service. The search engine is explained in Search, pagination, and bulk operations.

Identifiers

Path identifiers such as {warehouse_id} are UUIDs. Every model that derives from BaseModel gets its primary key from new_ulid, so ids are ULID values stored as UUIDs (src/cluster_erp/core/models.py:20-24). The id scheme is explained in Base model and ids. A few routes take a code or a key instead, for example {provider} on mail OAuth apps and {key} on the operator console (module pages and System endpoints).

Money values

The project's written engineering rules require Decimal for money, Numeric in the database, and strings in JSON. Module schemas declare 774 fields or parameters with a Decimal annotation across 55 schemas.py files (count of lines matching : Decimal in src/cluster_erp/modules/**/schemas.py). Some read schemas declare an explicit string serialiser, for example ExchangeRateRead (src/cluster_erp/core/currencies.py:186-190). The generic record routes turn a Decimal into a string themselves (src/cluster_erp/modules/records/lifecycle_routes.py:47-57).

Whether every route with response_model=dict sends its Decimal values as strings is not determined from code; this page did not audit each of the 323 routes that declare response_model=dict. Clients send money as JSON strings, which a Decimal field accepts. The rule is explained in Money and decimal.

Deactivation instead of deletion

A DELETE on a record route deactivates the record: the service sets deleted_at and the route answers 204 (src/cluster_erp/modules/inventory/warehouses/services.py:137-143, src/cluster_erp/modules/inventory/warehouses/routes.py:146-154). Lists exclude rows whose deleted_at is set (src/cluster_erp/modules/inventory/warehouses/services.py:47). A client may send the reason and a note in the X-Deact-Reason and X-Deact-Note headers; the change history stamps them on the row (src/cluster_erp/server/dependencies.py:63-72).

Deactivated rows are read and reactivated through the generic record routes: GET /api/v1/records/{module}/{entity}/inactive, GET /api/v1/records/{module}/{entity}/labels, and POST /api/v1/records/{module}/{entity}/{record_id}/activate (src/cluster_erp/modules/records/lifecycle_routes.py:113-240). The rule is explained in Soft delete and deactivation.

Bulk operations

72 routes end with /bulk-delete. They take BulkDeleteRequest with ids and answer BulkDeleteResponse with deleted_count (src/cluster_erp/core/bulk_operations.py:12-17). The helper bulk_soft_delete loads each row through the ORM and sets deleted_at, so every row reaches the change history (src/cluster_erp/core/bulk_operations.py:20-57).

Illustrative request built from BulkDeleteRequest (src/cluster_erp/core/bulk_operations.py:12-13):

POST /api/v1/bom/boms/bulk-delete HTTP/1.1
Host: erp.example.com
Authorization: Bearer <access_token>
Content-Type: application/json

{
 "ids": ["<bom_id>", "<bom_id>"]
}

The path above is an illustration of the shape only; the module pages list which entities have a bulk route.

Action endpoints

State changes are separate POST routes, not fields in an update body. Two forms exist:

Form Example Source
a verb after the record path POST /api/v1/costing/analyses/{analysis_id}/approve src/cluster_erp/modules/costing/costing_analyses/routes.py (approve_analysis)
a generic action segment POST /api/v1/fleet/assets/{asset_id}/actions/{action} (43 routes contain /actions/) src/cluster_erp/modules/fleet/assets/routes.py (run_asset_action)

Optimistic concurrency

Every model has a version column (src/cluster_erp/core/models.py:73). Update schemas of several entities carry a version field; the service calls check_version, which raises 409 with CONCURRENCY_VERSION_CONFLICT and details.expected_version and details.current_version when the values differ (src/cluster_erp/core/errors.py:411-430). The scan found 60 raise sites of this code. A request without version skips the check (src/cluster_erp/core/errors.py:428-430).

Tenant field values

Read schemas that support tenant-defined fields carry them in extra; the route removes the values the caller may not see before it answers, through redact_extra and redact_extra_items (src/cluster_erp/core/tenant_fields.py:2478, src/cluster_erp/modules/inventory/warehouses/routes.py:53-63). The mechanism is explained in Tenant fields.

Terms introduced

Term Code identifier Definition Turkish term Source
list envelope items, total, total_capped, limit, offset The object a paginated list route returns. liste zarfı src/cluster_erp/modules/inventory/warehouses/routes.py:84-96
count cap list_count_cap The number of rows after which a list count stops and reports total_capped. sayım tavanı src/cluster_erp/config/settings.py:324
bulk deactivation BulkDeleteRequest A POST .../bulk-delete call that deactivates a list of ids. toplu kullanımdan kaldırma src/cluster_erp/core/bulk_operations.py:12-17

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13