Reports

Sales and team analytics over a date range — order list with profit, dynamics, average deal, revenue, loss reasons, funnel conversion, forecast, team productivity and low stock.

Reports aggregate the project's orders over a period. All money figures come from order products:

line amount   = sale_price × quantity × (1 + markup_percent/100) × (1 − discount_percent/100)
order amount  = Σ line amount
profit        = Σ (unit price after markup and discount − purchase_price) × quantity

Soft-deleted orders are never counted. Orders are assigned to a period by their creation date.

Field types follow the field notation.

Access

Every endpoint on this page requires both the projects and reports OAuth scopes and the can_view_reports permission (the owner bypasses it). The user must be a member of the project, and the owner's subscription must be active. Otherwise the endpoint returns 404 RES_NOT_FOUND.

Reports cover all orders of the project and include purchase prices and profit, regardless of the user's order visibility and price permissions. Grant can_view_reports accordingly.

Common query params

Order reports accept these query parameters:

Field Type Description
from?, to? date Period, YYYY-MM-DD, both inclusive. Both must be given; otherwise the current month is used
funnel? uuid Only orders currently in a status of this funnel. Default: all funnels (except Funnel Stages)
contacts? string Contact UUIDs separated by ;
statuses? string Status UUIDs separated by ;

The period is clamped: it cannot start more than a year ago (it then starts at the beginning of that month) or end in the future. Every response echoes the period actually used:

{
  "filter": {
    "date": {
      "from": "2026-09-01",
      "to": "2026-09-30"
    },
    "funnel": null,
    "contacts": [],
    "statuses": []
  },
  "data": "..."
}

Orders

GET /api/v1/projects/{project.uuid}/reports/orders

Lists orders in the period with their money figures, newest first. Takes page? and limit? (1–100, default 50) in addition to the common params.

data is an array of:

Field Type Description
uuid uuid Order ID
serial string Order number
contact_uuid ?uuid Contact
contact_name ?string Contact name
user_uuid ?uuid Responsible member
user_name string Their name
status_uuid ?uuid Status
status_name ?string Status name
status_color ?string Status colour
status_type ?string new, active, done or canceled
order_amount decimal Order amount
purchase_amount decimal Σ purchase_price × quantity
profit decimal Profit
profit_percent decimal Profit as a percent of the order amount
potential_revenue decimal order_amount for new/active orders, else 0
paid_amount decimal Amount paid
remaining_amount decimal Amount still owed; for a canceled order, minus what was paid (a refund owed)
created_at ISO8601 datetime Created
updated_at ISO8601 datetime Last changed

Dynamics

GET /api/v1/projects/{project.uuid}/reports/orders/dynamics

Order totals per period bucket. Extra param groupBy?: day, week or month. Default: day for periods up to 31 days, week up to 90, month beyond. The value used is echoed as filter.group_by.

data is an array, oldest bucket first, with only buckets that have orders:

Field Type Description
period ISO8601 datetime Start of the bucket
total_orders integer Orders created
done_orders integer Of those, now in a done status
canceled_orders integer Of those, now in a canceled status
total_amount decimal Σ order amount
total_profit decimal Σ profit
total_paid decimal Σ paid
avg_amount decimal Average order amount

Average Deal

GET /api/v1/projects/{project.uuid}/reports/orders/avg-deal

data: {total_orders, avg_amount} over all orders in the period, whatever their status.

Revenue

GET /api/v1/projects/{project.uuid}/reports/orders/revenue

Revenue and gross profit. What counts as revenue depends on the project's report_settings.revenue_mode:

  • pipeline (default): every order that is not canceled;
  • actual: only orders in a done status. Open orders are reported separately as potential_revenue.
Field Type Description
revenue_mode string The mode used
revenue decimal Σ order amount of the counted orders
cogs decimal Σ purchase cost of the counted orders
gross_profit decimal revenue − cogs
gross_margin_percent decimal gross_profit / revenue × 100, 2 decimals
potential_revenue ?decimal actual mode: amount of new/active orders. null in pipeline
total_paid decimal Σ paid over all orders in the period
total_remaining decimal Σ still owed; canceled orders count only as refunds owed

