Get icon
OutputValues icon
If icon
SlackIncomingWebhook icon
Log icon
Schedule icon

Pennylane Trial Balance Integrity Gate

Fetch a Pennylane trial balance, sum debits versus credits, alert Slack if the period does not balance, and log the result.

Categories
BusinessCore

A trial balance that does not net to zero means something is wrong in the books, a missing entry, a sync error from an upstream integration, a manual journal entry that only hit one side of the ledger. Pennylane's own interface shows the trial balance on request; it does not page anyone when debits and credits stop matching. This blueprint fetches the trial balance for a period, sums both sides, and alerts the moment they diverge past a tolerance you set.

This blueprint was created by Mohit Jeswani.

How it works

  1. get_trial_balance (io.kestra.plugin.pennylane.accounting.trialbalance.Get) retrieves every ledger account's debits and credits for period_start to period_end, paginating up to max_records. Read-only; nothing in Pennylane is written. allowFailure: true covers an API or auth error without failing the flow outright.
  2. evaluate (io.kestra.plugin.core.output.OutputValues) sums debits and credits across every row with a jq filter (debits/credits are typed loosely by the API, so each value is coerced with tonumber before summing), and compares the absolute difference to max_imbalance. get_trial_balance failing outright is also CRITICAL.
  3. route (io.kestra.plugin.core.flow.If):
    • compliant: logs the totals and account count.
    • alert: posts the imbalance, both totals, and the account count to Slack.
  4. summary logs the imbalance and severity, closing the governance cycle.
  5. errors alerts Slack if the flow itself fails, for example an expired API token.

What you get

  • A direct answer to "do the books balance for this period", computed from the same numbers Pennylane itself reports, not a derived estimate.
  • A tolerance you control, so routine rounding on a large ledger does not page anyone, only a real divergence does.
  • A read-only check with zero risk of touching a real ledger entry.

Who it's for

  • Finance and accounting teams closing the books in Pennylane who want an automated sanity check before a period is reported as closed.
  • Teams evaluating the Pennylane plugin who want a worked example of accounting.trialbalance.Get used for a governance check rather than a data export.

Why orchestrate this with Kestra

Pennylane computes the trial balance; it does not decide a cadence, compare the two sides, or notify anyone when they stop matching. Kestra keeps max_imbalance as a flow input instead of a one-off spreadsheet check, turns the comparison into a Slack page naming the exact imbalance, and keeps a full execution history of every period checked.

Prerequisites

  • A Pennylane account (or firm account) with a company or firm API token scoped trial_balance:readonly.
  • At least one closed accounting period with ledger activity, to confirm the totals are nonzero and actually balance.
  • A Slack incoming webhook URL.

This flow calls the Pennylane API directly over HTTPS; there is no official local Pennylane emulator, so get_trial_balance cannot be exercised against a local Docker container. Use a Pennylane sandbox or test company to validate this end to end.

Secrets

  • PENNYLANE_API_TOKEN: the company or firm API token used by get_trial_balance. In Kestra OSS, provide it as the base64-encoded environment variable SECRET_PENNYLANE_API_TOKEN.
  • SLACK_WEBHOOK_URL: used by alert and the errors block. In Kestra OSS, provide it as the base64-encoded environment variable SECRET_SLACK_WEBHOOK_URL.

Inputs

Input Default Purpose
period_start 2026-01-01 Start of the accounting period
period_end 2026-01-31 End of the accounting period
max_imbalance 0.01 Absolute debits-minus-credits tolerance before alerting
max_records 5000 Safety cap on rows retrieved across all pages

Outputs

  • {{ outputs.get_trial_balance.rows }}: every ledger account row (number, formatted_number, label, debits, credits).
  • {{ outputs.evaluate.values.total_debits }} / .total_credits / .imbalance: the computed sums and their signed difference.
  • {{ outputs.evaluate.values.severity }}: OK, OUT_OF_BALANCE, or CRITICAL.

Pitfalls

  • debits/credits are typed loosely by the Pennylane API and may arrive as strings; the jq filter coerces every value with tonumber before summing. A ledger account whose values are not numeric strings would fail that coercion rather than being silently skipped.
  • Get does not paginate past max_records; a trial balance with more accounts than that cap is only partially summed, and a true imbalance could be masked by rows that were never fetched. Set max_records well above the company's actual chart-of-accounts size.
  • A trial balance with zero rows (an empty or never-opened period) sums to 0 debits and 0 credits, which reads as perfectly balanced. This blueprint does not distinguish "balanced" from "empty"; check account_count in the Slack alert or summary log if that matters to you.

Quick start

  1. Add the PENNYLANE_API_TOKEN and SLACK_WEBHOOK_URL secrets.
  2. Set period_start/period_end to a real closed period and run the flow manually.
  3. Confirm compliant logs matching totals.
  4. Lower max_imbalance below the real rounding noise on a large ledger to confirm alert fires, then restore it.
  5. Enable the monthly trigger once the period inputs match your actual closing cadence.

How to extend

  • Loop over several consecutive periods with io.kestra.plugin.core.flow.Loop to re-check prior months whenever a new integration or import touches historical data.
  • Break the alert down by label (ledger account) instead of a single total, to name which account is driving the imbalance rather than just the aggregate.
  • Chain this after a reconciliation flow that imports bank or supplier data, so a sync error is caught the same day it lands rather than at month-end close.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.