# Sales summary

Operational sales metrics for a date range, optionally grouped by day, week or month.

Section: Commerce · REST API
Canonical URL: https://docs.omniretailconnect.nl/commerce/api/sales-summary

> **Warning — Operational metrics, not accounting revenue:** These figures are for monitoring. They will not necessarily reconcile with finance reports, and no field is called revenue. The exact rules are returned with every response in `definitions` and explained below.

## Get a sales summary

`GET /api/v1/sales-summary` — Needs commerce.read. Equivalent MCP tool: get_sales_summary.

*Query parameters of GET /sales-summary*

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `from` | date | Required | `YYYY-MM-DD`, Europe/Amsterdam, inclusive. |
| `to` | date | Required | `YYYY-MM-DD`, Europe/Amsterdam, inclusive. Not before `from`; at most 366 days in total. |
| `group_by` | string | Optional | `none` (default), `day`, `week` (ISO, starting Monday) or `month`. |

**Daily sales summary · curl**

```bash
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/sales-summary?from=2026-10-01&to=2026-10-07&group_by=day" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"
```

**Daily sales summary · JavaScript**

```javascript
// Server-side code only: never put the token in browser code.
// Top-level await needs an ES module (a .mjs file or "type": "module").
const token = process.env.COMMERCE_API_TOKEN; // ocm_<token>

const response = await fetch('https://<your-shop-domain>/api/v1/sales-summary?from=2026-10-01&to=2026-10-07&group_by=day', {
  headers: { Authorization: `Bearer ${token}` },
});

console.log(response.status, await response.json());
```

**Daily sales summary · PHP**

```php
<?php

$token = getenv('COMMERCE_API_TOKEN'); // ocm_<token>

$curl = curl_init('https://<your-shop-domain>/api/v1/sales-summary?from=2026-10-01&to=2026-10-07&group_by=day');
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],
]);

$body = json_decode(curl_exec($curl), true);

echo curl_getinfo($curl, CURLINFO_RESPONSE_CODE), PHP_EOL;
print_r($body);
```

**Daily sales summary · Python**

```python
import json
import os
import urllib.error
import urllib.request

token = os.environ["COMMERCE_API_TOKEN"]  # ocm_<token>

request = urllib.request.Request(
    "https://<your-shop-domain>/api/v1/sales-summary?from=2026-10-01&to=2026-10-07&group_by=day",
    headers={"Authorization": f"Bearer {token}"},
)

try:
    with urllib.request.urlopen(request) as response:
        print(response.status, json.load(response))
except urllib.error.HTTPError as error:
    # Errors are JSON too: {"error": {"code": ..., "message": ...}}
    print(error.code, json.load(error))
```

**Example response (group_by=day) · 200**

```json
{
    "data": {
        "currency": "EUR",
        "period": {
            "from": "2026-10-01",
            "to": "2026-10-07",
            "timezone": "Europe/Amsterdam"
        },
        "orders_count": 42,
        "gross_sales": {
            "amount_minor": 318450,
            "currency": "EUR",
            "formatted": "€ 3.184,50"
        },
        "refunded": {
            "amount_minor": 12900,
            "currency": "EUR",
            "formatted": "€ 129,00"
        },
        "net_sales": {
            "amount_minor": 305550,
            "currency": "EUR",
            "formatted": "€ 3.055,50"
        },
        "average_order_value": {
            "amount_minor": 7582,
            "currency": "EUR",
            "formatted": "€ 75,82"
        },
        "buckets": [
            {
                "start": "2026-10-01",
                "orders_count": 7,
                "gross_sales": {
                    "amount_minor": 52210,
                    "currency": "EUR",
                    "formatted": "€ 522,10"
                },
                "refunded": {
                    "amount_minor": 0,
                    "currency": "EUR",
                    "formatted": "€ 0,00"
                },
                "net_sales": {
                    "amount_minor": 52210,
                    "currency": "EUR",
                    "formatted": "€ 522,10"
                }
            },
            {
                "start": "2026-10-02",
                "orders_count": 5,
                "gross_sales": {
                    "amount_minor": 38900,
                    "currency": "EUR",
                    "formatted": "€ 389,00"
                },
                "refunded": {
                    "amount_minor": 12900,
                    "currency": "EUR",
                    "formatted": "€ 129,00"
                },
                "net_sales": {
                    "amount_minor": 26000,
                    "currency": "EUR",
                    "formatted": "€ 260,00"
                }
            }
        ],
        "definitions": [
            "Operational sales metrics for monitoring. Not accounting revenue; will not necessarily reconcile with finance reports.",
            "All amounts are integer minor units in EUR and tax-inclusive. Days are Europe/Amsterdam calendar days; from and to are both inclusive.",
            "orders_count: orders whose placed_at lies within the period and whose status is not cancelled. Orders are counted by order date (placed_at); orders that were never placed are excluded."
        ]
    }
}
```

Shortened for readability: 2 of 7 daily buckets are shown, so the buckets shown do not add up to the totals. In a real response the buckets add up to the totals.

*Fields of data*

| Field | Type | Description |
| --- | --- | --- |
| `currency` | string | `EUR`. |
| `period` | object | `from`, `to` and `timezone`. |
| `orders_count` | integer | Orders placed in the period, counted by order date. Cancelled orders are excluded. |
| `gross_sales` | money | Sum of the tax-inclusive totals of the counted orders. |
| `refunded` | money | Succeeded refunds, counted by refund date. |
| `net_sales` | money | `gross_sales` minus `refunded`. Can be negative. |
| `average_order_value` | money | `gross_sales` divided by `orders_count`, rounded; 0 without orders. |
| `buckets` | array | Empty for `group_by=none`; otherwise one entry per day, week or month with `start`, `orders_count`, `gross_sales`, `refunded` and `net_sales`. |
| `definitions` | array of string | The rules of the metrics as text. |

## How the metrics are defined

- **Orders** are counted by order date (`placed_at`), in Europe/Amsterdam calendar days with both ends inclusive. Cancelled orders and orders that were never placed are excluded.
- **Refunds** are counted by refund date, regardless of when the order was placed. This includes refunds of orders placed before the period and of orders that are now cancelled. Pending and failed refunds are ignored.
- **All amounts** are integer minor units in EUR and tax-inclusive.
- **Buckets** are contiguous and zero-filled over the whole period: a day, an ISO week starting Monday, or a calendar month. `start` is the first local date of the bucket; the first bucket starts at `from` even when that is mid-week or mid-month. Bucket sums add up to the totals.
- A day is a calendar day in Europe/Amsterdam, so it is 23 or 25 hours long on the two daylight-saving changes. The 366-day limit counts calendar days.
- The response contains no customer data.

## Errors

*Errors of GET /sales-summary*

| Status | error.code | When |
| --- | --- | --- |
| `401` | `unauthenticated` | Missing, malformed, unknown, revoked or expired token, or a token without the `api` channel. |
| `422` | `invalid_argument` | A missing or malformed date, `to` before `from`, a period longer than 366 days, an unknown `group_by` or an unknown parameter. |
| `429` | `too_many_requests` | Rate limit reached. Wait the number of seconds in `Retry-After`. |

The same figures are available over MCP as [get_sales_summary](https://docs.omniretailconnect.nl/commerce/mcp/tools.md#get_sales_summary).
