Skip to main content

Stock Reconciliation API

Created: December 25, 2025This documentation provides comprehensive information about the stock reconciliation endpoint, including multi-item support, asynchronous processing, and complete workflow examples.
The Stock Reconciliation endpoint allows you to reconcile physical inventory counts with system-recorded quantities. This is essential for maintaining accurate inventory records, identifying discrepancies, and correcting stock levels. The endpoint processes multiple items in a single transaction and automatically updates stock ledger entries and bin quantities asynchronously.

Endpoint Details

Base URL: https://joptic.jethings.com

Required Headers

x-store-id: <store_id>
Authorization: Bearer <token>

Endpoint

POST /stock-reconciliation
Creates stock reconciliation records for one or more items, calculates differences between system and actual quantities, and queues background jobs to update stock ledger entries and bin quantities.

Create Stock Reconciliation

Request

Method: POST
Path: /stock-reconciliation

Headers

HeaderTypeRequiredDescription
x-store-idstring✅ YesThe store ID
Authorizationstring✅ YesBearer token for authentication
Content-Typestring✅ Yesapplication/json

Request Body

Type: CreateReconciliationDto
{
  items: Array<{                    // Required: At least one item required
    warehouse: string;              // Required: Warehouse ID
    itemVariant: string;            // Required: Item variant ID
    actualQty: number;              // Required: Physical count quantity
  }>;
  note?: string;                    // Optional: Note/reason for reconciliation
}

Field Descriptions

Items Array

FieldTypeRequiredDescription
itemsarray✅ YesArray of items to reconcile (minimum 1 item required)
items[].warehousestring✅ YesThe warehouse ID where the reconciliation is performed
items[].itemVariantstring✅ YesThe item variant ID being reconciled
items[].actualQtynumber✅ YesThe actual physical quantity counted during inventory

Note

FieldTypeRequiredDescription
notestringNoOptional note or reason for the reconciliation (e.g., “Monthly inventory check”, “Damaged goods adjustment”). This note is shared across all items in the request and will be stored in both the reconciliation record and the stock ledger entry.

Validation Rules

  • At least one item must be provided in the items array
  • Each item must have valid warehouse, itemVariant, and actualQty fields
  • actualQty must be a number (can be positive, negative, or zero)

Response

Type: CreateStockReconciliationResponseDto
{
  data: Array<{
    id: string;                     // Reconciliation record ID
    note?: string | null;           // Note (if provided)
    systemQty: string;              // System quantity at time of reconciliation
    actualQty: string;             // Actual physical quantity
    differenceQty: string;          // Difference: actualQty - systemQty
    createdAt: Date;                // Creation timestamp
    updatedAt: Date;                 // Last update timestamp
  }>;
}

Field Descriptions

FieldTypeDescription
dataarrayArray of created reconciliation records
data[].idstringUnique identifier for the reconciliation record
data[].notestring | nullThe note provided in the request (shared across all items)
data[].systemQtystringThe system-recorded quantity at the time of reconciliation (sum of all active bins for the warehouse/itemVariant)
data[].actualQtystringThe physical quantity that was counted
data[].differenceQtystringCalculated as actualQty - systemQty
data[].createdAtDateWhen the reconciliation record was created
data[].updatedAtDateWhen the reconciliation record was last updated
Difference Interpretation:
  • Positive: More items found than system shows (surplus)
  • Negative: Fewer items found than system shows (shortage)
  • Zero: Perfect match

How It Works

1. Transaction Processing

All items are processed within a single database transaction to ensure atomicity. If any item fails, the entire operation is rolled back.
All items in a single request are processed within one database transaction. If any item fails validation or processing, the entire request is rolled back.

2. For Each Item

Step 1: Calculate System Quantity

The system retrieves the current system quantity from the bin table:
SELECT COALESCE(SUM(qty) FILTER (WHERE isActive = true), 0) as systemQty
FROM bin
WHERE warehouseId = :warehouse AND itemVariantId = :itemVariant
  • Only active bins are included in the calculation
  • If no bins exist, system quantity defaults to 0
  • The sum accounts for all active bins for the warehouse/itemVariant combination

Step 2: Calculate Difference

differenceQty = actualQty - systemQty
Examples:
  • System: 100, Actual: 98 → Difference: -2 (shortage)
  • System: 50, Actual: 55 → Difference: +5 (surplus)
  • System: 0, Actual: 10 → Difference: +10 (new stock found)

Step 3: Create Reconciliation Record

A stockReconciliation record is created with:
  • Warehouse ID
  • Item variant ID
  • System quantity (as string for precision)
  • Actual quantity (as string for precision)
  • Difference quantity (as string for precision)
  • Note (shared across all items)

Step 4: Queue Stock Update Job

A background job is queued to update the stock ledger entry and bin table. The job runs asynchronously and includes:
  • Reconciliation ID
  • Warehouse ID
  • Item variant ID
  • Actual quantity
  • Note

3. Asynchronous Stock Update (Background Job)

After the transaction commits, the queued job processes each reconciliation:

Stock Ledger Entry (SLE)

