The Cards module manages two card types: Issued Cards (cards issued by Moria/BaaS partner for an account, both virtual and physical) and Linked Cards (external user-owned cards linked for payments). Both controllers share the same CardService + DTOs, but are split by path so FE can differentiate listing/issuance UX from linking UX. Sensitive fields (full PAN, CVV, expiry) are stored encrypted on the server, and only the last 4 digits appear in responses.
Property Value Base URL {HOST}/v1Auth Bearer JWT (header Authorization) or cookie access_token Content-Type application/jsonError envelope { "message": string | string[], "statusCode": number, "error": string }Validation Global ValidationPipe · whitelist: true, forbidNonWhitelisted: true · unknown field → 400 Related modules accounts, payments, payment-gateway, users Document version v1 · 2026-05-20 Audience Internal FE devs (mobile + web)
Two parallel controllers — CardController at /issued-cards for Moria-issued cards, and LinkedCardsController at /linked-cards for external cards linked by users. All endpoints require a Bearer JWT plus ACL permissions (create-card, read-card, update-card, delete-card).
Method Path Auth Summary POST /v1/issued-cardsbearer Issue a new card (virtual / physical) GET /v1/issued-cardsbearer List all issued cards GET /v1/issued-cards/:idbearer Detail of a single issued card PATCH /v1/issued-cards/:idbearer Update issued card (status, image) DELETE /v1/issued-cards/:idbearer Delete/deactivate issued card POST /v1/linked-cardsbearer Link a user’s external card GET /v1/linked-cardsbearer List all linked cards GET /v1/linked-cards/:idbearer Detail of a single linked card PATCH /v1/linked-cards/:idbearer Update linked card DELETE /v1/linked-cards/:idbearer Unlink linked card
Endpoints for cards issued by Moria / BaaS partner (physical or virtual) bound to a single account_id. Path: /v1/issued-cards.
Issue a new card (virtual or physical) for an account. The server handles all sensitive fields (PAN, CVV, expiry, network, BaaS provider) via the BaaS integration — FE may only send the optional card_image.
bearer
create-card
Field Type Required Notes card_imagestring optional Card image URL (see file-manager module). All sensitive fields (PAN, CVV, expiry, network, BaaS provider, account_id) are set by the server.
"card_image" : " https://cdn.moriafund.com/cards/template-visa.png "
"message" : " Card created successfully " ,
"id" : " 550e8400-e29b-41d4-a716-446655440000 " ,
"card_holder_name" : " John Doe " ,
"card_number" : " ****-****-****-XXXX " ,
"account_id" : " 660e8400-e29b-41d4-a716-446655440111 " ,
"card_image" : " https://cdn.moriafund.com/cards/template-visa.png " ,
"issued_at" : " 2026-05-20T08:30:00.000Z " ,
"expires_at" : " 2030-05-20T08:30:00.000Z " ,
"created_at" : " 2026-05-20T08:30:00.000Z "
The card_number field is always masked (last 4 only). Full PAN, CVV, and raw expiry are never sent to the client — the server stores them encrypted.
Status When it occurs 400 Bad RequestInvalid card data (unknown field, bad URL format) 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing create-card permission
Retrieve all issued cards. The service determines scope (per account / per organization) based on the logged-in user.
bearer
read-card
"message" : " Cards retrieved successfully " ,
"id" : " 550e8400-e29b-41d4-a716-446655440000 " ,
"card_holder_name" : " John Doe " ,
"card_number" : " ****-****-****-XXXX " ,
"account_id" : " 660e8400-e29b-41d4-a716-446655440111 " ,
"card_image" : " https://cdn.moriafund.com/cards/template-visa.png " ,
"issued_at" : " 2026-01-01T00:00:00.000Z " ,
"expires_at" : " 2030-01-01T00:00:00.000Z "
Status When it occurs 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing read-card permission
Detail of a single issued card. The response only contains masked data + metadata; PAN/CVV is still not sent.
bearer
read-card
Param Type Notes idstring Issued card ID
"message" : " Card retrieved successfully " ,
"id" : " 550e8400-e29b-41d4-a716-446655440000 " ,
"card_holder_name" : " John Doe " ,
"card_number" : " ****-****-****-XXXX " ,
"account_id" : " 660e8400-e29b-41d4-a716-446655440111 " ,
"card_image" : " https://cdn.moriafund.com/cards/template-visa.png " ,
"issued_at" : " 2026-01-01T00:00:00.000Z " ,
"expires_at" : " 2030-01-01T00:00:00.000Z "
Status When it occurs 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing read-card permission 404 Not FoundCard not found
Update an issued card. Only non-sensitive fields (e.g. card_image) can be changed — PAN, CVV, and expiry are not in the DTO.
bearer
update-card
Param Type Notes idstring Issued card ID
Field Type Required Notes card_imagestring optional New image URL. PAN/CVV/expiry fields cannot be updated via this endpoint.
"card_image" : " https://cdn.moriafund.com/cards/template-gold.png "
"message" : " Card updated successfully " ,
"data" : { "..." : " same shape as CardDto " }
Status When it occurs 400 Bad RequestInvalid update data 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing update-card permission 404 Not FoundCard not found
Delete / deactivate an issued card. Soft-delete via deleted_at from AuditableEntity — the record remains in DB for audit purposes.
bearer
delete-card
Param Type Notes idstring Issued card ID
"message" : " Card deleted successfully "
Status When it occurs 400 Bad RequestCannot delete an active card (e.g. balance remaining / pending transactions) 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing delete-card permission 404 Not FoundCard not found
Endpoints for external cards (user-owned debit/credit from other banks) linked for payments. Path: /v1/linked-cards. The DTO shape is identical to issued cards — the separation is only by UX context and audit log.
Link an external card (debit / credit) to the user’s account for use as a source of funds for payments. Card tokenization is actually performed by the BaaS / payment gateway — FE only sends a minimal payload.
bearer
create-card
Field Type Required Notes card_imagestring optional Card logo / image URL. PAN and CVV are tokenized by the BaaS — not sent directly in this DTO.
"card_image" : " https://cdn.moriafund.com/cards/visa-debit.png "
"message" : " Card created successfully " ,
"id" : " 770e8400-e29b-41d4-a716-446655440222 " ,
"card_holder_name" : " John Doe " ,
"baas_provider" : " berrypay " ,
"card_number" : " ****-****-****-XXXX " ,
"account_id" : " 660e8400-e29b-41d4-a716-446655440111 " ,
"card_image" : " https://cdn.moriafund.com/cards/visa-debit.png " ,
"issued_at" : " 2024-06-01T00:00:00.000Z " ,
"expires_at" : " 2028-06-01T00:00:00.000Z "
Status When it occurs 400 Bad RequestInvalid card data / tokenization failed 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing create-card permission
List all linked cards belonging to the user. Scope is determined by the service.
bearer
read-card
"message" : " Linked cards retrieved successfully " ,
"id" : " 770e8400-e29b-41d4-a716-446655440222 " ,
"card_holder_name" : " John Doe " ,
"baas_provider" : " berrypay " ,
"card_number" : " ****-****-****-XXXX " ,
"account_id" : " 660e8400-e29b-41d4-a716-446655440111 " ,
"card_image" : " https://cdn.moriafund.com/cards/visa-debit.png " ,
"issued_at" : " 2024-06-01T00:00:00.000Z " ,
"expires_at" : " 2028-06-01T00:00:00.000Z "
Status When it occurs 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing read-card permission
Detail of a single linked card.
bearer
read-card
Param Type Notes idstring Linked card ID
"message" : " Linked card retrieved successfully " ,
"card" : { "..." : " same shape as CardDto · sensitive fields remain masked " }
Status When it occurs 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing read-card permission 404 Not FoundLinked card not found
Update a linked card (e.g. card_image or billing metadata). Raw PAN/CVV/expiry cannot be changed via this endpoint.
bearer
update-card
Param Type Notes idstring Linked card ID
Field Type Required Notes card_imagestring optional New image URL
"card_image" : " https://cdn.moriafund.com/cards/mc-debit-v2.png "
"message" : " Card updated successfully " ,
"data" : { "..." : " same shape as CardDto " }
Status When it occurs 400 Bad RequestInvalid update data 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing update-card permission 404 Not FoundLinked card not found
Unlink an external card from the user’s account. Soft-delete; FE must refresh the card list afterwards.
bearer
delete-card
Param Type Notes idstring Linked card ID
"message" : " Card deleted successfully "
Status When it occurs 401 UnauthorizedBearer/cookie token invalid 403 ForbiddenMissing delete-card permission 404 Not FoundLinked card not found
pending — awaiting BaaS activation
active — ready to use
suspended — frozen by admin/user
expired — past expires_at
lost · stolen — reported lost/stolen
cimb · berrypay · amarbank
card_number (PAN) — stored via @EncryptedColumn; response sends masked form only
cvv — never sent to FE
card_holder_name, last_digits — encrypted in DB, decrypted server-side on response
expires_at — sent as ISO timestamp, not physical card format (MM/YY)
"message" : " Card not found " ,
400 invalid body/param validation
401 token expired / missing
403 permission mismatch
404 card not found
500 internal — show a generic toast