---
title: "Retrieval Aggregate"
method: POST
path: "/api/v1/retrieval/aggregate"
tags: ["Retrieval"]
---

# Retrieval Aggregate

`POST /api/v1/retrieval/aggregate`

## Request

- Base URL: `https://api.gemina.co` (the document declares no server; this is the origin it was published from)
- URL: `https://api.gemina.co/api/v1/retrieval/aggregate`
- Auth: one of:
  - API key in header `X-API-Key`
  - OAuth 2 (password, token URL `token`)

Sums/averages/counts/group-bys over the tenant's document index.

Deterministic DSL compiled server-side against whitelisted columns.
When money metrics are requested without a currency filter/grouping, the
server groups by currency automatically (mixed-currency sums are wrong by
construction) and flags it in ``meta.currencyGroupingAdded``. Same dual
auth as /query (API key, or Bearer token with enforced end-user scope).

## Request body

- RetrievalAggregateInDTO
  - `metrics` AggregateMetricDTO[], required
    - `op` 'sum' | 'avg' | 'min' | 'max' | 'count', required
    - `field` 'total_amount' | 'net_amount' | 'vat_amount' | 'measurable_quantity' | 'line_total', nullable
  - `groupBy` string[]
  - `filters` RetrievalFiltersDTO: Structured filters over promoted index columns. Tenant scope is NOT part of this DTO by design: ``user_id`` always comes from the authenticated context, server-side. ``end_user_id`` is a trusted within-tenant filter on the server-to-server path (PRD §6.6).
    - `vendorName` string, nullable
    - `vendorNames` string[], nullable
    - `docNumbers` string[], nullable
    - `documentIds` string[], nullable
    - `documentTypes` string[], nullable
    - `currencies` string[], nullable
    - `vendorTaxId` string, nullable
    - `buyerName` string, nullable
    - `docNumber` string, nullable
    - `documentType` string, nullable
    - `currency` string, nullable
    - `expenseType` string, nullable
    - `paymentMethod` string, nullable
    - `externalId` string, nullable
    - `endUserId` string, nullable
    - `documentId` string, uuid, nullable
    - `issueDateFrom` string, date, nullable
    - `issueDateTo` string, date, nullable
    - `dueDateFrom` string, date, nullable
    - `dueDateTo` string, date, nullable
    - `totalAmountMin` number, nullable
    - `totalAmountMax` number, nullable
  - `lineFilters` StructuredFilterDTO[]: Line-item scope only: conditions over each line-item chunk's structured fields (description, measurable_uom, quantity, ...). Same shape and ops as structured_filters. Presence of any condition switches the request to line-item scope.
    - `path` string, required
    - `op` 'eq' | 'neq' | 'gt' | 'lt' | 'contains' | 'exists' | 'in', required
    - `value` union
      - string
      - integer
      - number
      - boolean
      - union[]
        - union
          - string
          - integer
          - number
  - `limit` integer

## Response `200`

Successful Response

- RetrievalAggregateOutDTO
  - `servedAt` string, date-time
  - `servedAtTimestamp` number
  - `createdAt` string, date-time, nullable
  - `createdAtTimestamp` number, nullable
  - `rows` AggregateRowDTO[]
    - `group` object
    - `values` object
  - `meta` AggregateMetaDTO, required
    - `count` integer, required
    - `currencyGroupingAdded` boolean: True when the server added 'currency' to group_by because money metrics (document totals, or line_total in line-item scope) were requested without a currency filter — amounts in different currencies must never be summed together.
    - `duplicatesIgnored` integer: Count of duplicate index rows collapsed before aggregating — the same invoice (vendor tax id + document number + issue date) indexed more than once. >0 means the tenant has re-uploaded documents.
    - `uomGroupingAdded` boolean: True when the server added 'measurable_uom' to group_by because a measurable_quantity metric was requested without a unit filter or grouping — quantities in different units must never be summed together.
    - `linesWithoutUnitData` integer: Line-item scope only: matching line items that carry no unit data (indexed before unit extraction shipped, or unit indeterminate) and are therefore excluded from measurable sums. >0 means the aggregate undercounts older documents.

## Other responses

- `422`: Validation Error
- `429`: Rate or quota limit exceeded; `errors[0].error_code` says which. Retryable limits (`FILETAG_RATE_LIMIT_EXCEEDED`, burst limits) send a `Retry-After` header: back off and retry after it. `CREDIT_EXHAUSTED` for an account with no active subscription or trial sends no `Retry-After` and is not resolved by waiting: start the free trial (once per account) or choose a plan in the console.

---

[API](https://skmtc.dev/gemina/apis/gemina-api.md) · [All operations](https://skmtc.dev/gemina/apis/gemina-api/llms.txt) · [OpenAPI document](https://skmtc.dev/gemina/apis/gemina-api/revisions/152fd3f6f6ec?raw)
