Reports
Reports (/reports) are tabular data exports. A report is built in one of two ways:
- builder — columns (including fields of related entities), filters, grouping with subtotals, and sorting;
- SQL query — a
SELECTover the data of all tenant entities, see SQL reports.
A finished report can be viewed in the UI, exported to CSV / XLSX / PDF, and emailed on a schedule — the same way for both.
Reports complement dashboards and charts: a chart answers "how many and how it changes", a report answers "which exact records and with what values" — in a form ready to hand to a manager or a customer.
Who can do what
| Permission / role | Capabilities |
|---|---|
reports.view | See and run reports shared with you |
reports.build | Create and edit your own builder reports |
reports.sql | Create and edit your own SQL reports |
Report Builder role (report_builder) | Preset bundle: reports.build + reports.view |
Report Administrator role (report_administrator) | Same + the admin.reports section permission — administers all reports, SQL included |
Administrator role (admin) | Full access to all reports |
Data is filtered by permissions
A builder report only shows records visible to the user running it under the record access rules. Scheduled reports run as the report owner — email recipients see data within the owner's permissions.
An SQL report reads all records of the tenant's entities: record access rules do not apply to the query. That is why reports.sql is a permission of its own and the Report Builder role does not include it.
Creating a report
- Open the Reports section
- Click Create report
- On the General tab set the title, description and the entity — the data source. With the
reports.sqlpermission you also choose how the report is built: Builder or SQL query (see SQL reports)
TIP
The entity cannot be changed after creation: columns, filters and groupings are bound to its schema. For another entity, create a new report.
The report is then configured tab by tab.
Columns
Add column opens the field tree:
- fields of the entity itself — any field from the builder;
- fields of related entities — through reference fields you can reach into the related record (up to 2 hops), e.g.
Ticket → Client → Company name; - each column can have a custom Column label.
Add related aggregate creates a computed column over child records: count, sum, average, min or max of a numeric field of a related entity, optionally with conditions on which child records to include. Example: an "Open deals" column in a clients report.
Filters
Each filter is a field (own or related), an operator and a value. Available operators: equals / not equals, contains, comparisons (>, <, >=, <=), "one of", date range, relative period (Today, Yesterday, This/Last week, Last N hours/days, …), between, empty / not empty, by category, related records exist.
A filter can be marked as a User parameter — it becomes editable at view time: the report viewer can substitute their own value (e.g. pick a period) without edit rights on the report itself. The Required flag prevents running the report until the parameter is filled in.
Grouping & Totals
Up to 3 grouping levels, each with its own sort direction. A grouped report can show totals per group and for the whole report:
count— number of records;sum,avg,min,max— over numeric fields;- percentiles
p50,p90,p95,p99… — over numeric fields (e.g. "90% of tickets are resolved faster than X hours").
A total is rendered in the column where the aggregated field is shown, so the field must be one of the report columns.
Sorting
A list of fields with direction — applied within groups, or to the whole report when there is no grouping.
Access
By default only the owner (and administrators) see the report. The Access tab shares it with:
- specific users;
- roles;
- groups.
Grantees see the report in their list and can run it (with their own parameter values) but cannot edit it.
Schedules
The Schedules tab automates delivery:
| Field | Description |
|---|---|
| Cron expression | Frequency in cron format, e.g. 0 8 * * 1 — Mondays at 08:00 |
| Timezone | Timezone the cron expression is evaluated in |
| Format | CSV, XLSX or PDF |
| Recipients | Email addresses and/or system users (can be mixed) |
| Parameters | Parameter-filter values for this schedule |
| Active | A disabled schedule is kept but never fires |
Run now fires the schedule immediately — handy for checking the email without waiting for cron.
Files up to 10 MB arrive as attachments; larger ones come as a download link (valid for 7 days).
SQL reports
An SQL report is a SELECT query over the tenant's data. The way a report is built is chosen at creation and does not change later. Tabs of an SQL report: General, Query, Parameters, Access, Schedules.
The Query tab
On the left is the schema: the tables the query can read and their columns with types. Clicking a name inserts it into the editor. The editor completes table names, columns (also after alias.) and parameters (after :).
Run (or Ctrl+Enter in the editor) runs the unsaved text and shows the first 200 rows, the execution time, and the Postgres error with its line number if the query failed.
Tables the query can read
| Table | Contents |
|---|---|
<entity name> | One table per entity, named by the entity's system name (table_name). Columns: id, uid, created_at, updated_at, created_by, updated_by, then one column per field and data — all record values as JSONB |
<entity>_<relation>_<entity> | Many-to-many relations: id, l_table_id, r_table_id, created_at |
sys_users | Tenant users: id, name, first_name, last_name, middle_name, email, position, company_id, org_unit_id, manager_id, created_at |
sys_companies | Tenant companies: id, name, code, is_external |
sys_org_units | Tenant org units: id, name, company_id, parent_id |
sys_field_options | Option labels of list and status fields: entity, field, value, label (in the viewer's language), labels (all languages) |
Field column types:
| Field type | Column type |
|---|---|
| Integer, float, currency, number | numeric |
| Boolean | boolean |
| Date | date |
| Date and time | timestamptz |
| Reference to a record, user, company, org unit, category (single value) | bigint — the record id |
| Any multi-value field, files, images, coordinates | jsonb |
| Everything else | text |
A value that does not convert to the column type (e.g. text in a numeric field) comes out as NULL; the original stays in data.
Example — tickets per assignee with the status label:
SELECT u.name AS "Assignee",
coalesce(st.label, i.status) AS "Status",
count(*) AS "Tickets"
FROM incidents i
LEFT JOIN sys_users u ON u.id = i.assigned_to
LEFT JOIN sys_field_options st
ON st.entity = 'incidents' AND st.field = 'status' AND st.value = i.status
WHERE i.created_at >= :period_from AND i.created_at < :period_to
GROUP BY 1, 2
ORDER BY 3 DESCResult column headers are the names or aliases from SELECT (AS "Header"). An SQL report computes grouping and totals in the query itself (GROUP BY, ROLLUP, window functions) — it has no builder tabs.
Parameters
A parameter is a value the viewer enters before running. In the query it is written as :name. Types:
| Type | In the query |
|---|---|
| Text | :name — text |
| Number | :name — numeric |
| Date | :name — date |
| Period | :name_from and :name_to — bounds of the chosen period (timestamptz), the end is exclusive: WHERE created_at >= :name_from AND created_at < :name_to |
Periods are the same as the builder's relative-period filter; bounds are computed in the viewer's time zone, and in the schedule's time zone for scheduled runs.
An optional parameter left empty is passed as NULL, so a condition on it is written as (:status IS NULL OR status = :status). The default value is used when the viewer did not change the parameter, and in scheduled runs. When the query uses an undeclared :name, the Parameters tab shows a button that adds it.
Query limits
- One statement: anything after
;is an error. - Read only: the query starts with
SELECT,WITH,VALUESorTABLEand runs in a read-only transaction. - Schema tables only: the query runs under a separate database role that has no access to other tables or to other tenants' data.
- Time: 30 seconds for the view and the Run button, 300 seconds for exports and schedules (
REPORTS_SQL_TIMEOUT,REPORTS_SQL_EXPORT_TIMEOUT). A longer query is stopped with an error. - A colon before a name (
:name) is always a parameter, a double colon (::text) is a type cast. In an array slice put a space after the colon:arr[1: n].
Access to an SQL report
Assigned users, roles and groups see everything the query returns — there is no filter by their permissions. They do not see the query text: a viewer gets the parameter bar and the result only.
Viewing and exporting
An opened report (/reports/view/…) renders data right in the UI — with groups, totals and a parameters panel when configured. The synchronous view is capped at 10,000 rows; for more data, use export.
Export runs in the background: click the format button — the job goes to a queue, and the finished file appears in the report's runs list for download. Format limits:
| Format | Row limit | Notes |
|---|---|---|
| CSV | practically unlimited | Fastest and lightest |
| XLSX | 50,000 | Excel-ready: headers, groups, totals |
| 1,000 | For printing/signing; for larger volumes use XLSX/CSV |
Generated files are kept for 30 days, then removed automatically. The run history can also be cleared manually.
Business-case examples
Weekly digest for a support manager
Entity Tickets:
- Columns: number, subject, status, assignee, created date;
- Filter: "Created date" → relative period → Last week;
- Grouping: by assignee; total —
count(how many tickets each person handled); - Schedule:
0 8 * * 1, XLSX, recipient — the team lead.
Every Monday at 08:00 the manager gets an email with a ready-made table for the past week.
SLA control via percentiles
Entity Tickets, assuming an automation or a process writes a numeric "Resolution time, h" field:
- Columns: category, resolution time;
- Parameter filter: "Closed date" → date range (the viewer picks the period);
- Grouping: by category; totals —
avgandp90of resolution time.
The report answers "90% of tickets in which category do we resolve faster than N hours" — more honest than an average, which masks outliers.
Clients with open deals (related aggregate)
Entity Clients from the CRM preset:
- Columns: name, manager + related aggregates:
countof deals with condition "status = Open" andsumof the "Amount" field of the same deals; - Sorting: by open deal amount, descending;
- Access: the sales role.
The result is a live "pipeline by client" that every manager opens with their own permissions — record access rules automatically narrow the list to their clients.
See also
- Dashboards and charts — real-time visual analytics
- Record access — how permissions shape report contents
- Users, groups, roles — assigning the
report_builder/report_administratorroles