Reports and exports

Build, run and export reports in the app, the API and Glue.

Smooth POS reports let you slice your data however you need: sales by store and hour, best-selling lines, payments by method, stock on hand, gift card balances and more. Build them in the app, run them through the API, and export them as CSV for spreadsheets or other systems.

In the app

Go to Reports. Create a report, choose what it's about (its data source), pick the columns and filters, and set the period. Save it to run again later.

For the accounting statements (profit and loss, balance sheet, trial balance and the BAS report) go to Accounts → Reports.

How a report is defined

PartAPI fieldWhat it does
Data sourcedata_sourceWhat the report is about. See the table below.
ColumnscolumnsEach has a field, a name (used as the column heading) and an optional aggregation.
FiltersfiltersEach has a field, a filter (=, !=, <, <=, >, >= or in), a value and an optional aggregation.
SortsortA field, direction (asc or desc) and optional aggregation.
Periodperiodfrom_date, to_date and timezone, like Australia/Sydney.
Compare periodscompare_periodsExtra periods to run the same report over, side by side.
data_sourceReports on
salesSales
sales_linesSale lines
sales_budgetsSales budgets
paymentsPayments
inventoryStock on hand
gift_cardsGift cards
store_creditStore credit transactions
loyalty_pointsLoyalty point transactions
ledgerYour general ledger

Aggregations

A column with an aggregation of sum, count, avg, min, max or stdev is summarised, and the other columns become the grouping. So store_name plus total with sum gives total sales per store. A filter with an aggregation applies to the summarised value, so total, sum, >, 1000 keeps only stores that sold more than 1,000.

The period

The period filters by when records were created, in the timezone you give. Sales budgets use their budget date instead, and stock on hand isn't dated, so inventory reports ignore the period.

Access

Running a report needs access to its data source, so an API user can be allowed to see sales reports without seeing the ledger. Cost figures only show for users who can see costs. See API users and tokens.

Run a report through the API

Run a report with POST /report/run.json. Send the report definition and get it back with results and a summary filled in:

{
  "data_source": "sales",
  "columns": [
    {"field": "store_name", "name": "Store"},
    {"field": "total", "name": "Sales", "aggregation": "sum"},
    {"field": "receipt_number", "name": "Transactions", "aggregation": "count"}
  ],
  "sort": {"field": "total", "aggregation": "sum", "direction": "desc"},
  "period": {"from_date": "2026-10-01T00:00:00", "to_date": "2026-10-31T23:59:59", "timezone": "Australia/Sydney"},
  "filters": []
}

In each row of results, a plain column is keyed by its field and a summarised one by the aggregation and the field, so the example gives rows like this:

{"store_name": "Bondi", "sum_total": 18450.5, "count_receipt_number": 412}

summary adds up the summarised columns across the whole report, plus total_records. A field the data source doesn't have is left out. results holds up to 1,000 rows; for more, export it.

Saved reports work the same way. Get a report returns its definition under report, so you can send that to run.json with a new period. List reports, Create a report and Update a report manage them.

Export to CSV

Export a report with POST /report/export.json takes the same body and returns a CSV file of up to 10,000 rows. The column headings are your columns' names.

curl -X POST "https://api.smoothpos.net/report/export.json" \
  -H "X-Smoothpos-Auth: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d @sales-by-store.json -o sales-by-store.csv

Some lists have their own exports too, like Export journal entries. See Accounting sync.

Example: A weekly report, saved as a file

Glue's Email step can't attach files, so the way to share a scheduled report is to save it as a file and send the link.

Trigger: Scheduled, every Monday morning.

  1. API Request: Get Private Data for your Slack webhook, Response variable slack_res.

  2. API Request: Get a report for your saved report's ID, Response variable report_res.

  3. Script: Set last week's dates

    var def = JSON.parse($.report_res.body).report;
    var now = new Date();
    var end = new Date(now.getFullYear(), now.getMonth(), now.getDate() - now.getDay());
    var start = new Date(end.getFullYear(), end.getMonth(), end.getDate() - 7);
    function day(d) { return d.toISOString().slice(0, 10); }
    def.period = {from_date: day(start) + "T00:00:00", to_date: day(end) + "T00:00:00", timezone: "Australia/Sydney"};
    $.run_body = JSON.stringify(def);
    $.week = day(start);
    var pd = JSON.parse($.slack_res.body);
    $.slack_url = pd[Object.keys(pd)[0]].trim();
  4. API Request: Run a report, Body {{ run_body }}, Response variable run_res.

  5. Script: Read the rows: $.rows = JSON.parse($.run_res.body).results;

  6. File: Write the CSV, Operation Write, Format CSV, Source rows, Destination Cloud storage, Filename weekly-sales-{{ week }}.csv, Response variable file.

  7. Send Notification: Share the link, Channel Slack, Webhook URL {{ slack_url }}, Title Weekly sales, Message Last week's sales report: {{ file.url }}.

See File step for writing to FTP or SFTP instead, and Send Notification step for the message.


Did this page help you?