Complete API reference for the Credit Card Optimizer backend.
Development: http://localhost:3000
Production: https://your-backend-domain.com
The API uses versioned endpoints to ensure backward compatibility:
/api/v1/* - Current stable version
/api/* - Legacy (redirects to v1, deprecated)
Deprecation headers on legacy endpoints:
X-API-Deprecated: true
X-API-Version: v1
Deprecation: version="legacy"
Sunset: <date 90 days from now>
Most endpoints require JWT authentication:
Authorization: Bearer <your_jwt_token>Get token by:
- Register:
POST /api/v1/auth/register - Login:
POST /api/v1/auth/login
Token expiration: 7 days
All state-changing endpoints require:
Content-Type: application/json| Endpoint Type | Limit | Window |
|---|---|---|
| Authentication | 5 requests | 15 minutes |
| Plaid API | 20 requests | 1 minute |
| General API | 100 requests | 15 minutes |
Rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1234567890Rate limited response (429):
{
"error": "Too many requests, please try again later"
}{
"data": { ... },
"message": "Success"
}Or just the data object directly.
{
"error": "Error message",
"details": [
{
"field": "email",
"message": "Invalid email address"
}
]
}| Code | Meaning |
|---|---|
| 200 | OK - Request successful |
| 201 | Created - Resource created successfully |
| 400 | Bad Request - Invalid input/validation error |
| 401 | Unauthorized - Missing or invalid token |
| 403 | Forbidden - Valid token but insufficient permissions |
| 404 | Not Found - Resource doesn't exist |
| 429 | Too Many Requests - Rate limited |
| 500 | Internal Server Error - Server error |
- POST /api/v1/auth/register - Create new user
- POST /api/v1/auth/login - Login user
- GET /api/v1/auth/profile - Get user profile
- POST /api/v1/plaid/create_link_token - Create Plaid Link token
- POST /api/v1/plaid/exchange_public_token - Exchange public token
- GET /api/v1/plaid/accounts - Get accounts
- POST /api/v1/plaid/transactions - Get transactions
- DELETE /api/v1/plaid/items/:itemId - Remove Plaid item
- POST /api/v1/plaid/webhook - Plaid webhook handler
- GET /api/v1/cards - Get all credit cards
- GET /api/v1/cards/:accountId - Get specific card
- POST /api/v1/cards - Save/update card configuration
- DELETE /api/v1/cards/:accountId - Delete card configuration
- GET /api/v1/security/certificate-pins - Get certificate pins
- GET /health - Server health check (no auth required)
Content-Type: application/json
Authorization: Bearer <token>Content-Type: application/json
X-API-Version: v1
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1234567890Security headers (from Helmet):
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block{
"error": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email address"
},
{
"field": "password",
"message": "Password must be at least 8 characters"
}
]
}{
"error": "Invalid or expired token"
}{
"error": "Not found",
"path": "/api/v1/unknown"
}{
"error": "Internal server error"
}Details are hidden in production to prevent information leakage.
For endpoints that return large datasets (future):
Request:
GET /api/v1/transactions?page=2&limit=50Response:
{
"data": [...],
"pagination": {
"page": 2,
"limit": 50,
"total": 500,
"pages": 10
}
}App implements smart caching:
- Accounts cached for 5 minutes
- Transactions cached for 1 hour
- Liabilities cached for 1 day
Backend uses in-memory cache:
- Statement date analysis cached for 30 days
- LLM results cached for 30 days
Cache headers:
X-Cache-Strategy: cache-only
X-Rate-Limit-Info: Using cached data due to rate limitPlaid sends webhooks for account events:
Verification:
- HMAC-SHA256 signature in
Plaid-Verificationheader - Validates against raw request body
Events handled:
ITEM.ERROR- Item errorITEM.PENDING_EXPIRATION- Item about to expireTRANSACTIONS.DEFAULT_UPDATE- New transactions availableAUTH.AUTOMATICALLY_VERIFIED- Auth verified
See Plaid Webhooks for details.
Check if server is running (no authentication required).
Response: 200 OK
{
"status": "ok",
"message": "Credit Card Optimizer Backend is running",
"timestamp": "2025-10-13T12:00:00.000Z",
"environment": "sandbox",
"apiVersion": "v1",
"supportedVersions": ["v1"]
}# Register
curl -X POST http://localhost:3000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "TestPass123"
}'
# Login
curl -X POST http://localhost:3000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "TestPass123"
}'# Save token from login response
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# Get profile
curl http://localhost:3000/api/v1/auth/profile \
-H "Authorization: Bearer $TOKEN"
# Create link token
curl -X POST http://localhost:3000/api/v1/plaid/create_link_token \
-H "Authorization: Bearer $TOKEN"
# Get accounts
curl http://localhost:3000/api/v1/plaid/accounts \
-H "Authorization: Bearer $TOKEN"Import collection:
{
"info": {
"name": "CC Optimizer API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{jwt_token}}",
"type": "string"
}
]
},
"variable": [
{
"key": "base_url",
"value": "http://localhost:3000/api/v1"
},
{
"key": "jwt_token",
"value": ""
}
]
}None yet - Use HTTP client of your choice
Recommended:
- Android: Retrofit + OkHttp (used in this project)
- iOS: URLSession / Alamofire
- Web: fetch / axios
- Node.js: axios / got
- Python: requests / httpx
interface CreditCardApi {
@POST("auth/register")
suspend fun register(@Body request: RegisterRequest): AuthResponse
@POST("auth/login")
suspend fun login(@Body request: LoginRequest): AuthResponse
@GET("auth/profile")
suspend fun getProfile(): UserProfile
@POST("plaid/create_link_token")
suspend fun createLinkToken(): LinkTokenResponse
@GET("plaid/accounts")
suspend fun getAccounts(): List<Account>
}
val retrofit = Retrofit.Builder()
.baseUrl("http://localhost:3000/api/v1/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val api = retrofit.create(CreditCardApi::class.java)// Development
private const val BASE_URL = "http://localhost:3000/"
// Production
private const val BASE_URL = "https://api.your-domain.com/"suspend fun <T> retryRequest(
times: Int = 3,
delay: Long = 1000,
block: suspend () -> T
): T {
repeat(times - 1) { attempt ->
try {
return block()
} catch (e: IOException) {
delay(delay * (attempt + 1))
}
}
return block() // Last attempt
}if (response.code() == 429) {
// Serve cached data
return cachedData
}fun validateEmail(email: String): Boolean {
return Patterns.EMAIL_ADDRESS.matcher(email).matches()
}
fun validatePassword(password: String): Boolean {
return password.length >= 8 &&
password.any { it.isUpperCase() } &&
password.any { it.isLowerCase() } &&
password.any { it.isDigit() }
}// Use EncryptedSharedPreferences
val encryptedPrefs = EncryptedSharedPreferences.create(...)
encryptedPrefs.edit {
putString("jwt_token", token)
}- Authentication API - Auth endpoints
- Plaid API - Bank integration endpoints
- Card Management API - Credit card configuration
- Error Handling - Error codes and handling
For API questions or issues:
- Check this documentation
- Review FAQ
- Check Troubleshooting Guide
API Version: v1
Last Updated: October 2025