This page lists the 35 endpoints of the purchase_rfqs sub-module of the procurement module, under /api/v1/procurement. Scope: Two routers: RFQ records with send, evaluation, award, close, cancel, reopen, lines, invited vendors, quotations, criteria, scoring, and comparison; and the supplier portal router in portal_routes.py with a token in the path. The module entry page is Procurement API.
Group facts¶
| Item | Value | Source |
|---|---|---|
| Base path | /api/v1/procurement |
src/cluster_erp/core/module_base.py:37-38, src/cluster_erp/modules/procurement/module.py:70 |
| Routers | 2 router objects in 2 files; listed in the section Endpoints | src/cluster_erp/modules/procurement/module.py:35 |
| Endpoints | 35, counted from the route decorators and add_api_route calls of the routers that register_routes mounts |
src/cluster_erp/modules/procurement/purchase_rfqs/portal_routes.py, src/cluster_erp/modules/procurement/purchase_rfqs/routes.py |
| Authentication | Bearer header or session cookie, resolved by get_current_user; see Authentication and headers |
src/cluster_erp/modules/auth/guards.py:85-167 |
| Permission guard | 32 of 35 routes declare require_permission; the other routes show their guard in the tables |
src/cluster_erp/modules/auth/guards.py:205-240 |
| App entitlement gate | require_app("procurement") on every mounted router |
src/cluster_erp/modules/procurement/module.py:82 |
| Error format | JSON body {"error": {...}}; see Errors |
src/cluster_erp/server/error_handler.py:33-46 |
Endpoints¶
| Method | Path | Handler | Permission | Request | Query | Response | Status | Source |
|---|---|---|---|---|---|---|---|---|
GET |
/api/v1/procurement/purchase-rfqs/ |
list_rfqs |
purchase_rfq.read |
none | search, status, vendor_party_id, limit, offset |
dict |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:66 |
POST |
/api/v1/procurement/purchase-rfqs/ |
create_rfq |
purchase_rfq.create |
PurchaseRfqCreate |
none | PurchaseRfqRead |
201 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:102 |
GET |
/api/v1/procurement/purchase-rfqs/{rfq_id} |
get_rfq |
purchase_rfq.read |
none | none | PurchaseRfqRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:113 |
PUT |
/api/v1/procurement/purchase-rfqs/{rfq_id} |
update_rfq |
purchase_rfq.update |
PurchaseRfqUpdate |
none | PurchaseRfqRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:126 |
DELETE |
/api/v1/procurement/purchase-rfqs/{rfq_id} |
delete_rfq |
purchase_rfq.delete |
none | none | none | 204 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:140 |
POST |
/api/v1/procurement/purchase-rfqs/bulk-delete |
bulk_delete_rfqs |
purchase_rfq.delete |
BulkDeleteRequest |
none | BulkDeleteResponse |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:151 |
GET |
/api/v1/procurement/purchase-rfqs/{rfq_id}/completeness |
get_completeness |
purchase_rfq.read |
none | none | dict |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:166 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/send |
send_rfq |
purchase_rfq.send |
RfqTransition |
none | PurchaseRfqRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:181 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/start-evaluation |
start_evaluation |
purchase_rfq.evaluate |
RfqTransition |
none | PurchaseRfqRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:193 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/reopen-bidding |
reopen_bidding |
purchase_rfq.evaluate |
RfqTransition |
none | PurchaseRfqRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:207 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/award |
award_rfq |
purchase_rfq.award |
PurchaseRfqAwardRequest |
none | PurchaseRfqRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:221 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/close |
close_rfq |
purchase_rfq.evaluate |
RfqTransition |
none | PurchaseRfqRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:240 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/cancel |
cancel_rfq |
purchase_rfq.cancel |
RfqReasonTransition |
none | PurchaseRfqRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:252 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/reopen |
reopen_rfq |
purchase_rfq.update |
RfqTransition |
none | PurchaseRfqRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:266 |
GET |
/api/v1/procurement/purchase-rfqs/{rfq_id}/lines |
list_lines |
purchase_rfq.read |
none | search |
list[PurchaseRfqLineRead] |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:279 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/lines |
add_line |
purchase_rfq.update |
PurchaseRfqLineCreate |
none | PurchaseRfqLineRead |
201 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:291 |
PUT |
/api/v1/procurement/purchase-rfq-lines/{line_id} |
update_line |
purchase_rfq.update |
PurchaseRfqLineUpdate |
none | PurchaseRfqLineRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:303 |
DELETE |
/api/v1/procurement/purchase-rfq-lines/{line_id} |
delete_line |
purchase_rfq.update |
none | none | none | 204 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:317 |
GET |
/api/v1/procurement/purchase-rfqs/{rfq_id}/vendors |
list_vendors |
purchase_rfq.read |
none | search |
list[PurchaseRfqVendorRead] |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:329 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/vendors |
invite_vendor |
purchase_rfq.update |
PurchaseRfqVendorInvite |
none | PurchaseRfqVendorRead |
201 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:341 |
DELETE |
/api/v1/procurement/purchase-rfq-vendors/{vendor_id} |
remove_vendor |
purchase_rfq.update |
none | none | none | 204 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:357 |
GET |
/api/v1/procurement/purchase-rfq-vendors/{vendor_id}/portal-token |
get_vendor_portal_token |
purchase_rfq.update |
none | none | PurchaseRfqVendorPortalToken |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:368 |
GET |
/api/v1/procurement/purchase-rfqs/{rfq_id}/quotations |
list_quotations |
purchase_rfq.read |
none | none | list[PurchaseRfqQuotationRead] |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:398 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/quotations |
create_quotation |
purchase_rfq.update |
PurchaseRfqQuotationCreate |
none | PurchaseRfqQuotationRead |
201 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:412 |
DELETE |
/api/v1/procurement/purchase-rfq-quotations/{quotation_id} |
delete_quotation |
purchase_rfq.update |
none | none | none | 204 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:428 |
GET |
/api/v1/procurement/purchase-rfqs/{rfq_id}/criteria |
list_criteria |
purchase_rfq.read |
none | search |
list[PurchaseRfqCriterionRead] |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:440 |
POST |
/api/v1/procurement/purchase-rfqs/{rfq_id}/criteria |
add_criterion |
purchase_rfq.update |
PurchaseRfqCriterionCreate |
none | PurchaseRfqCriterionRead |
201 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:452 |
PUT |
/api/v1/procurement/purchase-rfq-criteria/{criterion_id} |
update_criterion |
purchase_rfq.update |
PurchaseRfqCriterionUpdate |
none | PurchaseRfqCriterionRead |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:468 |
DELETE |
/api/v1/procurement/purchase-rfq-criteria/{criterion_id} |
delete_criterion |
purchase_rfq.update |
none | none | none | 204 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:485 |
PUT |
/api/v1/procurement/purchase-rfqs/{rfq_id}/quotations/{quotation_id}/score |
set_score |
purchase_rfq.evaluate |
PurchaseRfqScoreSet |
none | none | 204 |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:496 |
GET |
/api/v1/procurement/purchase-rfqs/{rfq_id}/scoring |
scoring |
purchase_rfq.read |
none | none | dict |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:508 |
GET |
/api/v1/procurement/purchase-rfqs/{rfq_id}/comparison |
comparison |
purchase_rfq.read |
none | none | dict |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:519 |
GET |
/api/v1/procurement/portal/rfqs/{token} |
portal_view |
portal token in path | none | none | dict |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/portal_routes.py:34 |
POST |
/api/v1/procurement/portal/rfqs/{token}/quotation |
portal_submit |
portal token in path | PortalQuotationSubmit |
none | dict |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/portal_routes.py:45 |
POST |
/api/v1/procurement/portal/rfqs/{token}/decline |
portal_decline |
portal token in path | PortalDeclineRequest |
none | dict |
200 (default) | src/cluster_erp/modules/procurement/purchase_rfqs/portal_routes.py:56 |
Error codes¶
The table lists the error codes that code under src/cluster_erp/modules/procurement/purchase_rfqs/ raises, found by a syntax-tree scan for AppError(...), the factories in src/cluster_erp/core/errors.py (not_found, conflict, protected, permission_denied, privilege_escalation, check_version), and HTTPException(...). A status of 400 is the AppError default (src/cluster_erp/core/errors.py:23). Guard errors (401, 403), the entitlement codes, and validation errors (422) apply to every route and are listed on Errors.
| Code | HTTP status | Raised in | First site |
|---|---|---|---|
CONCURRENCY_VERSION_CONFLICT |
409 |
services |
src/cluster_erp/modules/procurement/purchase_rfqs/services.py:237 |
PRFQ_AWARD_INVALID |
422 |
services |
src/cluster_erp/modules/procurement/purchase_rfqs/services.py:778 |
PRFQ_INCOMPLETE |
409 |
services |
src/cluster_erp/modules/procurement/purchase_rfqs/services.py:299 |
PRFQ_INVALID_STATUS |
409 |
services |
src/cluster_erp/modules/procurement/purchase_rfqs/services.py:188 |
RESOURCE_NOT_FOUND |
404 |
routes, services |
src/cluster_erp/modules/procurement/purchase_rfqs/routes.py:122 |
Related pages¶
Verified against commit 92f8051 on 2026-11-13.