Posting Service API
The Posting Service manages the transition of journal entries from PENDING to POSTED status, providing both automated and manual posting capabilities.
Overview
All transactions in Keshless start as PENDING journal entries. The Posting Service handles:
- Single entry posting
- Batch posting
- Auto-posting based on validation period
- Entry validation before posting
- Review workflows for flagged entries
Service Methods
postEntry(journalId, options)
Post a single pending journal entry.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
journalId | string | Yes | The journal entry ID (e.g., JE-000123) |
options.postedBy | string | No | Admin ID who posted |
options.ipAddress | string | No | IP address for audit |
options.requestId | string | No | Request ID for tracing |
options.notes | string | No | Additional notes |
Returns: JournalEntry
Validation:
- Journal must exist
- Status must be
PENDING - Debits must equal credits (balanced)
Example:
const postedEntry = await postingService.postEntry('JE-000123', {
postedBy: 'admin-456',
notes: 'Approved after manual review'
});autoPostPendingEntries(olderThanMinutes, options)
Automatically post pending entries older than specified time.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
olderThanMinutes | number | 30 | Age threshold in minutes |
options.maxEntries | number | All | Maximum entries to process |
options.dryRun | boolean | false | Preview without posting |
Returns:
{
processed: number;
posted: number;
failed: number;
skipped: number; // AML-flagged or unbalanced
errors: Array<{ journalId: string; error: string }>;
}Skip Conditions:
- Entry is not balanced
- AML status is
FLAGGEDorBLOCKED
Example:
// Auto-post entries older than 30 minutes (dry run)
const result = await postingService.autoPostPendingEntries(30, { dryRun: true });
console.log(`Would post ${result.posted} entries`);
// Execute auto-posting
const result = await postingService.autoPostPendingEntries(30);batchPost(journalIds, options)
Post multiple pending entries in a batch.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
journalIds | string[] | Yes | Array of journal IDs |
options.postedBy | string | No | Admin ID |
options.ipAddress | string | No | IP address |
options.stopOnError | boolean | No | Stop on first error |
Returns:
{
posted: number;
failed: number;
errors: Array<{ journalId: string; error: string }>;
}getPendingEntries(options)
Get pending entries awaiting posting.
Parameters:
| Parameter | Type | Description |
|---|---|---|
options.entityId | string | Filter by user/vendor ID |
options.entityType | string | User or Vendor |
options.olderThanMinutes | number | Age threshold |
options.limit | number | Maximum results |
Returns: JournalEntry[]
getPostingStats(startDate?, endDate?)
Get posting statistics for reporting.
Returns:
{
totalPosted: number;
totalPending: number;
totalCancelled: number;
totalReversed: number;
averagePostingDelayMinutes: number;
oldestPendingMinutes?: number;
}validatePendingEntry(journalId)
Validate an entry before posting.
Returns:
{
isValid: boolean;
errors: string[]; // Blocking issues
warnings: string[]; // Non-blocking concerns
}Checks Performed:
- Status is
PENDING - Debits equal credits
- AML status (blocked = error, flagged = warning)
- Age check (>24 hours = warning)
cancelEntry(journalId, reason, options)
Cancel a pending entry.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
journalId | string | Yes | Journal ID |
reason | string | Yes | Cancellation reason |
options.cancelledBy | string | No | Admin ID |
options.ipAddress | string | No | IP address |
reviewEntry(journalId, action, options)
Review and approve/reject pending entries (admin workflow).
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
journalId | string | Yes | Journal ID |
action | 'approve' | 'reject' | Yes | Review decision |
options.reviewedBy | string | Yes | Admin ID |
options.notes | string | No | Review notes |
options.reason | string | No | Rejection reason |
options.ipAddress | string | No | IP address |
getEntriesForReview(options)
Get entries requiring manual review.
Parameters:
| Parameter | Type | Description |
|---|---|---|
options.amlFlagged | boolean | Only AML-flagged entries |
options.highValue | number | Amount threshold (E) |
options.limit | number | Maximum results |
Workflow Diagrams
Standard Posting Flow
Transaction Created
│
▼
PENDING Status
│
├─── Auto-post (after 30 min)
│ │
▼ ▼
Manual Review ──► POSTED
│
└─── Reject
│
▼
CANCELLEDAML-Flagged Entry Flow
Transaction Created
│
▼
PENDING + AML FLAGGED
│
▼
Manual Review Required
│
├─── Approve (after investigation)
│ │
▼ ▼
POSTED with notes
│
└─── Reject (suspicious)
│
▼
CANCELLED + SAR filedBest Practices
Always validate before posting:
typescriptconst validation = await postingService.validatePendingEntry(journalId); if (!validation.isValid) { console.error('Cannot post:', validation.errors); }Use dry-run for auto-posting:
typescript// Preview first await postingService.autoPostPendingEntries(30, { dryRun: true }); // Then execute await postingService.autoPostPendingEntries(30);Monitor posting stats:
typescriptconst stats = await postingService.getPostingStats(); if (stats.oldestPendingMinutes > 60) { console.warn('Entries stuck in pending'); }
Related
- Ledger Service - Core ledger operations
- Balance Verification - Reconciliation
- Trial Balance - Balance reports