Loss Reasons

GET /api/v1/projects/{project.uuid}/reports/orders/loss-reasons

Canceled orders grouped by loss reason, biggest loss first.

{
  "filter": {
    "...": "..."
  },
  "data": {
    "reasons": [
      {
        "loss_reason_uuid": "0199a1bb-0001-7000-8000-000000000001",
        "loss_reason_name": "Too expensive",
        "orders_count": 7,
        "lost_revenue": 12450
      },
      {
        "loss_reason_uuid": null,
        "loss_reason_name": null,
        "orders_count": 2,
        "lost_revenue": 980
      }
    ],
    "total_orders": 9,
    "total_lost_revenue": 13430
  }
}

The entry with null uuid collects canceled orders without a reason.

Funnel Stages

GET /api/v1/projects/{project.uuid}/reports/orders/funnel-stages

Conversion through the stages of one funnel: funnel if given, else the default funnel. Takes the orders created in the period that are now in that funnel, and follows their status history.

Stages are the funnel's new, active and done statuses in order. An order counts as having reached a stage if it reached it or any later stage.

Field Type Description
total_orders integer Orders in the cohort
stages[].uuid uuid Status
stages[].name string Status name
stages[].reached_count integer Orders that reached this stage or later
stages[].pct_of_first decimal Percent of the first stage, 1 decimal
stages[].pct_of_previous ?decimal Percent of the previous stage; null for the first
stages[].dropped_to_canceled integer Orders moved from this stage straight to a canceled status

Forecast

GET /api/v1/projects/{project.uuid}/reports/orders/forecast

A linear trend over monthly order amounts in the period, projected 3 months ahead, plus the current open pipeline.

Field Type Description
history[] array {period, total_amount} per month with orders
forecast[] array {period, predicted_amount} for the next 3 months; empty with under 2 months of history
trend ?object {slope, direction}, direction is up, down or flat; null with under 2 months
pipeline.amount decimal Amount of all orders now in new/active statuses, regardless of the period
pipeline.orders_count integer Their number

statuses does not apply to the pipeline.

Productivity

GET /api/v1/projects/{project.uuid}/reports/productivity

Per-member results for the period, compared with the previous period of the same length. Takes only from and to. filter also contains prev_date and revenue_mode.

data is an array, top seller first:

Field Type Description
user_uuid uuid Member
user_name string Name
user_email string Email
total_orders integer Orders they are responsible for, created in the period
closed_orders integer Of those, in a done status
total_sales decimal Σ order amount, counted per the project's revenue mode
total_profit decimal Σ profit, same rule
total_paid decimal Σ paid
avg_margin_percent decimal total_profit / total_sales × 100
total_tasks integer Tasks assigned to them, created in the period
closed_tasks integer Of those, done
prev_total_sales decimal total_sales in the previous period
prev_total_orders integer total_orders in the previous period
sales_growth decimal Percent change of total_sales; 100 if the previous period had none
orders_growth decimal Percent change of total_orders; 100 if the previous period had none

Members with tasks but no orders appear with zero sales. Orders without a responsible member are not counted.

Low Stock

GET /api/v1/projects/{project.uuid}/reports/low-stock

Stock items in any warehouse that are below their min_stock (only where min_stock > 0). Takes no parameters.

{
  "data": [
    {
      "uuid": "0199a0d0-0000-7000-8000-000000000001",
      "item_uuid": "0199a2c0-7a1b-7c2d-8e3f-a0b1c2d3e4f5",
      "item_type": "material",
      "warehouse_uuid": "0199a0b1-3c4d-7e5f-8a9b-0c1d2e3f4a5b",
      "warehouse_name": "Main warehouse",
      "name": "Blackout 605",
      "unit": "sm",
      "quantity": 8,
      "min_stock": 20,
      "deficit": -12
    }
  ]
}

deficit is quantity − min_stock, so it is negative.

Errors

Status Code When
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or reports
403 AUTH_NO_PROJECT_ACCESS The user's role lacks can_view_reports
404 RES_NOT_FOUND The project does not exist or is not accessible

Invalid dates or filters do not cause errors: they are ignored and the defaults apply.