Definition
- 2xx Success – 200 OK (general success), 201 Created (new resource with
Locationheader), 202 Accepted (async processing), 204 No Content (success with no body, e.g. DELETE). - 3xx Redirection – 301 Moved Permanently, 302 Found (temporary), 304 Not Modified (reuse cached copy), 307 Temporary Redirect (preserves method), 308 Permanent Redirect (preserves method).
- 4xx Client Error – 400 Bad Request (malformed), 401 Unauthorized (not authenticated), 403 Forbidden (not authorised), 404 Not Found, 405 Method Not Allowed, 409 Conflict, 422 Unprocessable Entity (business-rule failure), 429 Too Many Requests.
- 5xx Server Error – 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable (with
Retry-After), 504 Gateway Timeout.
Think of the status code as the stamp the National Bank of India clerk puts on Rajesh's request slip. A green 200 stamp means "done successfully." A 201 stamp means "a new record was created." A 400 stamp means "your form was filled wrong." A 401 means "you did not show your ID." A 403 means "you showed your ID but you are not allowed in this room." A 404 means "we looked but found no such account." A 500 means "the bank's own system crashed, not your fault." The stamp lets everyone: the app, the monitoring team, and retry robots, understand what happened at a glance.
Understanding the Concepts
What are the 2xx success codes?
The 2xx class means success. These are the codes you return when the request was processed correctly.
- 200 OK – general success for reads and updates that return a body.
- 201 Created – a POST created a new resource, usually with a
Locationheader pointing to it. - 202 Accepted – the request was received but processing happens asynchronously, useful for batch payouts.
- 204 No Content – success with no body, typical after a DELETE.
package com.bank.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.net.URI;
@RestController
@RequestMapping("/api/v1")
public class BankController {
// 200 OK: read with body.
@GetMapping("/accounts/{id}")
public ResponseEntity<AccountResponse> getAccount(@PathVariable String id) {
return ResponseEntity.ok(accountService.findById(id));
}
// 201 Created: new resource with Location header.
@PostMapping("/transfers")
public ResponseEntity<TransferResponse> createTransfer(@RequestBody TransferRequest req) {
TransferResponse resp = transferService.create(req);
return ResponseEntity.created(URI.create("/api/v1/transfers/" + resp.id()))
.body(resp);
}
// 204 No Content: successful DELETE, no body.
@DeleteMapping("/beneficiaries/{id}")
public ResponseEntity<Void> deleteBeneficiary(@PathVariable String id) {
beneficiaryService.delete(id);
return ResponseEntity.noContent().build();
}
}
What are the 3xx redirection codes?
The 3xx class means the resource moved. The important distinction is whether the method is preserved.
- 301 Moved Permanently – resource has a new permanent URL; may change POST to GET.
- 302 Found – temporary redirect; may change POST to GET.
- 304 Not Modified – the client's cached copy is still valid, reuse it; must have no body.
- 307 Temporary Redirect – preserves the HTTP method and body.
- 308 Permanent Redirect – preserves the HTTP method and body.
// 308: permanently moved, preserves POST method and body.
return ResponseEntity.status(HttpStatus.PERMANENT_REDIRECT)
.header("Location", "/api/v2/transfers")
.build();
// 304: client's cached copy is still valid.
return ResponseEntity.status(HttpStatus.NOT_MODIFIED)
.eTag("\"v7\"")
.build(); // No body!
What are the 4xx client error codes?
The 4xx class means the client made a mistake. Each code tells the client exactly what went wrong.
| Code | Meaning | Banking Example |
|---|---|---|
| 400 | Malformed request | Invalid JSON, missing required field |
| 401 | Not authenticated | Missing or expired JWT token |
| 403 | Authenticated but not permitted | Customer accessing admin endpoint |
| 404 | Resource does not exist | Account SAV9999 not found |
| 405 | Wrong HTTP method for this resource | POST on a read-only endpoint |
| 409 | State conflict | Duplicate account number |
| 422 | Business-rule failure on valid input | Insufficient funds |
| 429 | Rate limited | Client exceeded 100 requests per minute |
package com.bank.exception;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
@RestControllerAdvice
public class ClientErrorHandler {
@ExceptionHandler(AccountNotFoundException.class)
public ResponseEntity<ApiError> handleNotFound(AccountNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ApiError("ACCOUNT_NOT_FOUND", e.getMessage()));
}
@ExceptionHandler(DuplicateAccountException.class)
public ResponseEntity<ApiError> handleConflict(DuplicateAccountException e) {
return ResponseEntity.status(HttpStatus.CONFLICT)
.body(new ApiError("DUPLICATE_ACCOUNT", e.getMessage()));
}
@ExceptionHandler(InsufficientFundsException.class)
public ResponseEntity<ApiError> handleUnprocessable(InsufficientFundsException e) {
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(new ApiError("INSUFFICIENT_FUNDS", e.getMessage()));
}
}
What are the 5xx server error codes?
The 5xx class means the server failed. The client did nothing wrong.
- 500 Internal Server Error – unexpected crash: null pointer, unhandled exception.
- 502 Bad Gateway – a server acting as a gateway got an invalid response from upstream.
- 503 Service Unavailable – temporarily down or overloaded, often with
Retry-After. - 504 Gateway Timeout – an upstream server did not respond in time.
// 503 with Retry-After: tell the client when to try again.
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.header("Retry-After", "30")
.body(new ApiError("SERVICE_UNAVAILABLE", "Try again in 30 seconds"));
Why does choosing the right code matter?
The right code matters because the entire ecosystem keys off it. Monitoring dashboards count 5xx as outages and 4xx as client issues. Retry logic retries 503 and 429 but not 400. Caches respect 304. If you return 200 for everything, you blind all these tools and hide real failures.
// Wrong: 200 for everything – monitoring stays green during an outage.
@PostMapping("/transfers")
public Map<String, Object> transfer(@RequestBody TransferRequest req) {
try {
transferService.create(req);
return Map.of("success", true);
} catch (Exception e) {
return Map.of("success", false, "error", e.getMessage()); // 200!
}
}
// Correct: proper status codes – monitoring, retries, and caching all work.
@PostMapping("/transfers")
public ResponseEntity<TransferResponse> create(@RequestBody TransferRequest req) {
TransferResponse resp = transferService.create(req);
return ResponseEntity.created(URI.create("/api/v1/transfers/" + resp.id()))
.body(resp);
}
// Exceptions handled centrally by @RestControllerAdvice.
What is 400 vs 422 in detail?
400 Bad Request means the request is syntactically malformed: invalid JSON, wrong data type, missing required field. 422 Unprocessable Entity means the request is syntactically valid but fails semantic or business-rule validation: a negative transfer amount, insufficient funds, or a duplicate account.
// 400: the JSON is malformed or a required field is missing.
// POST /api/v1/transfers
// Body: {"from": "SAV2001"} ← missing "to" and "amount"
// → 400 Bad Request
// 422: the JSON is valid but the business rule fails.
// POST /api/v1/transfers
// Body: {"from": "SAV2001", "to": "SAV2002", "amount": 900000}
// Balance is only 250000.
// → 422 Unprocessable Entity, code: INSUFFICIENT_FUNDS
What is 401 vs 403 in detail?
401 Unauthorized means not authenticated: no valid credentials were provided. The client should authenticate and retry. 403 Forbidden means authenticated but not authorised: the server knows who you are, but you lack the permission. Retrying with the same credentials will not help.
// Order of checks:
// 1. Authenticate: who are you? If fail → 401.
// 2. Authorise: are you allowed? If fail → 403.
// Never return 403 to an unauthenticated client.
What should a senior engineer know about status codes?
A senior engineer knows that the difference between 400 and 422 is syntactically malformed vs semantically invalid, and the difference between 401 and 403 is not authenticated vs not authorised. Getting these right makes client handling and monitoring far more precise, and interviewers probe these edges to see if you truly understand the semantics rather than just returning 200 and 500.
Visual Overview
┌────────────────────────────────────────────────────┐ │ Common HTTP Status Codes Reference │ ├────────────────────────────────────────────────────┤ │ │ │ 2xx Success: │ │ ┌──────┬──────────────────────────────────┐ │ │ │ 200 │ OK – general success with body │ │ │ │ 201 │ Created – new resource + Location │ │ │ │ 202 │ Accepted – async processing │ │ │ │ 204 │ No Content – success, no body │ │ │ └──────┴──────────────────────────────────┘ │ │ │ │ 3xx Redirection: │ │ ┌──────┬──────────────────────────────────┐ │ │ │ 301 │ Moved Permanently (may drop POST) │ │ │ │ 304 │ Not Modified (reuse cache, no body)│ │ │ │ 307 │ Temporary Redirect (keeps method) │ │ │ │ 308 │ Permanent Redirect (keeps method) │ │ │ └──────┴──────────────────────────────────┘ │ │ │ │ 4xx Client Error: │ │ ┌──────┬──────────────────────────────────┐ │ │ │ 400 │ Bad Request (malformed) │ │ │ │ 401 │ Unauthorized (not authenticated) │ │ │ │ 403 │ Forbidden (not authorised) │ │ │ │ 404 │ Not Found │ │ │ │ 409 │ Conflict (duplicate/state clash) │ │ │ │ 422 │ Unprocessable (business rule) │ │ │ │ 429 │ Too Many Requests (rate limited) │ │ │ └──────┴──────────────────────────────────┘ │ │ │ │ 5xx Server Error: │ │ ┌──────┬──────────────────────────────────┐ │ │ │ 500 │ Internal Server Error │ │ │ │ 502 │ Bad Gateway (upstream invalid) │ │ │ │ 503 │ Service Unavailable (temp down) │ │ │ │ 504 │ Gateway Timeout (upstream slow) │ │ │ └──────┴──────────────────────────────────┘ │ └────────────────────────────────────────────────────┘
Key Takeaway
HTTP status codes are three-digit numbers that summarise the outcome of a request, grouped into five classes. The 2xx class means success: 200 OK for general success, 201 Created for a new resource with a Location header, 202 Accepted for async processing, and 204 No Content for success with no body. The 3xx class means redirection, with 301 and 308 for permanent moves, 304 for cache reuse, and 307 for temporary redirect with method preservation.
The codes used most in banking are the 4xx family, because they tell the client exactly what went wrong. 400 is for a malformed request, 401 is for not authenticated, 403 is for not authorised, 404 is for missing, 409 is for conflict, 422 is for a business-rule failure, and 429 is for rate limiting. On the server side, 500 is an unexpected crash, 502 and 504 relate to upstream gateway problems, and 503 signals a temporary outage with a Retry-After header.
The reason to take these seriously comes from a real problem at National Bank of India. The old transfer API returned 200 for everything, even failures, with a body saying {"success": false}. The monitoring dashboard showed a perfectly green graph while customers complained transfers were failing, because dashboards count the HTTP status, not the body. The fix was returning the correct status for each outcome, so a failed transfer now returns a 4xx or 5xx that monitoring and retry logic can see and act on.
The distinctions interviewers probe are the edges: 400 vs 422 is malformed vs semantically invalid; 401 vs 403 is not authenticated vs not authorised. Getting these right makes client handling precise, keeps monitoring honest, and lets retry logic retry a 503 or 429 while never retrying a 400. In a banking system, that precision is exactly the kind of correctness that matters.
Remember one sentence: the HTTP status code is the one field the entire ecosystem reads, so choosing the right one makes monitoring, caching, retry logic, and client error handling all work correctly.
---