507 lines
15 KiB
Markdown
507 lines
15 KiB
Markdown
# BankFlow API Reference
|
||
|
||
This document describes the HTTP API contract for BankFlow. It is based on the requirements in [bank-flow.md](./bank-flow.md).
|
||
|
||
## Overview
|
||
|
||
All loan routes are versioned under `/api/v1`. The health-check route is not versioned.
|
||
|
||
### Routes
|
||
|
||
| Method | Route | Description | Success status |
|
||
|--------|----------------------------------|----------------------------|----------------|
|
||
| `POST` | `/api/v1/loans` | Create a loan application | `201 Created` |
|
||
| `POST` | `/api/v1/loans/{loanId}/process` | Process a loan application | `200 OK` |
|
||
| `GET` | `/api/v1/loans/{loanId}` | Get a loan application | `200 OK` |
|
||
| `GET` | `/api/v1/loans/{loanId}/history` | Get processing history | `200 OK` |
|
||
| `GET` | `/health` | Check service health | `200 OK` |
|
||
|
||
## General conventions
|
||
|
||
### Content type
|
||
|
||
Requests with a body must use JSON. Every response must include:
|
||
|
||
```http
|
||
Content-Type: application/json
|
||
```
|
||
|
||
### Timestamps
|
||
|
||
All timestamps use ISO 8601 format in UTC:
|
||
|
||
```text
|
||
2026-07-15T10:00:00Z
|
||
```
|
||
|
||
### Loan types
|
||
|
||
| Value | Description |
|
||
|------------|---------------|
|
||
| `PERSONAL` | Personal loan |
|
||
| `BUSINESS` | Business loan |
|
||
|
||
### Loan statuses
|
||
|
||
| Status | Description | Terminal for automatic processing |
|
||
|-----------------|----------------------------------------------------|-----------------------------------|
|
||
| `SUBMITTED` | The application has been created but not processed | No |
|
||
| `IN_PROGRESS` | The application is being processed | No |
|
||
| `MANUAL_REVIEW` | The application requires a manual decision | Yes |
|
||
| `APPROVED` | The application has been approved | Yes |
|
||
| `REJECTED` | The application has been rejected | Yes |
|
||
|
||
### Loan stages
|
||
|
||
| Stage | Description |
|
||
|--------------------|-----------------------------------------|
|
||
| `VALIDATION` | Validate the submitted application data |
|
||
| `FRAUD_CHECK` | Perform the mocked fraud check |
|
||
| `GUARANTOR_CHECK` | Check for a guarantor on business loans |
|
||
| `CREDIT_CHECK` | Evaluate the applicant's credit score |
|
||
| `MANAGER_APPROVAL` | Apply the mocked manager-approval rule |
|
||
|
||
### Stage result type
|
||
|
||
| Result | Effect |
|
||
|-----------------|------------------------------------------------------------|
|
||
| `PASS` | Continue to the next applicable stage |
|
||
| `FAIL` | Stop processing and set the loan status to `REJECTED` |
|
||
| `MANUAL_REVIEW` | Stop processing and set the loan status to `MANUAL_REVIEW` |
|
||
|
||
## Create a loan application
|
||
|
||
Creates and stores a new loan application. Creating an application does not execute its workflow.
|
||
|
||
```http
|
||
POST /api/v1/loans
|
||
```
|
||
|
||
### Request body
|
||
|
||
| Field | Type | Required | Rules |
|
||
|-----------------|-----------|----------|----------------------------------------------------|
|
||
| `customerId` | `string` | Yes | Must not be empty |
|
||
| `amount` | `integer` | Yes | Must be greater than `0` |
|
||
| `phone` | `string` | Yes | Must contain exactly 11 digits and start with `09` |
|
||
| `loanType` | `string` | Yes | Must be `PERSONAL` or `BUSINESS` |
|
||
| `monthlyIncome` | `integer` | Yes | Must be greater than or equal to `0` |
|
||
| `creditScore` | `integer` | Yes | Must be between `0` and `1000`, inclusive |
|
||
| `hasGuarantor` | `boolean` | Yes | Indicates whether the applicant has a guarantor |
|
||
|
||
```json
|
||
{
|
||
"customerId": "C-1001",
|
||
"amount": 400000000,
|
||
"phone": "09121234567",
|
||
"loanType": "PERSONAL",
|
||
"monthlyIncome": 50000000,
|
||
"creditScore": 720,
|
||
"hasGuarantor": false
|
||
}
|
||
```
|
||
|
||
### Processing rules
|
||
|
||
- The service generates a unique `loanId`.
|
||
- The initial status is `SUBMITTED`.
|
||
- The initial workflow stage is `VALIDATION`.
|
||
- Business validation is deferred until the workflow is processed.
|
||
- An invalid field value does not produce an HTTP `400` response during creation. It causes the `VALIDATION` stage to fail when the application is processed.
|
||
- A malformed JSON document produces an HTTP `400` response.
|
||
|
||
### Success response
|
||
|
||
```http
|
||
HTTP/1.1 201 Created
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"loanId": "L-10001",
|
||
"status": "SUBMITTED",
|
||
"currentStage": "VALIDATION"
|
||
}
|
||
```
|
||
|
||
#### Response fields
|
||
|
||
| Field | Type | Description |
|
||
|----------------|----------|------------------------------------------------|
|
||
| `loanId` | `string` | Unique identifier generated by the service |
|
||
| `status` | `string` | Current loan status; initially `SUBMITTED` |
|
||
| `currentStage` | `string` | Current workflow stage; initially `VALIDATION` |
|
||
|
||
### Malformed JSON response
|
||
|
||
```http
|
||
HTTP/1.1 400 Bad Request
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"error": "INVALID_REQUEST"
|
||
}
|
||
```
|
||
|
||
## Process a loan application
|
||
|
||
Processes an application from its current workflow stage until it reaches a terminal automatic-processing status.
|
||
|
||
```http
|
||
POST /api/v1/loans/{loanId}/process
|
||
```
|
||
|
||
### Path parameters
|
||
|
||
| Parameter | Type | Required | Description |
|
||
|-----------|----------|----------|------------------------------------|
|
||
| `loanId` | `string` | Yes | Unique loan application identifier |
|
||
|
||
This route does not accept a request body.
|
||
|
||
### Workflow rules
|
||
|
||
All applications begin with the following stages:
|
||
|
||
```text
|
||
VALIDATION → FRAUD_CHECK
|
||
```
|
||
|
||
The remaining route depends on the loan type:
|
||
|
||
```text
|
||
PERSONAL:
|
||
VALIDATION → FRAUD_CHECK → CREDIT_CHECK → [MANAGER_APPROVAL] → APPROVED
|
||
|
||
BUSINESS:
|
||
VALIDATION → FRAUD_CHECK → GUARANTOR_CHECK → CREDIT_CHECK
|
||
→ [MANAGER_APPROVAL] → APPROVED
|
||
```
|
||
|
||
`MANAGER_APPROVAL` runs only when `amount` is greater than the configured `managerApprovalThreshold`.
|
||
|
||
Processing stops immediately when a stage returns `FAIL` or `MANUAL_REVIEW`.
|
||
|
||
### Idempotency rules
|
||
|
||
This operation must be idempotent.
|
||
|
||
If the application is already `APPROVED`, `REJECTED`, or `MANUAL_REVIEW`:
|
||
|
||
- no workflow stage is executed again;
|
||
- no duplicate history record is created;
|
||
- the existing status is not changed;
|
||
- the current application state is returned with `200 OK`.
|
||
|
||
### Approved response
|
||
|
||
```http
|
||
HTTP/1.1 200 OK
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"loanId": "L-10001",
|
||
"status": "APPROVED",
|
||
"currentStage": null
|
||
}
|
||
```
|
||
|
||
### Rejected response
|
||
|
||
```http
|
||
HTTP/1.1 200 OK
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"loanId": "L-10001",
|
||
"status": "REJECTED",
|
||
"currentStage": null
|
||
}
|
||
```
|
||
|
||
### Manual-review response
|
||
|
||
```http
|
||
HTTP/1.1 200 OK
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"loanId": "L-10001",
|
||
"status": "MANUAL_REVIEW",
|
||
"currentStage": null
|
||
}
|
||
```
|
||
|
||
### Loan-not-found response
|
||
|
||
```http
|
||
HTTP/1.1 404 Not Found
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"error": "LOAN_NOT_FOUND"
|
||
}
|
||
```
|
||
|
||
## Get a loan application
|
||
|
||
Returns the submitted data and current processing state of a loan application.
|
||
|
||
```http
|
||
GET /api/v1/loans/{loanId}
|
||
```
|
||
|
||
### Path parameters
|
||
|
||
| Parameter | Type | Required | Description |
|
||
| --- | --- | --- | --- |
|
||
| `loanId` | `string` | Yes | Unique loan application identifier |
|
||
|
||
### Success response
|
||
|
||
```http
|
||
HTTP/1.1 200 OK
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"loanId": "L-10001",
|
||
"customerId": "C-1001",
|
||
"amount": 400000000,
|
||
"phone": "09121234567",
|
||
"loanType": "PERSONAL",
|
||
"monthlyIncome": 50000000,
|
||
"creditScore": 720,
|
||
"hasGuarantor": false,
|
||
"status": "APPROVED",
|
||
"currentStage": null,
|
||
"createdAt": "2026-07-15T10:00:00Z",
|
||
"updatedAt": "2026-07-15T10:00:03Z"
|
||
}
|
||
```
|
||
|
||
### Response fields
|
||
|
||
| Field | Type | Nullable | Description |
|
||
|-----------------|-----------|----------|-------------------------------------------------------|
|
||
| `loanId` | `string` | No | Unique loan application identifier |
|
||
| `customerId` | `string` | No | Customer identifier |
|
||
| `amount` | `integer` | No | Requested loan amount |
|
||
| `phone` | `string` | No | Applicant's mobile number |
|
||
| `loanType` | `string` | No | Loan type |
|
||
| `monthlyIncome` | `integer` | No | Applicant's monthly income |
|
||
| `creditScore` | `integer` | No | Applicant's credit score |
|
||
| `hasGuarantor` | `boolean` | No | Whether the applicant has a guarantor |
|
||
| `status` | `string` | No | Current loan status |
|
||
| `currentStage` | `string` | Yes | Current workflow stage; `null` after processing stops |
|
||
| `createdAt` | `string` | No | Creation timestamp in ISO 8601 UTC format |
|
||
| `updatedAt` | `string` | No | Last-update timestamp in ISO 8601 UTC format |
|
||
|
||
### Loan-not-found response
|
||
|
||
```http
|
||
HTTP/1.1 404 Not Found
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"error": "LOAN_NOT_FOUND"
|
||
}
|
||
```
|
||
|
||
## Get processing history
|
||
|
||
Returns workflow stage executions in chronological order.
|
||
|
||
```http
|
||
GET /api/v1/loans/{loanId}/history
|
||
```
|
||
|
||
### Path parameters
|
||
|
||
| Parameter | Type | Required | Description |
|
||
|-----------|----------|----------|------------------------------------|
|
||
| `loanId` | `string` | Yes | Unique loan application identifier |
|
||
|
||
### History rules
|
||
|
||
- Records are ordered by `timestamp` from oldest to newest.
|
||
- A workflow stage is recorded at most once for an application.
|
||
- Reprocessing a terminal application does not add history records.
|
||
- `reason` contains `SUCCESS` for a successful stage or the applicable business error code for a failed validation.
|
||
|
||
### Success response
|
||
|
||
```http
|
||
HTTP/1.1 200 OK
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
[
|
||
{
|
||
"stage": "VALIDATION",
|
||
"result": "PASS",
|
||
"timestamp": "2026-07-15T10:00:00Z",
|
||
"reason": "SUCCESS"
|
||
},
|
||
{
|
||
"stage": "FRAUD_CHECK",
|
||
"result": "PASS",
|
||
"timestamp": "2026-07-15T10:00:01Z",
|
||
"reason": "SUCCESS"
|
||
},
|
||
{
|
||
"stage": "CREDIT_CHECK",
|
||
"result": "PASS",
|
||
"timestamp": "2026-07-15T10:00:02Z",
|
||
"reason": "SUCCESS"
|
||
}
|
||
]
|
||
```
|
||
|
||
### History record fields
|
||
|
||
| Field | Type | Description |
|
||
|-------------|----------|----------------------------------------------------------|
|
||
| `stage` | `string` | Name of the executed workflow stage |
|
||
| `result` | `string` | `PASS`, `FAIL`, or `MANUAL_REVIEW` |
|
||
| `timestamp` | `string` | Execution time in ISO 8601 UTC format |
|
||
| `reason` | `string` | Reason or business error code associated with the result |
|
||
|
||
### Loan-not-found response
|
||
|
||
```http
|
||
HTTP/1.1 404 Not Found
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"error": "LOAN_NOT_FOUND"
|
||
}
|
||
```
|
||
|
||
## Health check
|
||
|
||
Reports whether the service is available.
|
||
|
||
```http
|
||
GET /health
|
||
```
|
||
|
||
### Success response
|
||
|
||
```http
|
||
HTTP/1.1 200 OK
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"status": "UP"
|
||
}
|
||
```
|
||
|
||
## Business rules
|
||
|
||
### Validation
|
||
|
||
The `VALIDATION` stage applies the following rules:
|
||
|
||
| Field | Rule | Failure reason |
|
||
|-----------------|---------------------------------------------------------------|--------------------------|
|
||
| `customerId` | Required and not empty | `INVALID_CUSTOMER_ID` |
|
||
| `amount` | Greater than `0` | `INVALID_AMOUNT` |
|
||
| `phone` | Exactly 11 digits, starts with `09`, and contains digits only | `INVALID_PHONE` |
|
||
| `loanType` | `PERSONAL` or `BUSINESS` | `INVALID_LOAN_TYPE` |
|
||
| `monthlyIncome` | Greater than or equal to `0` | `INVALID_MONTHLY_INCOME` |
|
||
| `creditScore` | Between `0` and `1000`, inclusive | `INVALID_CREDIT_SCORE` |
|
||
|
||
Any validation failure produces a `FAIL` stage result and changes the application status to `REJECTED`.
|
||
|
||
### Fraud check
|
||
|
||
The `FRAUD_CHECK` stage is mocked using `customerId`:
|
||
|
||
| Rule | Result |
|
||
|----------------------|-----------------|
|
||
| Starts with `FRAUD` | `FAIL` |
|
||
| Starts with `REVIEW` | `MANUAL_REVIEW` |
|
||
| Any other value | `PASS` |
|
||
|
||
### Guarantor check
|
||
|
||
The `GUARANTOR_CHECK` stage runs only for `BUSINESS` loans:
|
||
|
||
| Rule | Result |
|
||
|---------------------------|--------|
|
||
| `hasGuarantor` is `false` | `FAIL` |
|
||
| `hasGuarantor` is `true` | `PASS` |
|
||
|
||
### Credit check
|
||
|
||
The default credit-score rules are:
|
||
|
||
| Rule | Result |
|
||
|----------------------------|-----------------|
|
||
| `creditScore < 500` | `FAIL` |
|
||
| `500 <= creditScore < 650` | `MANUAL_REVIEW` |
|
||
| `creditScore >= 650` | `PASS` |
|
||
|
||
The score boundaries must be loaded from configuration and must not be hard-coded.
|
||
|
||
### Manager approval
|
||
|
||
`MANAGER_APPROVAL` runs when the requested amount is greater than the configured `managerApprovalThreshold`.
|
||
|
||
| Rule | Result |
|
||
|---------------------------------------------|--------|
|
||
| `amount > monthlyIncome × incomeMultiplier` | `FAIL` |
|
||
| Otherwise | `PASS` |
|
||
|
||
Both `managerApprovalThreshold` and `incomeMultiplier` must be loaded from configuration.
|
||
|
||
## Error codes
|
||
|
||
### HTTP errors
|
||
|
||
| HTTP status | Error code | Description |
|
||
|-------------------|-------------------|------------------------------------------------------|
|
||
| `400 Bad Request` | `INVALID_REQUEST` | The request body is not valid JSON |
|
||
| `404 Not Found` | `LOAN_NOT_FOUND` | No loan application exists for the supplied `loanId` |
|
||
|
||
HTTP error responses use this shape:
|
||
|
||
```json
|
||
{
|
||
"error": "LOAN_NOT_FOUND"
|
||
}
|
||
```
|
||
|
||
### Business validation errors
|
||
|
||
Business validation errors do not produce HTTP `400` responses during loan creation. They are recorded as the `reason` of the failed `VALIDATION` history entry.
|
||
|
||
| Error code | Description |
|
||
|--------------------------|------------------------------------|
|
||
| `INVALID_AMOUNT` | The requested amount is invalid |
|
||
| `INVALID_PHONE` | The mobile number is invalid |
|
||
| `INVALID_CUSTOMER_ID` | The customer identifier is invalid |
|
||
| `INVALID_LOAN_TYPE` | The loan type is invalid |
|
||
| `INVALID_CREDIT_SCORE` | The credit score is invalid |
|
||
| `INVALID_MONTHLY_INCOME` | The monthly income is invalid |
|