This page lists the 28 endpoints that are not part of a business module: the 7 routes that create_app declares in src/cluster_erp/server/app.py, the 6 signup routes, and the 15 operator console routes. Signup and operator console routes exist only when multi_tenant is on (src/cluster_erp/server/app.py:1440-1457). They are described here at mechanism level only.
Group facts¶
| Item | Value | Source |
|---|---|---|
| Endpoints | 28: 7 in app.py, 6 in signup.py, 15 in vendor.py |
@app.get and @router.* decorators |
| Mount condition | signup.py and vendor.py routers when settings.multi_tenant is true; otherwise only the closed-signup probe |
src/cluster_erp/server/app.py:1440-1457 |
| Operator authentication | Separate operator token in the Authorization header, read by require_operator; the router also depends on _client_address_allowed |
src/cluster_erp/server/routes/vendor.py:68-70, src/cluster_erp/server/routes/vendor.py:84-95 |
| Cross-origin access | Only /api/v1/vendor/fleet, only for the configured console origins |
src/cluster_erp/server/app.py:1363-1389 |
| Error format | See Errors | src/cluster_erp/server/error_handler.py:33-46 |
Application routes¶
create_app declares these routes directly with @app.get. Health and readiness answer without a credential; the module list, metadata, and switches require a signed-in user. The readiness contract is described in Security.txt and readiness.
| Method | Path | Handler | Permission | Response | Notes | Source |
|---|---|---|---|---|---|---|
GET |
/api/v1/signup/ |
signup_closed |
none | {"signup": "closed"} |
Registered only when multi_tenant is off; excluded from the schema. |
src/cluster_erp/server/app.py:1456 |
GET |
/.well-known/security.txt |
security_txt |
none | text/plain or 404 |
Body from security_txt_body; 404 when it returns None. Excluded from the schema. |
src/cluster_erp/server/app.py:1460 |
GET |
/health |
health |
none | {"status": "healthy", "version": ...} |
Liveness: no external check. | src/cluster_erp/server/app.py:1469 |
GET |
/ready |
ready |
none | 200 or 503 with status and checks |
Probes the database and, when present, the control plane under readiness_timeout_seconds. |
src/cluster_erp/server/app.py:1475 |
GET |
/api/v1/modules |
list_modules |
authenticated user (get_current_user) |
module name to version and description | Reads app.state.registry.loaded. |
src/cluster_erp/server/app.py:1508 |
GET |
/api/v1/_meta |
get_metadata |
authenticated user (get_current_user) |
metadata JSON with ETag, or 304 |
Rendered once per process; gzip form when Accept-Encoding contains gzip; 304 when If-None-Match matches. |
src/cluster_erp/server/app.py:1519 |
GET |
/api/v1/_switches |
get_display_switches |
authenticated user (get_current_user) |
switch name to boolean | Merges the answers to DescribeDisplaySwitches. |
src/cluster_erp/server/app.py:1559 |
Static web app mounts¶
When the webapp folder exists, create_app mounts it twice: /app/v serves files under a stamp segment, and /app serves the same files with Cache-Control: no-cache and index.html for the root (src/cluster_erp/server/app.py:1634-1645). The web app is described in Web app overview.
Signup endpoints¶
The public self-service signup flow: an availability probe, the signup request, the verification of the mailed link, a resend, a cancel, and a status poll. Requests that create work are rate-limited per client, and the poll and resend calls carry the signup token in the body, never in the URL (src/cluster_erp/server/routes/signup.py:102-112). The flow is explained in Signup.
| Method | Path | Handler | Permission | Request | Query | Response | Status | Source |
|---|---|---|---|---|---|---|---|---|
GET |
/api/v1/signup/ |
signup_available |
none (public) | none | none | dict |
200 (default) | src/cluster_erp/server/routes/signup.py:114 |
POST |
/api/v1/signup/ |
create_signup |
none (public) | SignupCreate |
none | dict |
202 |
src/cluster_erp/server/routes/signup.py:146 |
POST |
/api/v1/signup/verify |
verify_signup |
none (public) | VerifyTokenBody |
none | dict |
200 (default) | src/cluster_erp/server/routes/signup.py:171 |
POST |
/api/v1/signup/resend |
resend_signup_verification |
none (public) | SignupTokenBody |
none | dict |
202 |
src/cluster_erp/server/routes/signup.py:192 |
POST |
/api/v1/signup/cancel |
cancel_signup_route |
none (public) | SignupTokenBody |
none | none | 204 |
src/cluster_erp/server/routes/signup.py:205 |
POST |
/api/v1/signup/status |
signup_state |
none (public) | SignupTokenBody |
none | dict |
200 (default) | src/cluster_erp/server/routes/signup.py:222 |
Operator console endpoints¶
The operator console API under /api/v1/vendor: operator sign-in, the operator's own record, console metadata, the overview, tenants, the minimised fleet view, signups, the operator action log, membership changes, seed refresh, and tenant backups. Every route except sign-in requires an operator token; operator accounts and tokens are separate from tenant users. The console is explained in Operator console.
| Method | Path | Handler | Permission | Request | Query | Response | Status | Source |
|---|---|---|---|---|---|---|---|---|
POST |
/api/v1/vendor/login |
vendor_login |
none (operator sign-in) | OperatorLogin |
none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:113 |
GET |
/api/v1/vendor/me |
vendor_me |
operator token (require_operator) |
none | none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:141 |
GET |
/api/v1/vendor/_meta |
vendor_meta |
operator token (require_operator) |
none | none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:149 |
GET |
/api/v1/vendor/overview |
vendor_overview |
operator token (require_operator) |
none | none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:332 |
GET |
/api/v1/vendor/tenants |
vendor_tenants |
operator token (require_operator) |
none | search |
dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:394 |
GET |
/api/v1/vendor/fleet |
vendor_fleet |
operator token (require_operator) |
none | none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:456 |
GET |
/api/v1/vendor/tenants/{key} |
vendor_tenant |
operator token (require_operator) |
none | none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:497 |
GET |
/api/v1/vendor/signups |
vendor_signups |
operator token (require_operator) |
none | none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:540 |
GET |
/api/v1/vendor/actions |
vendor_actions |
operator token (require_operator) |
none | limit |
dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:570 |
POST |
/api/v1/vendor/tenants/{key}/membership/extend |
vendor_extend |
operator token (require_operator) |
ExtendBody |
none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:607 |
POST |
/api/v1/vendor/tenants/{key}/membership/suspend |
vendor_suspend |
operator token (require_operator) |
none | none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:626 |
POST |
/api/v1/vendor/tenants/{key}/membership/activate |
vendor_activate |
operator token (require_operator) |
none | none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:639 |
POST |
/api/v1/vendor/tenants/{key}/refresh-seeds |
vendor_refresh_seeds |
operator token (require_operator) |
none | none | dict |
202 |
src/cluster_erp/server/routes/vendor.py:652 |
POST |
/api/v1/vendor/tenants/{key}/backup |
vendor_backup |
operator token (require_operator) |
BackupBody |
none | dict |
202 |
src/cluster_erp/server/routes/vendor.py:690 |
GET |
/api/v1/vendor/tenants/{key}/backups |
vendor_backups |
operator token (require_operator) |
none | none | dict |
200 (default) | src/cluster_erp/server/routes/vendor.py:740 |
Error codes¶
Error codes raised in src/cluster_erp/server/routes/signup.py, src/cluster_erp/server/routes/vendor.py, and the signup service src/cluster_erp/server/signup.py, found by the syntax-tree scan described on Errors.
| Code | HTTP status | Raised in | First site |
|---|---|---|---|
CONSOLE_BACKUP_DISABLED |
403 |
`` | src/cluster_erp/server/routes/vendor.py:697 |
RESOURCE_NOT_FOUND |
404 |
`` | src/cluster_erp/server/routes/signup.py:182 |
SIGNUP_ALREADY_PROVISIONED |
409 |
`` | src/cluster_erp/server/routes/signup.py:215 |
SIGNUP_CLOSED |
403 |
`` | src/cluster_erp/server/signup.py:230 |
SIGNUP_DOMAIN_TAKEN |
409 |
`` | src/cluster_erp/server/signup.py:303 |
SIGNUP_INVALID |
402, 422, 500 |
`` | src/cluster_erp/server/signup.py:166 |
SIGNUP_NOTICE_MISSING |
409 |
`` | src/cluster_erp/server/signup.py:242 |
SIGNUP_NOTICE_REQUIRED |
422 |
`` | src/cluster_erp/server/signup.py:248 |
SIGNUP_RATE_LIMITED |
429 |
`` | src/cluster_erp/server/routes/signup.py:81 |
SIGNUP_VERIFY_EXPIRED |
410 |
`` | src/cluster_erp/server/routes/signup.py:184 |
VENDOR_ACCOUNT_LOCKED |
401 |
`` | src/cluster_erp/server/routes/vendor.py:124 |
VENDOR_AUTH_REQUIRED |
401, 403 |
`` | src/cluster_erp/server/routes/vendor.py:53 |
VENDOR_INVALID_CREDENTIALS |
401 |
`` | src/cluster_erp/server/routes/vendor.py:134 |
VENDOR_TOTP_NOT_ENROLLED |
403 |
`` | src/cluster_erp/server/routes/vendor.py:128 |
VENDOR_TOTP_REQUIRED |
401 |
`` | src/cluster_erp/server/routes/vendor.py:126 |
Related pages¶
- API reference
- Signup
- Operator console
- Multi-tenancy
- Request pipeline and middleware
- Shared server routes API
Verified against commit 92f8051 on 2026-11-13.