The Pools module manages organization-level fund pools used as aggregate containers for investments and takaful. Each pool has a pool_type (investment / takaful), currency, total balance, and one optional pool_config that defines ROI rate, ROI cycle, contribution limits, lock-in days, and notice days for withdrawal. A single PoolsController at /pools; pool creation is not exposed here (handled during onboarding/initialization), but pool config can be created and updated.
Property Value Base URL {HOST}/v1Auth Bearer JWT (header Authorization) or access_token cookie Content-Type application/jsonError envelope { "message": string | string[], "statusCode": number, "error": string }Validation Global ValidationPipe · whitelist: true, forbidNonWhitelisted: true · unknown fields → 400 Related modules organizations, accounts, investments, takaful, withdrawal Document version v1 · 2026-05-20 Audience Internal FE devs (mobile + web)
Organization admins read the list of pools they own and the detail of each pool / its config. Only the MORIA superadmin can create or full-replace a pool config (Put). The pool’s name / status / currency fields can be updated by MORIA and ORGANIZATION. All actions are logged via BusinessEvent.
Method Path Auth Summary GET /v1/poolsbearer List pools for one organization (paginated) GET /v1/pools/:pool_idbearer Detail of one pool GET /v1/pools/config/:pool_idbearer Config detail for one pool POST /v1/pools/configbearer Create pool config (MORIA only) PUT /v1/pools/configbearer Full-replace pool config (MORIA only) PATCH /v1/pools/:pool_idbearer Update pool fields (name, currency, status)
Auth + scope notes
All endpoints require Bearer JWT.
GET /pools requires the query organization_id (UUID, validated via ParseUUIDPipe) — the server does not auto-resolve from the logged-in user.
Pool config: create and full-replace via PUT are MORIA-role only. For partial field updates use PATCH (currently not available for config — only for the pool itself).
PATCH /pools/:pool_id is allowed for both MORIA and ORGANIZATION (org admin) roles.
All endpoints emit @BusinessEvent — FE telemetry can subscribe to RESOURCE_FETCHED / RESOURCE_CREATED / RESOURCE_UPDATED types.
List pools for one organization. The organization_id query is required and must be a UUID. The result is paginated with page, limit, and ordered by created_at (asc/desc).
bearer
MORIA, ORGANIZATION
read-pool
RESOURCE_FETCHED
Param Type Default Notes organization_idstring (UUID) — Required — validated by ParseUUIDPipepagenumber 1Page number limitnumber 10Records per page order'asc' | 'desc'descOrder by created_at
"message" : " Pools retrieved successfully " ,
"id" : " be2cb7da-e217-401c-8d07-b01e64adfb34 " ,
"organization_id" : " 123e4567-e89b-12d3-a456-426614174000 " ,
"account_id" : " 123e4567-e89b-12d3-a456-426614174001 " ,
"pool_type" : " investment " ,
"name" : " Emergency Fund Pool " ,
"total_balance" : " 25000000.0000 " ,
"created_at" : " 2026-01-10T08:30:00.000Z " ,
"updated_at" : " 2026-05-20T08:30:00.000Z "
Status When it occurs 400 Bad Requestorganization_id is not a UUID / empty401 UnauthorizedBearer/cookie token is invalid 403 ForbiddenMissing read-pool permission or role is not MORIA/ORGANIZATION
Detail of one pool. No explicit role gate on the controller — only the read-pool permission and a valid Bearer are needed.
bearer
read-pool
RESOURCE_FETCHED
Param Type Notes pool_idUUID Validated via ParseUUIDPipe
"message" : " Pool retrieved successfully " ,
"id" : " be2cb7da-e217-401c-8d07-b01e64adfb34 " ,
"organization_id" : " 123e4567-e89b-12d3-a456-426614174000 " ,
"account_id" : " 123e4567-e89b-12d3-a456-426614174001 " ,
"pool_type" : " investment " ,
"name" : " Emergency Fund Pool " ,
"total_balance" : " 25000000.0000 " ,
"created_at" : " 2026-01-10T08:30:00.000Z " ,
"updated_at" : " 2026-05-20T08:30:00.000Z "
Status When it occurs 400 Bad Requestpool_id is not a UUID401 UnauthorizedBearer/cookie token is invalid 403 ForbiddenMissing read-pool permission 404 Not FoundPool not found
Detail of one pool’s configuration (ROI rate, ROI cycle, contribution limits, lock-in, notice days). Separate permission: read-pool-config.
bearer
read-pool-config
RESOURCE_FETCHED
Param Type Notes pool_idUUID Validated via ParseUUIDPipe
"message" : " Pool config for pool id be2cb7da-e217-401c-8d07-b01e64adfb34 retrieved successfully " ,
"id" : " 990e8400-e29b-41d4-a716-446655440444 " ,
"pool_id" : " be2cb7da-e217-401c-8d07-b01e64adfb34 " ,
"min_contribution" : " 1000.0000 " ,
"max_contribution" : " 100000.0000 " ,
"min_withdrawal" : " 500.0000 " ,
"created_at" : " 2026-01-10T08:30:00.000Z " ,
"updated_at" : " 2026-05-20T08:30:00.000Z "
Status When it occurs 400 Bad Requestpool_id is not a UUID401 UnauthorizedBearer/cookie token is invalid 403 ForbiddenMissing read-pool-config permission 404 Not FoundPool config not found
Create a new pool config for a pool. Only the MORIA (superadmin) role can call this endpoint.
bearer
MORIA
create-pool-config
RESOURCE_CREATED
Field Type Required Notes pool_idstring (UUID) yes ID of the pool to be configured · IsUUID roi_ratestring (numeric) optional ROI rate (e.g. "0.05" = 5%) · IsNumberString roi_cycleenum ROICycle optional monthly, quarterly, yearlymin_contributionstring (numeric) optional Minimum contribution (rupiah, integer/decimal) max_contributionstring (numeric) optional Maximum contribution lock_in_daysinteger optional Minimum days funds are locked before withdrawal min_withdrawalstring (numeric) optional Minimum withdrawal amount notice_daysinteger optional Notice days required before withdrawal
"pool_id" : " be2cb7da-e217-401c-8d07-b01e64adfb34 " ,
"min_contribution" : " 1000.00 " ,
"max_contribution" : " 100000.00 " ,
"min_withdrawal" : " 500.00 " ,
"message" : " Pool configuration created successfully " ,
"id" : " 990e8400-e29b-41d4-a716-446655440444 " ,
"pool_id" : " be2cb7da-e217-401c-8d07-b01e64adfb34 " ,
"min_contribution" : " 1000.0000 " ,
"max_contribution" : " 100000.0000 " ,
"min_withdrawal" : " 500.0000 " ,
"created_at" : " 2026-05-20T08:30:00.000Z "
Note: the current controller code sets the statusCode field to 200 even though the HTTP response status is 201 Created (Nest default for @Post()). FE should rely on the HTTP status, not the body statusCode for this endpoint.
Status When it occurs 400 Bad Requestpool_id is not a UUID · invalid enum/numeric401 UnauthorizedBearer/cookie token is invalid 403 ForbiddenRole is not MORIA or missing create-pool-config permission 404 Not FoundPool with pool_id not found
Emit BusinessEvent RESOURCE_CREATED (impact MEDIUM, request body captured).
Full-replace pool config (every optional field sent will overwrite). MORIA role only.
bearer
MORIA
update-pool-config
RESOURCE_UPDATED
Field Type Required Notes pool_idstring (UUID) yes ID of the pool whose config is updated roi_ratestring (numeric) optional New ROI rate roi_cycleenum ROICycle optional monthly, quarterly, yearlymin_contributionstring (numeric) optional max_contributionstring (numeric) optional lock_in_daysinteger optional min_withdrawalstring (numeric) optional notice_daysinteger optional
"pool_id" : " be2cb7da-e217-401c-8d07-b01e64adfb34 " ,
"roi_cycle" : " quarterly " ,
"message" : " Pool configuration updated successfully " ,
"pool_config" : { "..." : " shape matches GET /v1/pools/config/:pool_id " }
Status When it occurs 400 Bad RequestInvalid body (pool_id not UUID, wrong enum, wrong numeric) 401 UnauthorizedBearer/cookie token is invalid 403 ForbiddenRole is not MORIA or missing update-pool-config permission 404 Not FoundPool / pool config not found
Emit BusinessEvent RESOURCE_UPDATED (impact MEDIUM, request body captured).
Update the pool fields themselves (not the config) — name, currency, status. Callable by both MORIA and ORGANIZATION.
bearer
MORIA, ORGANIZATION
update-pool
RESOURCE_UPDATED
Param Type Notes pool_idUUID Validated via ParseUUIDPipe
Field Type Required Notes namestring optional New pool name currencystring optional Currency code (DB default IDR) statusenum PoolStatus optional active, inactive
"name" : " Emergency Fund Pool 2026 " ,
"message" : " Pool information updated successfully " ,
"pool" : { "..." : " shape matches GET /v1/pools/:pool_id " }
Status When it occurs 400 Bad Requestpool_id is not a UUID · invalid status enum401 UnauthorizedBearer/cookie token is invalid 403 ForbiddenRole is not MORIA/ORGANIZATION · missing update-pool permission 404 Not FoundPool not found
Emit BusinessEvent RESOURCE_UPDATED (impact MEDIUM, request body captured).
investment — pool for investment products
takaful — pool for takaful products
monthly · quarterly · yearly
read-pool — list + pool detail
read-pool-config — config detail
create-pool-config — create config (MORIA)
update-pool-config — replace config (MORIA)
update-pool — update pool (MORIA/ORG)
"message" : " Validation failed (uuid is expected) " ,
message can be a string or an array of strings (multi-field validation error).
400 body/query/param validation invalid
401 token expired / missing
403 wrong role · missing permission
404 pool / pool config not found
500 internal — show a generic toast
The pool creation endpoint itself (POST /pools) is not exposed in the current controller — pools are created by the backend during onboarding/initialization. FE does not need a create-pool form.
The total_balance field is always a numeric string (precision 20, scale 4) — render as rupiah with client-side formatting.
roi_rate is fractional (e.g. 0.0500 = 5%); do not multiply by 100 on display if you are already using a percent helper.