Creates a stock ledger entry for audit trail:
{
  warehouse: string;
  itemVariant: string;
  qtyChange: "0";                    // Always 0 for reconciliation
  qtyAfterTransaction: actualQty;    // Set to actual quantity
  voucherType: "stock_reconciliation";
  voucher: reconciliationId;          // Links to reconciliation record
  reason: note;                      // Note from reconciliation
}
The qtyChange is always 0 for reconciliations because we’re setting an absolute quantity, not making a relative change.

Bin Update

Updates or creates the bin entry:
  • If bin exists: Updates the bin’s qty to match actualQty
  • If bin doesn’t exist: Creates a new bin entry with qty = actualQty and isActive = true
The bin quantity is set to the absolute actual quantity, not adjusted by the difference.
Stock updates are processed asynchronously via a queue system. The endpoint returns immediately after creating reconciliation records. Background jobs process stock updates. Jobs have retry logic (3 attempts by default). If a job fails, it remains in the queue for manual review.

4. Response Mapping

All created reconciliation records are mapped to response DTOs and returned in the response array.

Example Requests

Single Item Reconciliation

{
  "items": [
    {
      "warehouse": "warehouse_123",
      "itemVariant": "variant_456",
      "actualQty": 98
    }
  ],
  "note": "Monthly inventory check - found 2 units missing"
}
Scenario:
  • System quantity: 100
  • Actual quantity: 98
  • Difference: -2 (shortage)

Multiple Items Reconciliation

{
  "items": [
{
  "warehouse": "warehouse_123",
      "itemVariant": "variant_456",
      "actualQty": 98
    },
    {
  "warehouse": "warehouse_123",
      "itemVariant": "variant_789",
      "actualQty": 55
    },
    {
      "warehouse": "warehouse_456",
      "itemVariant": "variant_123",
      "actualQty": 10
    }
  ],
  "note": "End of month inventory reconciliation"
}
Processing:
  • All three items are processed in a single transaction
  • Each item gets its own reconciliation record
  • All records share the same note
  • Three separate background jobs are queued (one per item)

New Stock Found (No Previous Bin)

{
  "items": [
    {
      "warehouse": "warehouse_123",
      "itemVariant": "variant_new",
      "actualQty": 25
    }
  ],
  "note": "Found unrecorded stock in warehouse"
}
Scenario:
  • System quantity: 0 (no bin exists)
  • Actual quantity: 25
  • Difference: +25
  • Result: New bin entry is created with qty = 25

Zero Quantity Reconciliation

{
  "items": [
    {
      "warehouse": "warehouse_123",
      "itemVariant": "variant_456",
      "actualQty": 0
    }
  ],
  "note": "Item completely out of stock"
}
Scenario:
  • System quantity: 5
  • Actual quantity: 0
  • Difference: -5 (all items missing)
  • Result: Bin is updated to qty = 0

Example Responses

Success Response (Single Item)

{
  "data": [
    {
      "id": "recon_abc123",
      "note": "Monthly inventory check",
      "systemQty": "100.00",
      "actualQty": "98.00",
      "differenceQty": "-2.00",
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    }
  ]
}

Success Response (Multiple Items)

When multiple items are reconciled, each gets its own record in the response array:
{
  "data": [
    {
      "id": "recon_001",
      "note": "End of month reconciliation",
      "systemQty": "100.00",
      "actualQty": "98.00",
      "differenceQty": "-2.00",
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    },
    {
      "id": "recon_002",
      "note": "End of month reconciliation",
      "systemQty": "50.00",
      "actualQty": "55.00",
      "differenceQty": "5.00",
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    },
    {
      "id": "recon_003",
      "note": "End of month reconciliation",
      "systemQty": "0.00",
      "actualQty": "10.00",
      "differenceQty": "10.00",
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    }
  ]
}

Error Responses

400 Bad Request

Validation Errors

  • Empty items array: "At least one item is required"
    • Occurs when items array is empty or not provided
  • Missing required fields: Validation errors for missing warehouse, itemVariant, or actualQty
    • "warehouse must be a string"
    • "warehouse should not be empty"
    • "itemVariant must be a string"
    • "itemVariant should not be empty"
    • "actualQty must be a number"

500 Internal Server Error

  • Database transaction failures
  • Queue processing errors
  • Foreign key constraint violations (invalid warehouse or itemVariant IDs)

Important Notes

1. Atomic Transaction

All items in a single request are processed within one database transaction. This means:
  • All succeed or all fail: If any item fails validation or processing, the entire request is rolled back
  • Consistent state: You won’t have partial reconciliations

2. System Quantity Calculation

The system quantity is calculated as:
  • Sum of all active bins for the warehouse/itemVariant combination
  • Only bins where isActive = true are included
  • If no active bins exist, system quantity is 0

3. Difference Calculation

differenceQty = actualQty - systemQty
  • Positive difference: More items found than system shows (surplus)
  • Negative difference: Fewer items found than system shows (shortage)
  • Zero difference: Perfect match

4. Asynchronous Stock Updates

Stock ledger entries and bin updates happen asynchronously via a queue system:
  • The endpoint returns immediately after creating reconciliation records
  • Background jobs process stock updates
  • Jobs have retry logic (3 attempts by default)
  • If a job fails, it remains in the queue for manual review
