Accounting sync

Read your general ledger and keep an accounting system in sync.

Smooth POS keeps a full general ledger. Transactions that affect your books, like supplier bills and store credit adjustments, post journals to it, so your accounting system can take its numbers straight from Smooth POS. This guide covers how the ledger is laid out and the two ways to get it out: a CSV export, or an incremental sync through the API or a Glue job.

How the ledger works

  • Accounts. Your chart of accounts is under Accounts → Chart Of Accounts, or List accounts. Each account has an id and a number (like 100). Map them to your accounting system by number.
  • Journals and entries. A journal is one balanced posting, with a name (like AP Invoice or Store Credit Adjustment), a date, tags and two or more entries. Each entry posts an amount to one account, optionally with a cost_centre_id and a tax_rate_id.
  • Signs. Positive amounts are debits and negative amounts are credits. A journal's entries always add up to zero.
  • Locked periods. Entries dated outside your general ledger dates are locked. See Get general ledger dates.

Option 1: Export a CSV

Export journal entries (GET /journals/entries/export.json) returns a CSV of up to 10,000 entries. It takes the same filters as List journal entries, so a date range is usually all you need:

curl "https://api.smoothpos.net/journals/entries/export.json?date_min=2026-10-01&date_max=2026-10-31" \
  -H "X-Smoothpos-Auth: YOUR_TOKEN" -o october.csv

The columns are id, number, name, account_name, account_number, date, cost_centre, tax_rate, tax_rate_percent, amount, tags, reversed, created and created_by.

For a bigger range, split it into smaller ones. Count journal entries tells you how many entries match, plus their total debit and credit, which is a handy check that your import is complete.

Option 2: Sync incrementally

For a regular sync, read entries in ID order and remember the last one you sent.

  1. Call List journal entries with since_id and limit=250.
  2. Send the entries to your accounting system.
  3. Remember the highest id you sent.
  4. Next time, carry on from there.
🚧

since_id is inclusive here

On journals and journal entries, since_id returns the entry with that ID as well as the ones after it. Ask for since_id = last ID + 1, or skip the first entry, so you don't send it twice.

Follow the X-Next-Page header for each next page. Don't build cursors yourself. See Filtering, sorting and search.

📘

Poll rather than wait for a hook

journal_create only fires for journals created through Create a journal, not for every journal in the ledger. Supplier bills and store credit adjustments, for example, post journals without it. A scheduled sync is the reliable way to catch everything.

Reversals

Smooth POS doesn't edit posted journals. A correction is a reversal: a new journal with the opposite entries. Both the original and the reversal are then marked reversed, and a journal can only be reversed once. Your sync picks the reversal up like any other journal, so your accounting system stays in step.

Example: A nightly Glue job

Trigger: Scheduled, once a night.

  1. Cache: Get the bookmark, Operation Get, Key accounting:next_entry_id, Default value 1, Response variable next_id.

  2. API Request: List journal entries, Additional URL parameters since_id={{ next_id }}&limit=250, Fetch all pages on, Response variable entries.

  3. Script: Build the posting

    var rows = $.entries.items;
    $.has_entries = rows.length > 0;
    var maxId = 0;
    rows.forEach(function (e) { if (e.id > maxId) maxId = e.id; });
    $.new_next_id = maxId + 1;
    $.posting_body = JSON.stringify(rows.map(function (e) {
      return {
        ref: e.id,
        journal: e.journal_id,
        date: e.date,
        account: e.account_number,
        amount: e.amount,
        description: e.name
      };
    }));
  4. If Condition: If there's anything new, Condition $.has_entries === true. Under THEN:

    1. REST Request: Post to the accounting system, with Fail on bad response code on, and Body {{ posting_body }}.
    2. Cache: Save the bookmark, Operation Set, Key accounting:next_entry_id, Value {{ new_next_id }}, Value type Number, Time to live 2592000 (30 days).

Because the bookmark is only saved after the post succeeds, a failed run simply tries the same entries again. Send each entry's id as its reference so your accounting system can ignore anything it's already seen.

📘

Cache entries expire

A cache entry lasts up to 30 days, and every nightly run refreshes it. If the job is paused for longer, the bookmark goes back to the default, so make sure your accounting system ignores entries it already has.

Post journals into Smooth POS

Need to bring adjustments the other way? Create a journal with balanced entries:

{
  "name": "Month-end accrual",
  "date": "2026-10-31T00:00:00Z",
  "tags": ["accrual"],
  "entries": [
    {"account_id": 512, "amount": 1200},
    {"account_id": 208, "amount": -1200}
  ]
}

Unbalanced entries fail with unbalanced journal, and unknown accounts or tax rates fail with account doesent exist or tax rate doesent exist. To undo a journal, reverse it.

Supplier bills, invoices and bank feeds


Did this page help you?