🏦 Bank Wiki
The Bank application is a financial management sandbox simulating real-world banking operations. It supports account creation (checking and savings), balance-based transfers between accounts, bill payment scheduling, loan applications, and in-app message notifications.
All monetary values are stored in cents (integers) and returned as formatted dollar strings. This creates interesting edge cases around precision, rounding, and type handling that are valuable for QA testing.
Key Characteristics
- Account types:
CHECKINGandSAVINGS - Maximum 5
OPENaccounts per user - Balance stored in cents — e.g.,
10000=$100.00 - Account must have $0.00 balance to be closed or deleted
- Transfers are atomic — sender debited, recipient credited immediately
- Bills can be one-time or recurring with scheduling
- Loan applications go through PENDING → APPROVED/REJECTED lifecycle
- Messages auto-created on transfer events
- Reset endpoint restores account + transaction template state
- All endpoints require API key authentication
Quick Start
Authorization: qac_live_<your_api_key> in the request header. Get your key from the Profile page.
amount fields are in cents. Send 1000 to transfer $10.00. Responses format amounts as dollar strings: "$10.00".
Account Flow
Accounts are the root resource. All transfers, bills, and loans are associated with accounts.
Account Statuses
Business Rules
- Account type must be
CHECKINGorSAVINGS - Maximum 5 open accounts per user at a time
- Initial deposit: $0 to $10,000 (in cents: 0 to 1,000,000)
- To close an account: balance must be exactly
0 - To remove an account: account must already be
CLOSED - To delete an account permanently: balance must be
0 - Account numbers are 10-digit auto-generated unique strings
Transfer Flow
Transfers move funds between two open accounts. Transfers are completed immediately and generate message notifications for both sender and recipient.
Transfer Rules
- Both
from_account_idandto_account_idmust beOPEN - Cannot transfer to the same account
- Sender must have sufficient balance (amount in cents)
- Source account must be owned by the authenticated user
- Recipient account can belong to any user (cross-user transfers allowed)
- Funds are deducted from sender and credited to recipient atomically
- A
TRANSFER_SENTmessage is created for the sender - A
TRANSFER_RECEIVEDmessage is created for the recipient (if different user)
Bill Payment Flow
Bills represent scheduled or one-time payment obligations. Bills are associated with a pay-from account and can be set as recurring.
Bill Statuses
Bill Rules
- Required:
payee,category,amount(cents),next_due_date,from_account_id - For recurring bills:
is_recurring: truerequiresfrequencyfield - Pay-from account must be owned by the authenticated user
- PAID bills appear in the transaction history as
withdrawaltype
Loan Application Flow
Users submit loan applications which go through a manually-controlled approval process. Loans do not automatically credit accounts.
Loan Statuses
Loan Rules
- Required fields:
loan_reason,loan_amount,term_months,employment_status,annual_income - Only PENDING loans can be approved or rejected
- Approval and rejection are done via PATCH endpoints
- Loans can be deleted regardless of status
API – Accounts
Base path: /api/bank/accounts
| Field | Type | Required | Notes |
|---|---|---|---|
type | string | required | CHECKING or SAVINGS |
initial_deposit | integer | optional | Cents. 0–1,000,000 ($0–$10,000) |
Returns: 201 with account object. 400 if max 5 open accounts reached or invalid type.
| Query Param | Notes |
|---|---|
status | OPEN, CLOSED, REMOVED, or ALL. Default: shows OPEN only |
Returns { accounts: [...] }. Balance shown as both formatted string and raw cents.
Sets status to CLOSED and records closed_at timestamp. Returns 400 if balance is non-zero or account already closed.
Sets status to REMOVED. Account history is preserved. Returns 400 if account is not CLOSED.
Permanently removes account. Returns 400 if balance is non-zero. This is irreversible.
Deletes all user's accounts, transactions, bills, loans, and messages. Repopulates from template. Returns counts of created resources.
API – Transfers
| Field | Type | Required | Notes |
|---|---|---|---|
from_account_id | uuid | required | Must be owned by user, must be OPEN |
to_account_id | uuid | required | Must be OPEN, cannot be same as from |
amount | integer | required | Positive integer in cents |
Returns: 201 with transaction object. 400 for same-account transfer, insufficient funds, or non-OPEN accounts.
| Query Param | Notes |
|---|---|
account_id | Filter to one account |
type | Comma-separated: transfer, withdrawal |
min_amount | Minimum amount in cents |
max_amount | Maximum amount in cents |
Returns combined transfers and paid bills, ordered by date DESC. Each item has a type field: transfer or withdrawal.
API – Bills
Base path: /api/bank/bills
| Field | Type | Required | Notes |
|---|---|---|---|
payee | string | required | Recipient name e.g. "Electric Co" |
category | string | required | e.g. "utilities", "rent" |
amount | integer | required | Positive cents value |
next_due_date | date | required | YYYY-MM-DD |
from_account_id | uuid | required | Must be owned by user |
is_recurring | boolean | optional | Default: false |
frequency | string | optional | Required if is_recurring: true |
Returns: 201 with bill object. Status defaults to ACTIVE.
Returns all bills for the authenticated user. Ordered by created_at DESC.
Can update status to: ACTIVE, PAUSED, PAID, CANCELLED. Also updatable: payee, amount, next_due_date, frequency, is_recurring.
Permanently removes the bill. Returns 200 on success, 404 if not found.
API – Loans
Base path: /api/bank/loans
| Field | Type | Required | Notes |
|---|---|---|---|
loan_reason | string | required | Purpose of loan |
loan_amount | integer | required | Cents |
term_months | integer | required | Loan term in months |
employment_status | string | required | |
annual_income | integer | required | Cents |
Returns 201 with loan application. Status defaults to PENDING.
Returns all loan applications for the authenticated user.
Sets status to APPROVED. Only works on PENDING loans. Returns 400 if loan is not PENDING.
Sets status to REJECTED. Only works on PENDING loans.
Permanently removes the loan application. Allowed for any status.
API – Messages
Base path: /api/bank/messages
Returns all bank messages for the user. Includes is_read flag, message_type (e.g. TRANSFER_SENT, TRANSFER_RECEIVED), title, and description.
Allows manual creation of a notification message for testing purposes.
Marks one or all messages as read. Pass message IDs array or mark all at once.
Test Cases
Accounts
- Create 5 CHECKING accounts (each with unique account numbers)
- Attempt to create a 6th account
- POST /api/bank/accounts with
{ "type": "MORTGAGE" }
- POST /api/bank/accounts with
{ "type": "CHECKING", "initial_deposit": 1000100 }
- Create account with initial deposit of $100 (10000 cents)
- PATCH /api/bank/accounts/:id/close
- Create account A with $200 (20000 cents)
- Create account B
- Transfer 20000 from A to B
- PATCH /api/bank/accounts/:accountA/close
- Create an OPEN account
- PATCH /api/bank/accounts/:id/remove (skip close step)
Transfers
- POST /api/bank/transfer with
from_account_id=to_account_id
- Create account with 1000 cents ($10.00)
- POST /api/bank/transfer with amount: 5000 ($50.00)
- POST /api/bank/transfer with
amount: 0 - Repeat with
amount: -100
- Account A: 50000 cents ($500), Account B: 0 cents
- Transfer 10000 cents ($100) from A to B
- GET /api/bank/accounts for both
- Close an account (ensure $0 balance first)
- Attempt to use it as from_account_id in a transfer
Bills
- POST /api/bank/bills with
is_recurring: truebut nofrequencyfield
- Create an ACTIVE bill
- PATCH /api/bank/bills/:id with
{ "status": "PAID" } - GET /api/bank/transactions
Loans
- Submit loan, then PATCH approve it
- PATCH approve it again
- POST /api/bank/loans with all required fields
- Verify status is PENDING
- PATCH /api/bank/loans/:id/approve
- GET /api/bank/loans — verify status is APPROVED
QA Tasks
Hands-on challenges for manual and automated testing of the Bank application.
Automate creation of exactly 5 accounts, then attempt a 6th. Verify the 5th succeeds and the 6th returns 400. Then close one account and verify a new 6th can be created.
Create an account with initial_deposit: 1 (1 cent = $0.01). Transfer 1 cent to another account. Verify both balances in cents and dollar format. Test transfer of 0 cents and a float value like 10.5 — document what happens.
Open 2 accounts. Move all funds from Account A to B. Close Account A → remove it → attempt to delete it (should fail: already removed). Verify Account A appears in the transaction history with status=REMOVED when queried with ?status=REMOVED.
Use two different API keys. Transfer from User A's account to User B's account. Check GET /api/bank/messages for both users. Verify User A gets TRANSFER_SENT and User B gets TRANSFER_RECEIVED messages.
Create a recurring bill (is_recurring: true, frequency: "monthly"). Verify it appears in GET /api/bank/bills. Then pause it, then cancel it. Verify status transitions are reflected correctly at each step.
Submit a loan. Reject it. Attempt to approve the rejected loan — document the response. Delete the rejected loan. Submit a new loan application and approve it. Confirm the entire REJECTED → deleted → new PENDING → APPROVED flow.
Create 3 transfers of different amounts. Mark one bill as PAID. Then: GET /api/bank/transactions?type=transfer (should return 3). GET ?type=withdrawal (should return 1). GET ?min_amount=500 (filter by minimum 500 cents). Verify filter combinations work correctly.
Make several transfers and create bills. Then call POST /api/bank/reset. Verify the response includes counts for accounts, transactions, bills, loans, and messages. Check GET /api/bank/accounts and confirm template accounts are restored with expected balances.
Create an account with a positive balance. Attempt DELETE /api/bank/accounts/:id. Verify it returns 400. Then transfer the full balance out, and DELETE again — verify 200 response. Document the exact error message and assertion.
Fund an account with exactly 10000 cents ($100). Fire two simultaneous POST /api/bank/transfer requests: each for 7000 cents from the same account. Observe if both succeed (double spend) or one fails. Document the exact behavior — this is a concurrency edge case.