Automation
BigLedger has no workflow engine, and this page is the honest answer to what it has instead: four separate mechanisms, none of which is a visual designer or a rule language, and each of which is worth knowing about because between them they cover most of what integrators ask for.
There is no business rule engine, no workflow designer and no automation builder. Nothing in BigLedger lets you write a condition over a document and attach an action to it. The one package in the platform called a rule engine belongs to the contact centre and does exactly one thing: route an incoming task to a team or an agent. It has two rule types and twelve actions, all of them assignment, queueing, or handing the item to an OCR processor. It cannot see an invoice.
The Workflow Design applet is a catalogue of statuses and transitions — a status track a document applet can display — not an engine. It runs nothing.
If you need “when X happens, do Y”, Y runs in your system, triggered by a webhook and carried out through the API. The rest of this page is what BigLedger contributes to that.
The four mechanisms
| You want to… | Use | Where it runs |
|---|---|---|
| React to a change in a tenant | Webhooks | your endpoint |
| Run something on a schedule | The Scheduler — the tenant’s cron table | BigLedger |
| Run one of BigLedger’s own background jobs | A job processor, put on the Scheduler | BigLedger |
| Gate a purchase document on sign-off | The document approval engine | BigLedger |
Everything else — branching, retries, escalation, orchestration across steps — is yours to build.
React to a change: webhooks
Register a subscription (a URL, a topic, one optional header) and BigLedger POSTs the changed
record to you. Webhooks documents the whole mechanism, including four
absences that change how you must build:
- no signature — the payload is not signed; the only credential is a static header you chose;
- no retry — one attempt, ever, with no dead-letter queue and no replay;
- no alerting — the notification fields on a subscription are loaded and never looked at again; no mail or SMS sender exists in the delivery path;
- no readable delivery log — attempts are recorded, but the endpoint that lists them returns an empty list on every tenant.
The consequence is the single most important design rule on this page: treat a webhook as a hint
that something changed, never as a delivery guarantee. Re-read the record through the
Data API before you act on it, and pair every subscription
with a periodic updated_date_from reconciliation pull. The webhook makes your integration fast;
the pull is what makes it correct.
Run something on a schedule: the tenant cron table
Each tenant has a cron table. A row pairs the queue code of a background job processor with a UNIX cron expression and an optional JSON payload of properties for that job. On each platform tick, BigLedger reads the tenant’s active rows, works out which are due, and puts the matching job on the tenant queue.
The Scheduler applet is the screen for this and documents the behaviour in full. Over the API:
| Purpose | Endpoint |
|---|---|
| List schedules | GET /core2/tnt/dm/sch/crontabs |
| Filter schedules | GET /core2/tnt/dm/sch/crontabs/query |
| Read one | GET /core2/tnt/dm/sch/crontabs/{guid} |
| Create | POST /core2/tnt/dm/sch/crontabs |
| Update | PUT /core2/tnt/dm/sch/crontabs |
| Delete | DELETE /core2/tnt/dm/sch/crontabs/{guid} |
| Run one now | POST /core2/tnt/dm/sch/crontabs/scheduler/execute/backoffice-ep/{guid} |
These need API_TNT_DM_SCH_CRONTAB_READ, _CREATE, _UPDATE or _DELETE, or the broader
_OWNER / _ADMIN grants. There is no documented etl-ep path for any of them, so a
scheduled job is set up by a signed-in user rather than as part of a server-to-server integration.
A schedule row carries:
| Field | Notes |
|---|---|
code | The job processor’s queue code. Stored upper-cased and trimmed. |
name | A label. |
cron_expression | Parsed with the UNIX five-field definition. A six- or seven-field Quartz expression will not behave as written. |
event_properties | A JSON object handed to the job as its properties. |
status | Only ACTIVE rows are read. |
execution_strategy | INSERT_TO_QUEUE or RUN_NOW; the backend fills in INSERT_TO_QUEUE when it is absent. |
last_run_time | Maintained by the platform. Defaults to the moment the row was created. |
code that does not resolve to a job processor
the platform knows is filtered out before the cron check — no error, no event, no change to the
last-run time. A row that is not ACTIVE is never read. And because the next occurrence is
computed forward from last_run_time, which starts at the moment of creation, a daily job created
this afternoon does not back-fill this morning’s run.What you can schedule: job processors
Everything BigLedger does in the background is a job processor — a named unit of work with a queue code, a description and a properties class. There are 597 of them registered in the platform. A processor has to be registered and enabled for your tenant before it can be selected; that is a platform-side step, visible in the Developer SysAdmin applet.
A few that show what the mechanism is for:
| Queue code | What it does |
|---|---|
RECURRING_GENERIC_DOCUMENT_PROCESSOR | Creates the next generic document when a recurring period comes round |
STOCK_REPLENISHMENT_RUN_PROCESSOR | Runs every stock replenishment configuration against the current date and time |
STOCK_LEVEL_MONITORING_RUN_PROCESSOR | Checks stock levels for a configured scope and records breaches |
E_INVOICE_BATCH_PROCESSING_CYCLE_RUN_PROCESSOR | Builds the to-IRB submission rows from the batch pool |
BI_REPORT_QUERY_RUN_PROCESSOR | Executes report query runs from an event and a template |
That last one is how scheduled reporting works: a report is defined in the BI report surface
(/core2/tnt/dm/bi-reports/…, backoffice-ep throughout — there is no documented etl-ep
path into it), and BI_REPORT_QUERY_RUN_PROCESSOR on a cron row is what runs it. There is no
“generate report of type X” API endpoint.
processEvent returns immediately while its description claims real work
(recorded as a product defect). A processor you have not seen produce an effect on a test tenant is
a processor you should test before you schedule.Chained jobs, and why you cannot define one
BigLedger can run a second job when a first one finishes: a platform-level applet trigger template links a publisher job processor to a subscriber job processor, and a tenant enables that link by holding a matching trigger-configuration row. The e-invoice pipeline is built this way.
The half that decides which job follows which lives in the platform registry, not in the tenant. A tenant can enable or disable a link that already exists; it cannot create one, and neither can you. If you need work chained, chain it in your own system.
The largest chain in the product starts when somebody finalises a document
Finalising a generic document — a sales invoice, a cash bill, a purchase order, a credit note — is
not a save. It is its own endpoint, PUT /core2/tnt/dm/erp/generic-documents/{docType}/update-posting-status/{guid}
with posting_status: FINAL in the body, and the last thing it does before returning is put one row
on the tenant queue for the Generic Document Primary Processor. That processor exists to fan
out: it reads the subscriber links for its own queue code and queues one job for each
(GenericDocumentPrimaryProcessor.java L70–L101).
Dozens of processors subscribe to it in the platform registry — the journal posting, the inventory transaction line, the cashbook line, two e-invoice matching queues, the membership point update, four coupon processors, finance charges, budget register lines, historical aging for a back-dated document, intercompany, the open-queue (knock-off) processor, bundle and make-to-order unwrapping, sales commission, non-stock trade-in, and several integration-specific ones. None of that is on the screen, and the count is a property of the platform rather than of your tenant.
Three filters decide which of them actually run for one document, in this order:
- Your tenant’s subscription. Only links whose applet trigger template has an ACTIVE
bl_applet_trigger_config_hdrrow in your database are returned at all (JobProcessorService.javaL538–L552, L592–L606). This is the switch that matters, and it is why two tenants pressing the same button get different amounts of work done. The stock chain is one template covering all three of its levels, so it is on or off as a unit. - Your company’s override list.
bl_fi_mst_comp.posting_final_jsonmay carryincludeJobProcessorCodeandexcludeJobProcessorCodearrays; an excluded code is skipped, and if the include list is non-empty everything not on it is skipped (GenericDocumentPrimaryProcessor.javaL103–L115). It is an override on top of the subscription, not the thing that enables anything — and it is unset on almost every company in the estate, so in practice the subscription is the whole answer. - The document type on the link. A link may carry
serverDocTypesandclientDocTypesconstraints; the job is queued only if this document matches (L117–L133). Most links carry none and therefore match every document type.
The chain is deeper than one hop
A subscriber may itself be a publisher. Stock is three levels below the button:
Finalise → BLG_ERP_GENERIC_DOCUMENT_PRIMARY_PROCESSOR
→ INVENTORY_TRANSACTION_LINE_PROCESSOR (writes the stock ledger line)
→ INVENTORY_TXN_LINE_TO_CURRENT_STOCK_BALANCE_PROCESSOR (balances, moving average, write-back)
→ INVENTORY_FIFO_LIFO_COSTING_PROCESSOR (FIFO baskets, FIFO write-back)Each level re-runs the same lookup for its own queue code, so the depth is data, not code
(InventoryTransactionLineProcessor.java L110–L132).
What the queue does with a job that fails
The queue row is deleted before the handler runs — DELETE … RETURNING is how a worker claims
it (TenantQueue.java L78–L88). So a handler that throws does not leave a row to retry: the
failure is written to app_scheduler_std_error with the event code, the properties (which carry
the document GUID) and the message (BaseQueue.java L172–L195), and nothing re-drives it.
The safety net covers the other failure: a row that was inserted and never attempted. The Main
Tenant Processor’s tick reads up to ten distinct event codes from app_scheduler_queue and
calls the job endpoint once for each, and that endpoint pulls a single event per call
(MainTenantProcessor.java L170–L192). That is what makes asynchronous posting safe against a
restart — and it is also why a tenant producing work faster than one job per code per tick
accumulates a visible backlog in that table.
JobProcessorClassName.valueOf(code) and does not guard it
(GenericDocumentPrimaryProcessor.java L89), while the scheduler path guards the identical call
with a Try and skips the row (MainTenantProcessor.java L132). One subscriber link in the
platform registry today names a queue code that no enum constant matches, so on a tenant that
enables that template the exception aborts the loop and every subscriber after it in the list —
journal, stock, e-invoice — is never queued, silently. Recorded as a product finding; if you are
enabling trigger templates, enable them one at a time and check app_scheduler_std_error
afterwards.Approvals
What the engine does expose over the API is the approval record for a document, so an integration can read where a purchase order stands and react to it:
| Purpose | Endpoint |
|---|---|
| Read approval headers for a document | GET /core2/tnt/dm/erp/generic-doc/approvals/backoffice-ep/query?generic_doc_hdr_guids=… |
| Read an approver’s outstanding requests | GET /core2/tnt/dm/erp/generic-doc/approvals/approval-requests/login-entity-ep |
| Record an approve or reject decision | PUT /core2/tnt/dm/erp/generic-doc/approvals/approval-requests/processors/login-entity-primary-ep |
| Read the approval history | GET /core2/tnt/dm/erp/generic-doc/approvals/approval-histories/backoffice-ep/query |
| Submit or resubmit a document for approval | POST / PUT /core2/tnt/dm/erp/generic-doc/approvals/processors/submission/backoffice-ep |
| Withdraw a pending approval | PUT /core2/tnt/dm/erp/generic-doc/approvals/processors/withdrawals/backoffice-ep |
The behaviour behind them — levels, quorum, the Min Approval Amount rule, the two notification e-mails, and the move to FINAL on the first approval — is described in Document Approvals. Anything beyond that (reminders, escalation, routing by supplier or category, a mobile approval app) has to be built in your own system.
The pattern that works
Automation that holds up on this platform looks the same every time:
- Subscribe to the topics you care about, one URL per subscription.
- Re-read the record through the Data API when a webhook arrives. Do not trust the body.
- Decide and act in your own system. Your conditions, your retries, your audit trail.
- Write back through the
etl-ependpoints in the API Reference. - Reconcile on a schedule with an
updated_date_frompull, because step 1 drops events silently. - Schedule BigLedger’s own jobs for the work that has to happen inside BigLedger — recurring documents, replenishment runs, e-invoice sweeps, report runs.
Start at Integration: Getting Started, which takes you from no credential to moving real data.
What is not there
Stated plainly, because it changes what is worth designing:
| Not available | What to do instead |
|---|---|
| A workflow engine or visual designer | Orchestrate in your own system |
| A business rule engine over documents | Evaluate conditions in your own system |
| Webhook signatures, retries, replay or a readable delivery log | Re-read every record; reconcile on a schedule |
| An API to create a job processor | Job processors are platform code; only scheduling is tenant-side |
| An API to chain one job to another | Chain in your own system |
| An approval API for documents other than the three named above | Use permissions |
| A “generate report” endpoint | Define the report, then schedule BI_REPORT_QUERY_RUN_PROCESSOR |
| A BigLedger client SDK for JavaScript, Python, PHP, Java, .NET or Go | Call the REST API directly. The published Angular libraries are for applet development, not for server-side integration |