The reconciliation record is created synchronously, but the actual stock adjustment happens asynchronously. The bin quantity will be updated shortly after the request completes.

5. Bin Quantity Update

The bin quantity is set to the absolute actual quantity, not adjusted by the difference:
  • Before reconciliation: Bin qty = 100
  • Actual count: 98
  • After reconciliation: Bin qty = 98 (not 100 - 2)
This ensures the bin quantity matches the physical count exactly.

6. Stock Ledger Entry

Every reconciliation creates a stock ledger entry for audit purposes:
  • qtyChange is always 0 (reconciliation sets absolute quantity, not relative)
  • qtyAfterTransaction is set to the actual quantity
  • voucherType is "stock_reconciliation"
  • voucher links to the reconciliation record ID
  • reason contains the note from the reconciliation

7. Shared Note

The note field is shared across all items in a single request. If you need different notes for different items, make separate requests.

8. Decimal Precision

All quantities are stored as strings (decimal type in database) to maintain precision. The response returns them as strings to preserve exact values.

9. Multiple Warehouses

You can reconcile items from different warehouses in a single request. Each item specifies its own warehouse.

10. New Stock Creation

If an item variant doesn’t have a bin entry, the reconciliation will:
  • Create a new bin entry with the actual quantity
  • Set isActive = true
  • Create a stock ledger entry documenting the new stock

Workflow Example

Typical Inventory Reconciliation Process

  1. Physical Count
    • Staff counts physical inventory in the warehouse
    • Records actual quantities for each item variant
  2. Create Reconciliation
    POST /stock-reconciliation
    {
      "items": [
        {
          "warehouse": "warehouse_123",
          "itemVariant": "variant_456",
          "actualQty": 98
        }
      ],
      "note": "Monthly inventory - 2 units missing"
    }
    
  3. System Processing
    • System calculates current quantity: 100
    • Calculates difference: -2
    • Creates reconciliation record
    • Queues background job
  4. Background Job Execution
    • Creates stock ledger entry (qtyChange=0, qtyAfterTransaction=98)
    • Updates bin quantity to 98
    • Job completes successfully
  5. Verification
    • Check reconciliation record via GET endpoint
    • Verify bin quantity was updated
    • Review stock ledger entry for audit trail

Integration with Other Systems

Stock Ledger

Every reconciliation creates a stock ledger entry:
  • Provides complete audit trail
  • Links to reconciliation record via voucher field
  • Can be queried to see all stock adjustments

Bin Management

Bin quantities are automatically updated:
  • Reflects actual physical counts
  • Maintains accurate inventory levels
  • Supports multi-warehouse scenarios

Reporting

Reconciliation records can be queried via:
  • GET /stock-reconciliation - List all reconciliations with pagination
  • Filter by warehouse, item variant, date range
  • Analyze discrepancies over time

Database Schema

stockReconciliation Table

{
  id: string;                    // Primary key (CUID)
  warehouse: string;              // Foreign key to warehouse
  itemVariant: string;            // Foreign key to itemVariant
  systemQty: string;              // System quantity (decimal as string)
  actualQty: string;              // Actual quantity (decimal as string)
  differenceQty: string;          // Difference (decimal as string)
  note?: string;                  // Optional note
  createdAt: Date;               // Creation timestamp
  updatedAt: Date;               // Update timestamp
}

stockLedgerEntry (Created by Background Job)

{
  id: string;                    // Primary key
  warehouse: string;              // Warehouse ID
  itemVariant: string;            // Item variant ID
  qtyChange: "0";                // Always 0 for reconciliation
  qtyAfterTransaction: string;    // Actual quantity
  voucherType: "stock_reconciliation";
  voucher: string;               // Reconciliation ID
  reason?: string;               // Note from reconciliation
  createdAt: Date;
  updatedAt: Date;
}

bin (Updated by Background Job)

{
  id: string;
  warehouseId: string;
  itemVariantId: string;
  qty: number;                   // Updated to actualQty
  isActive: boolean;
  createdAt: Date;
  updatedAt: Date;
}

  • GET /stock-reconciliation - Get paginated list of all reconciliations
    • Supports search, pagination, and sorting
    • Filters by store (via warehouse relationship)

Best Practices

  1. Batch Related Items: Group items from the same warehouse or reconciliation session in a single request for better performance and consistency.
  2. Use Descriptive Notes: Always provide meaningful notes to explain why the reconciliation was performed (e.g., “Monthly inventory”, “Damaged goods adjustment”, “Theft investigation”).
  3. Verify Before Reconciling: Check system quantities before performing reconciliation to understand expected differences.
  4. Monitor Background Jobs: Ensure the queue processor is running to complete stock updates. Check job status if bin quantities aren’t updating.
  5. Regular Reconciliation: Perform regular inventory counts to maintain accurate stock levels and identify discrepancies early.
  6. Document Discrepancies: Use the note field to document reasons for significant differences (theft, damage, counting errors, etc.).
  7. Review Stock Ledger: Regularly review stock ledger entries to track all inventory adjustments and maintain audit trail.