Skip to main content

Overview

The Loans API manages the lending and tracking of inventory items. It supports loan creation, status management, automatic stock adjustments, and return processing. All loan operations are automatically associated with the authenticated user via JWT token.

Endpoints

Get All Loans

Retrieves all loans across all containers for the authenticated user (Dashboard view).
GET /loans

Authentication

The backend automatically filters loans by the userId extracted from the JWT token.

Response

data
Loan[]
Array of loan objects

Example Request

const response = await fetch('/loans', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN'
  }
});

const loans = await response.json();

Example Response

[
  {
    "id": 1,
    "containerId": 42,
    "inventoryItemId": 15,
    "itemName": "Laptop Dell XPS 15",
    "quantity": 1,
    "borrowerName": "John Doe",
    "borrowerEmail": "[email protected]",
    "borrowerPhone": "+1234567890",
    "loanDate": "2026-03-01",
    "expectedReturnDate": "2026-03-15",
    "actualReturnDate": null,
    "notes": "For presentation at tech conference",
    "status": "active",
    "userId": 7
  }
]

Get Container Loans

Retrieves all loans for a specific container.
GET /containers/{containerId}/loans

Path Parameters

containerId
integer
required
The ID of the container whose loans to retrieve

Response

data
Loan[]
Array of loan objects (same structure as Get All Loans)

Example Request

const response = await fetch('/containers/42/loans', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN'
  }
});

const loans = await response.json();

Create Loan

Creates a new loan record and automatically decrements the inventory item stock.
POST /containers/{containerId}/loans

Path Parameters

containerId
integer
required
The ID of the container containing the inventory item

Request Body

inventoryItemId
integer
required
ID of the inventory item to loan
itemName
string
required
Name of the item being loaned
quantity
integer
default:"1"
Number of units to loan
borrowerName
string
Name of the person borrowing the item
borrowerEmail
string
Email address of the borrower
borrowerPhone
string
Phone number of the borrower
loanDate
string
required
Date of the loan (YYYY-MM-DD)
expectedReturnDate
string
Expected return date (YYYY-MM-DD)
notes
string
Additional notes about the loan
status
string
default:"active"
Initial status (typically ‘active’)

Response

data
Loan
The newly created loan object

Example Request

const response = await fetch('/containers/42/loans', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    inventoryItemId: 15,
    itemName: 'Laptop Dell XPS 15',
    quantity: 1,
    borrowerName: 'John Doe',
    borrowerEmail: '[email protected]',
    borrowerPhone: '+1234567890',
    loanDate: '2026-03-01',
    expectedReturnDate: '2026-03-15',
    notes: 'For presentation at tech conference',
    status: 'active'
  })
});

const loan = await response.json();

Example Response

{
  "id": 1,
  "containerId": 42,
  "inventoryItemId": 15,
  "itemName": "Laptop Dell XPS 15",
  "quantity": 1,
  "borrowerName": "John Doe",
  "borrowerEmail": "[email protected]",
  "borrowerPhone": "+1234567890",
  "loanDate": "2026-03-01",
  "expectedReturnDate": "2026-03-15",
  "actualReturnDate": null,
  "notes": "For presentation at tech conference",
  "status": "active",
  "userId": 7
}

Error Responses

201
success
Loan created successfully
400
error
Bad request - Insufficient stock or invalid inventory item
401
error
Unauthorized - Authentication required

Return Loan

Marks a loan as returned and automatically increments the inventory item stock.
PUT /containers/{containerId}/loans/{loanId}/return

Path Parameters

containerId
integer
required
The ID of the container
loanId
integer
required
The ID of the loan to mark as returned

Response

data
Loan
The updated loan object with status ‘returned’ and actualReturnDate populated

Example Request

const response = await fetch('/containers/42/loans/1/return', {
  method: 'PUT',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN'
  }
});

const returnedLoan = await response.json();

Example Response

{
  "id": 1,
  "containerId": 42,
  "inventoryItemId": 15,
  "itemName": "Laptop Dell XPS 15",
  "quantity": 1,
  "borrowerName": "John Doe",
  "borrowerEmail": "[email protected]",
  "borrowerPhone": "+1234567890",
  "loanDate": "2026-03-01",
  "expectedReturnDate": "2026-03-15",
  "actualReturnDate": "2026-03-07",
  "notes": "For presentation at tech conference",
  "status": "returned",
  "userId": 7
}

Error Responses

200
success
Loan returned successfully
400
error
Bad request - Loan already returned
404
error
Loan not found

Delete Loan

Deletes a loan record. Only the loan creator can delete their own loans.
DELETE /containers/{containerId}/loans/{loanId}

Path Parameters

containerId
integer
required
The ID of the container
loanId
integer
required
The ID of the loan to delete

Response

Returns status code 200 or 204 on successful deletion.

Example Request

const response = await fetch('/containers/42/loans/1', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN'
  }
});

if (response.ok) {
  console.log('Loan deleted successfully');
}

Error Responses

200
success
Loan deleted successfully
204
success
Loan deleted successfully (no content)
403
error
Forbidden - You can only delete your own loans
404
error
Loan not found

Get Loan Statistics

Retrieves statistical summary of loans for a container.
GET /containers/{containerId}/loans-stats

Path Parameters

containerId
integer
required
The ID of the container

Response

totalLoans
integer
Total number of loans (all statuses)
activeLoans
integer
Number of currently active loans
overdueLoans
integer
Number of active loans past their expected return date
returnedLoans
integer
Number of returned loans

Example Request

const response = await fetch('/containers/42/loans-stats', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN'
  }
});

const stats = await response.json();

Example Response

{
  "totalLoans": 45,
  "activeLoans": 12,
  "overdueLoans": 3,
  "returnedLoans": 33
}

Business Logic Notes

Automatic Stock Management

  • Creating a loan automatically decrements the inventory item’s stock by the loan quantity
  • Returning a loan (via the return endpoint) automatically increments the stock back
  • The backend enforces stock validation - loans cannot exceed available quantity

User Authentication

  • The userId field is automatically populated from the JWT token during loan creation
  • Users cannot specify their own userId in the request body
  • Only loan owners can delete their loans

Status Management

  • Loans are created with status ‘active’
  • The return endpoint sets status to ‘returned’ and populates actualReturnDate
  • Status transitions are enforced server-side

Overdue Detection

  • The isOverdue computed property returns true if:
    • Status is ‘active’ AND
    • expectedReturnDate exists AND
    • Current date is after expectedReturnDate
  • Use this for UI indicators and alert generation

Voucher IDs

  • Each loan has a formatted voucher ID: V-000001, V-000042, etc.
  • Generated via formattedVoucherId getter: pads loan ID to 6 digits
  • Useful for printing and reference

Date Handling

  • All dates are stored and transmitted in ISO 8601 format (YYYY-MM-DD)
  • The toJson() method automatically formats dates to local timezone
  • Client should parse dates using DateTime.parse()

Data Integrity

  • Deleting a loan does not automatically restore inventory stock
  • Consider implementing soft deletes for audit trails
  • Loan history is valuable for analytics and reporting

Build docs developers (and LLMs) love