DDC Backend API
A RESTful API backend for a debt collection agency built with AdonisJS 6 and PostgreSQL. This system is designed for global, email-centric debt collection with OTP-based authentication support.
Features
- Debtors Management: Create, read, update, and delete debtor records with international address support
- Debts Management: Track debts with currency support, P2P scoring, and AI segmentation
- Payments Tracking: Record and manage payments with multi-currency support
- OTP Authentication: Mobile phone-based login system with one-time passwords and JWT tokens
- Debtor Portal: Self-service portal for debtors to view their debts and profile
- AI Segmentation: Segment debtors based on P2P scores and balance thresholds
- Communication Logging: Track all communications (email, SMS, voice calls)
- Communication Templates: Manage reusable templates for automated communications
- Digital Activity Tracking: Monitor frontend user interactions
- Strategy Rules: Automation rules for different segments and channels with time-based triggers
- Dashboard: Real-time statistics and analytics
- RESTful API: Clean REST endpoints for all resources
- Request Validation: Input validation using VineJS validators
- Database Migrations: Version-controlled database schema with UUID primary keys
Tech Stack
- Framework: AdonisJS 6
- Database: PostgreSQL
- Language: TypeScript
- Validation: VineJS
Prerequisites
- Node.js >= 18.0.0
- PostgreSQL database (remote or local)
- npm or yarn
Installation
- Clone the repository and navigate to the project directory:
cd ddc-backend
- Install dependencies:
npm install
- Copy the environment example file and configure it:
cp .env.example .env
- Copy
.env.exampleto.envand update if needed:
cp .env.example .env
The .env.example file already contains the Neon PostgreSQL connection details. If you need to change them, edit the .env file:
PORT=3333
HOST=0.0.0.0
NODE_ENV=development
APP_KEY=your-app-key-here
APP_NAME=Debt Collection Agency API
APP_URL=https://ddc-api.streamlinelab.co # Production URL (optional, defaults to http://HOST:PORT)
# Database Configuration (Neon PostgreSQL)
DB_CONNECTION=pg
DB_HOST=ep-long-hall-ag615knm-pooler.c-2.eu-central-1.aws.neon.tech
DB_PORT=5432
DB_USER=neondb_owner
DB_PASSWORD=npg_zbYUF45ZoKSr
DB_DATABASE=neondb
DB_SSL=true # Required for Neon PostgreSQL
- Generate an application key:
node ace generate:key
Copy the generated key to APP_KEY in your .env file.
- Run database migrations:
npm run migration:run
Running the Application
Development Mode
npm run dev
The API will be available at http://localhost:3333
Production Mode
npm run build
npm start
Production URL: https://ddc-api.streamlinelab.co/
Make sure to set the APP_URL environment variable in production:
APP_URL=https://ddc-api.streamlinelab.co
This is important for email tracking URLs and other absolute URL generation.
API Endpoints
Health Check
GET /health- Check API health status
Debtors
GET /debtors- List all debtors (supports pagination, search, country filtering)POST /debtors- Create a new debtor (email is required and unique)GET /debtors/:id- Get a specific debtor with debts, communication history, and digital activitiesPUT /debtors/:id- Update a debtorDELETE /debtors/:id- Delete a debtor
Query Parameters for GET /debtors:
page- Page number (default: 1)limit- Items per page (default: 20)country_code- Filter by ISO 3166-1 alpha-2 country code (e.g., 'GB', 'US', 'IN')search- Search by first name, last name, email, or legal entity
Response includes:
- Each debtor includes
totalOutstandingAmount(sum of all current balances) - Debtor details include
legalEntityandmobilePhonefields GET /debtors/:idresponse includes:communicationLogs- All communication logs from all debts (sorted by timestamp desc)digitalActivities- All digital activities from all debts (sorted by timestamp desc)totals- Object withtotalOriginalBalanceandtotalCurrentBalance
Debts
GET /debts- List all debts (supports pagination, filtering by status, debtor, segment)POST /debts- Create a new debt (with currency, P2P score, segment support)GET /debts/:id- Get a specific debt with debtor, segment, and paymentsPUT /debts/:id- Update a debtDELETE /debts/:id- Delete a debtGET /debtors/:debtorId/debts- Get all debts for a specific debtor
Query Parameters for GET /debts:
page- Page number (default: 1)limit- Items per page (default: 20)status- Filter by current statusdebtor_id- Filter by debtor ID (UUID)segment_id- Filter by segment ID (UUID)
Payments
GET /payments- List all payments (supports pagination, filtering by debt, status)POST /payments- Create a new payment (automatically updates debt balance)GET /payments/:id- Get a specific payment with debt detailsPUT /payments/:id- Update a payment (adjusts debt balance if needed)DELETE /payments/:id- Delete a payment (restores debt balance)GET /debts/:debtId/payments- Get all payments for a specific debt
Query Parameters for GET /payments:
page- Page number (default: 1)limit- Items per page (default: 20)debt_id- Filter by debt ID (UUID)status- Filter by payment status
Dashboard
GET /dashboard/stats- Get dashboard statistics
Response includes:
totalDebtors- Total number of debtorstotalActiveDebts- Total number of active debtstotalDebtAmount- Sum of all current debt balancestotalDebtAmountOverdue- Sum of overdue debt balances
Communication Templates
GET /communication-templates- List all communication templates (supports pagination and filtering)POST /communication-templates- Create a new communication templateGET /communication-templates/:id- Get a specific templatePUT /communication-templates/:id- Update a templateDELETE /communication-templates/:id- Delete a templatePOST /communication-templates/process- Process and optionally send templates
Query Parameters for GET /communication-templates:
page- Page number (default: 1)limit- Items per page (default: 20)communication_type- Filter by type (e.g., 'email', 'sms', 'call_script')is_active- Filter by active status (true/false)search- Search by template name or description
Request Body for POST /communication-templates/process:
template_name(optional) - Process specific template by namecommunication_type(optional) - Filter by communication typedebtor_id(optional) - Debtor ID for template renderingdebt_id(optional) - Debt ID for template renderingsend(optional, default: false) - If true, actually send the communication
Debtor Authentication (Public)
POST /debtors/auth/request-otp- Request OTP code (sends to mobile phone)POST /debtors/auth/verify-otp- Verify OTP and receive JWT token
Request Body for POST /debtors/auth/request-otp:
{
"mobilePhone": "+1234567890"
}
Request Body for POST /debtors/auth/verify-otp:
{
"mobilePhone": "+1234567890",
"otpCode": "1234"
}
Response from verify-otp includes:
token- JWT token for authenticated sessions (valid for 7 days)debtor- Basic debtor information
Debtor Portal (Protected - Requires JWT Authentication)
All endpoints require Authorization: Bearer <jwt_token> header.
GET /debtors/me/profile- Get authenticated debtor's profileGET /debtors/me/debts- Get authenticated debtor's debts with totals
Response from GET /debtors/me/debts includes:
debts- Array of all debts with segment informationtotals- Object withtotalOriginalBalanceandtotalCurrentBalance
Debtor Deferral Requests (Protected - Requires JWT Authentication)
All endpoints require Authorization: Bearer <jwt_token> header.
POST /debtors/me/deferral-requests- Submit a deferral request for a debtGET /debtors/me/deferral-requests- Get all deferral requests for the authenticated debtor
Request Body for POST /debtors/me/deferral-requests:
{
"debtId": "550e8400-e29b-41d4-a716-446655440001",
"requestedDeferralDays": 30,
"reason": "I am experiencing temporary financial hardship and need additional time to arrange payment."
}
Response includes:
requestId- Unique identifier for the deferral requestdebtId- The debt being deferredrequestedDeferralDays- Number of days requested (1-365)reason- Reason provided by debtorstatus- Current status: 'pending', 'approved', or 'rejected'reviewedAt- Timestamp when reviewed (if reviewed)reviewNotes- Notes from reviewer (if reviewed)createdAt- When the request was submitted
Validation Rules:
debtIdmust be a valid UUID and belong to the authenticated debtorrequestedDeferralDaysmust be between 1 and 365reasonmust be between 10 and 1000 characters- Only one pending request per debt is allowed
Deferral Requests Management (Backoffice)
Endpoints for managing deferral requests (requires backoffice authentication).
GET /deferral-requests- List all deferral requests (with optional filters)GET /deferral-requests/:id- Get a specific deferral requestPUT /deferral-requests/:id/approve- Approve a deferral requestPUT /deferral-requests/:id/reject- Reject a deferral request
Query Parameters for GET /deferral-requests:
status- Filter by status ('pending', 'approved', 'rejected')debtorId- Filter by debtor ID (UUID)debtId- Filter by debt ID (UUID)
Request Body for PUT /deferral-requests/:id/approve:
{
"status": "approved",
"reviewNotes": "Approved based on debtor's payment history and current circumstances."
}
Request Body for PUT /deferral-requests/:id/reject:
{
"status": "rejected",
"reviewNotes": "Rejected due to insufficient documentation."
}
Response includes:
- Full request details with debtor and debt information
- Reviewer information (if reviewed)
- Review timestamp and notes
Database Schema
The database uses UUID primary keys and supports:
- Global/International: Country codes, international address format
- Email-Centric: Email is the primary unique identifier for debtors
- Multi-Currency: All monetary values support currency codes (ISO 4217)
- AI Features: P2P scoring, segmentation, strategy rules
- OTP Support: Email-based authentication with one-time passwords
Key Tables:
debtor- Debtor information with international address support (includeslegalEntityandmobilePhone)debt- Debt accounts with currency, P2P scores, and segmentspayment- Payments with currency and processor transaction IDsotp_session- OTP codes for mobile phone-based loginsegment- AI segmentation rules with days since placement filterscommunication_log- Email, SMS, and voice call communication trackingcommunication_templates- Reusable templates for automated communicationsdigital_activity- Frontend user activity tracking (linked to debtor)deferral_requests- Debt deferral requests from debtors with approval workflowstrategy_rule- Automation rules for segments with time-based triggersUSER- Agents and admin users
Example API Requests
Request OTP for Debtor Login
# Development
curl -X POST http://localhost:3333/debtors/auth/request-otp \
-H "Content-Type: application/json" \
-d '{
"mobilePhone": "+1234567890"
}'
# Production
curl -X POST https://ddc-api.streamlinelab.co/debtors/auth/request-otp \
-H "Content-Type: application/json" \
-d '{
"mobilePhone": "+1234567890"
}'
Verify OTP and Get JWT Token
# Development
curl -X POST http://localhost:3333/debtors/auth/verify-otp \
-H "Content-Type: application/json" \
-d '{
"mobilePhone": "+1234567890",
"otpCode": "1234"
}'
# Production
curl -X POST https://ddc-api.streamlinelab.co/debtors/auth/verify-otp \
-H "Content-Type: application/json" \
-d '{
"mobilePhone": "+1234567890",
"otpCode": "1234"
}'
Get Debtor's Debts (Protected)
# Development
curl -X GET http://localhost:3333/debtors/me/debts \
-H "Authorization: Bearer <jwt_token>"
# Production
curl -X GET https://ddc-api.streamlinelab.co/debtors/me/debts \
-H "Authorization: Bearer <jwt_token>"
Submit Deferral Request (Protected)
# Development
curl -X POST http://localhost:3333/debtors/me/deferral-requests \
-H "Authorization: Bearer <jwt_token>" \
-H "Content-Type: application/json" \
-d '{
"debtId": "550e8400-e29b-41d4-a716-446655440001",
"requestedDeferralDays": 30,
"reason": "I am experiencing temporary financial hardship and need additional time to arrange payment."
}'
# Production
curl -X POST https://ddc-api.streamlinelab.co/debtors/me/deferral-requests \
-H "Authorization: Bearer <jwt_token>" \
-H "Content-Type: application/json" \
-d '{
"debtId": "550e8400-e29b-41d4-a716-446655440001",
"requestedDeferralDays": 30,
"reason": "I am experiencing temporary financial hardship and need additional time to arrange payment."
}'
Get My Deferral Requests (Protected)
# Development
curl -X GET http://localhost:3333/debtors/me/deferral-requests \
-H "Authorization: Bearer <jwt_token>"
# Production
curl -X GET https://ddc-api.streamlinelab.co/debtors/me/deferral-requests \
-H "Authorization: Bearer <jwt_token>"
List All Deferral Requests (Backoffice)
# Development
curl -X GET "http://localhost:3333/deferral-requests?status=pending" \
-H "Authorization: Bearer <backoffice_token>"
# Production
curl -X GET "https://ddc-api.streamlinelab.co/deferral-requests?status=pending" \
-H "Authorization: Bearer <backoffice_token>"
Approve Deferral Request (Backoffice)
# Development
curl -X PUT http://localhost:3333/deferral-requests/550e8400-e29b-41d4-a716-446655440002/approve \
-H "Authorization: Bearer <backoffice_token>" \
-H "Content-Type: application/json" \
-d '{
"status": "approved",
"reviewNotes": "Approved based on debtor payment history."
}'
# Production
curl -X PUT https://ddc-api.streamlinelab.co/deferral-requests/550e8400-e29b-41d4-a716-446655440002/approve \
-H "Authorization: Bearer <backoffice_token>" \
-H "Content-Type: application/json" \
-d '{
"status": "approved",
"reviewNotes": "Approved based on debtor payment history."
}'
Reject Deferral Request (Backoffice)
# Development
curl -X PUT http://localhost:3333/deferral-requests/550e8400-e29b-41d4-a716-446655440002/reject \
-H "Authorization: Bearer <backoffice_token>" \
-H "Content-Type: application/json" \
-d '{
"status": "rejected",
"reviewNotes": "Insufficient documentation provided."
}'
# Production
curl -X PUT https://ddc-api.streamlinelab.co/deferral-requests/550e8400-e29b-41d4-a716-446655440002/reject \
-H "Authorization: Bearer <backoffice_token>" \
-H "Content-Type: application/json" \
-d '{
"status": "rejected",
"reviewNotes": "Insufficient documentation provided."
}'
Create a Debtor
# Development
curl -X POST http://localhost:3333/debtors \
# Production
curl -X POST https://ddc-api.streamlinelab.co/debtors \
-H "Content-Type: application/json" \
-d '{
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"mobilePhone": "+1234567890",
"legalEntity": "ABC Corp",
"dob": "1980-01-15",
"addressLine1": "123 Main St",
"addressLine2": "Apt 4B",
"city": "London",
"regionState": "Greater London",
"postalCode": "SW1A 1AA",
"countryCode": "GB",
"languagePreference": "English"
}'
Create a Debt
# Development
curl -X POST http://localhost:3333/debts \
# Production
curl -X POST https://ddc-api.streamlinelab.co/debts \
-H "Content-Type: application/json" \
-d '{
"debtorId": "550e8400-e29b-41d4-a716-446655440000",
"originalCreditor": "ABC Credit Corp",
"currencyCode": "GBP",
"originalBalance": 5000.00,
"currentBalance": 5000.00,
"currentStatus": "active",
"placementDate": "2024-01-01",
"p2pScore": 0.750
}'
Record a Payment
# Development
curl -X POST http://localhost:3333/payments \
# Production
curl -X POST https://ddc-api.streamlinelab.co/payments \
-H "Content-Type: application/json" \
-d '{
"debtId": "550e8400-e29b-41d4-a716-446655440001",
"amount": 500.00,
"currencyCode": "GBP",
"paymentMethod": "bank_transfer",
"status": "completed",
"processorTxnId": "TXN-12345"
}'
Get Dashboard Statistics
# Development
curl -X GET http://localhost:3333/dashboard/stats
# Production
curl -X GET https://ddc-api.streamlinelab.co/dashboard/stats
Process Communication Template
# Development
curl -X POST http://localhost:3333/communication-templates/process \
# Production
curl -X POST https://ddc-api.streamlinelab.co/communication-templates/process \
-H "Content-Type: application/json" \
-d '{
"template_name": "final_email_warning",
"debtor_id": "550e8400-e29b-41d4-a716-446655440000",
"debt_id": "550e8400-e29b-41d4-a716-446655440001",
"send": true
}'
Database Migrations
Run Migrations
npm run migration:run
Rollback Last Migration
npm run migration:rollback
Fresh Migration (Drops all tables and recreates)
npm run migration:fresh
Check Migration Status
npm run migration:status
Project Structure
ddc-backend/
├── app/
│ ├── controllers/ # API controllers
│ ├── models/ # Database models
│ ├── validators/ # Request validators
│ ├── services/ # Business logic services (OTP, JWT, SMS, Email, Voice, Strategy)
│ ├── jobs/ # Background jobs (template processing)
│ ├── commands/ # Ace commands (strategy engine, template processing)
│ ├── middleware/ # HTTP middleware (authentication, JSON response)
│ └── exceptions/ # Exception handlers
├── config/ # Configuration files
├── database/
│ └── migrations/ # Database migrations
├── start/
│ ├── routes.ts # Route definitions
│ ├── kernel.ts # Middleware configuration
│ └── env.ts # Environment validation
├── server.ts # Application entry point
└── package.json
Commands
Strategy Engine
Run the daily collection strategy engine:
node ace strategy:run
Process Communication Templates
Process and optionally send communication templates:
# Process all active templates
node ace process:communication-templates
# Process specific template
node ace process:communication-templates final_email_warning
# Process by type
node ace process:communication-templates --type=email
# Process and send (requires debtor and debt IDs)
node ace process:communication-templates --debtorId=<uuid> --debtId=<uuid> --send
Notes
- All API responses are in JSON format
- All IDs are UUIDs (not integers)
- Email is required and unique for debtors
- Mobile phone is required for OTP-based authentication
- Payments automatically update the debt's current balance
- Deleting a payment restores the debt balance
- All monetary values support currency codes (default: USD)
- All timestamps are stored in UTC
- The database uses PostgreSQL UUID extension (pgcrypto)
- Table names are lowercase (except
USER) - JWT tokens expire after 7 days
- OTP codes expire after 5 minutes and have a maximum of 5 verification attempts
- Communication templates support placeholders like
{{debtor_first_name}},{{debt_amount}}, etc. - Strategy engine processes TimeTrigger rules automatically based on debt placement dates
- Deferral requests allow debtors to request payment extensions (1-365 days) with approval workflow
- Only one pending deferral request per debt is allowed
- Email open tracking is implemented via tracking pixels in email content
License
This project is private and proprietary.