> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dakota.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Client Insights

> One deterministic report over your whole customer book: portfolio KPIs with trends, daily series for charts, cross-customer findings, and a per-customer roll-up to drill into

[Account Insights](/documentation/agentic-payments/insights) answers *"how is this customer doing?"*. Client Insights answers the question you ask as the operator of many customers: *"across my whole book, what is trending, what is anomalous, and what needs attention next?"*

* **`GET /insights`** — a computed report over **every customer under your API key**: snapshot KPIs with previous-period deltas, daily time series, observations and advisory suggestions attributed to the customer they concern, and a roll-up row per customer. **No LLM anywhere in this path** — every number, date and sentence is computed deterministically from your customers' own objects.

<Info>
  **Beta** — part of the agentic early-access surface, like the rest of the Insights tag. The report is read-only by construction: it cannot move money, create, or change anything. It reads scheduled payments, mandates, wallets, customers and recipients, so it needs an **`admin`-role API key** — a `viewer` key is refused with `403`.
</Info>

## The report

The report is a **view, not a stored object** — computed on demand from your customers' scheduled payments, mandates, recipients, wallets and balances. There is nothing to create, poll into existence, or clean up. The client is resolved from the API key; there is no path parameter.

```bash theme={null}
curl "https://api.platform.dakota.xyz/insights?window_days=14" \
  -H "X-API-Key: $DAKOTA_API_KEY"
```

```json theme={null}
{
  "generated_at": 1788337300,
  "window_days": 14,
  "snapshot": {
    "customers": { "total": 42, "scanned": 42, "with_activity": 17, "with_critical": 2 },
    "total_usd": "1284500.00",
    "upcoming": { "days": 14, "count": 31, "totals": { "USDC": "412600" } },
    "open_scheduled_payments": 44,
    "active_mandates": 57,
    "metrics": [
      { "key": "executed_volume", "label": "Executed volume", "asset": "USDC",
        "value": "861200", "previous": "702300", "change_pct": "22.6" },
      { "key": "failed_payments", "label": "Payments failed",
        "value": "9", "previous": "3", "change_pct": "200.0" }
    ]
  },
  "series": {
    "bucket": "day",
    "from": 1787097600,
    "to": 1789516800,
    "metrics": {
      "failed_payments": [ { "t": 1787097600, "v": "0" }, { "t": 1787184000, "v": "3" } ],
      "executed_volume.USDC": [ { "t": 1787097600, "v": "41200" }, { "t": 1787184000, "v": "63800" } ]
    }
  },
  "facets": {
    "kinds": ["mandate_expiring", "payment_failures_clustered", "recipient_dormant"],
    "severities": ["info", "warn"],
    "responsibilities": ["compliance", "payment_ops"],
    "assets": ["USDC"]
  },
  "insights": [
    {
      "kind": "payment_failures_clustered",
      "severity": "warn",
      "responsibility": "payment_ops",
      "message": "7 payments across 2 customers failed in the last 14 days with the same failure code: mandate_denied (e.g. \"transaction denied by mandate: amount 5000 exceeds max_per_tx 2500\").",
      "detail": {
        "count": 7, "window_days": 14,
        "failure_code": "mandate_denied",
        "failure_reason": "transaction denied by mandate: amount 5000 exceeds max_per_tx 2500",
        "docs_url": "https://docs.dakota.xyz/documentation/agentic-payments/webhooks#failure_code-reference",
        "customer_ids": ["2vWxCustomer0000000000000000", "2vWxCustomer0000000000000001"]
      },
      "evidence": [ { "type": "scheduled_payment", "id": "2vWxPayment00000000000000000" } ]
    },
    {
      "kind": "recipient_dormant",
      "severity": "info",
      "responsibility": "compliance",
      "customer_id": "2vWxCustomer0000000000000000",
      "message": "Recipient \"Northwind Ltd\" hasn't been paid in 123 days (last payment 2026-05-02, 14 payment(s) before that).",
      "detail": { "recipient_id": "2vWxRecipient000000000000000", "recipient_name": "Northwind Ltd",
                  "days_since_last_use": 123, "last_used_at": 1777500000, "prior_payments": 14 },
      "evidence": [ { "type": "recipient", "id": "2vWxRecipient000000000000000" } ]
    }
  ],
  "suggestions": [
    {
      "kind": "mandate_expiring",
      "severity": "warn",
      "responsibility": "payment_ops",
      "customer_id": "2vWxCustomer0000000000000000",
      "message": "Mandate for USDC on base expires in 6 day(s) (2026-09-07). 3 open scheduled payment(s) depend on it. Mandates are immutable — a replacement needs a new signature.",
      "detail": { "mandate_id": "2vWxMandate00000000000000000", "days_left": 6, "valid_until": 1788855700, "dependent_payments": 3 },
      "evidence": [ { "type": "mandate", "id": "2vWxMandate00000000000000000" } ]
    }
  ],
  "customers": [
    {
      "customer_id": "2vWxCustomer0000000000000000",
      "name": "Acme Robotics",
      "total_usd": "84200.00",
      "open_scheduled_payments": 6,
      "active_mandates": 4,
      "upcoming": { "days": 14, "count": 5, "totals": { "USDC": "9400" } },
      "item_counts": { "critical": 1, "warn": 1, "info": 1 },
      "last_activity_at": 1788310000
    }
  ]
}
```

Six parts:

* **`snapshot`** — typed facts for stat cards. `customers` describes the population behind the report (see [Scan cap](#scan-cap)); `metrics[]` are the KPIs over the window with the previous window's value and a `change_pct` for trend rendering. Count metrics (`executed_payments`, `failed_payments`, `new_counterparties`) appear once; amount metrics (`executed_volume`) appear once **per asset**. With `?customer_id=` the snapshot also carries that customer's per-wallet `balances`.
* **`series`** — daily buckets for charts. Keys are an open set: `<metric>` for counts and `<metric>.<ASSET>` for amounts (`executed_payments`, `failed_payments`, `executed_volume.<ASSET>` over the window, and `upcoming_obligations.<ASSET>` looking forward over the next `window_days` — an overdue open payment is not plotted there; it is counted in `snapshot.upcoming` and surfaces as `payment_at_risk`). `t` is the bucket's UTC midnight — format labels in UTC.
* **`facets`** — the kinds, severities, responsibilities and assets present in the report, computed **before** any item filter is applied, so a filter menu keeps offering every option while a filter is active.
* **`insights[]`** — observations: what *is happening*, across the book.
* **`suggestions[]`** — advice: what a customer *may want to do*. Always advisory — acting on one is a separate, human-gated step through the normal payment flow.
* **`customers[]`** — one roll-up row per scanned customer (balance, open payments, active mandates, upcoming totals, finding counts by severity, last executed payment), sorted worst-first: critical count, then warn count, then most recent activity. Use it for the drill-down table.

### Items are the customer report's items

`insights[]` and `suggestions[]` use the **same item schema** as [Account Insights](/documentation/agentic-payments/insights#rendering-items) — `{kind, severity, message, detail, evidence}` — with two additive fields at this scope:

* **`customer_id`** — the customer the item is about. **Omitted on cross-customer aggregates**, which list the affected customers in `detail.customer_ids` instead.
* **`responsibility`** — a coarse grouping label (`payment_ops` or `compliance`) for filtering and routing. It is a label only; it carries no ownership semantics.

Everything that holds for the customer report holds here: `severity` is closed (`info | warn | critical`), `kind` and `evidence.type` are **open sets** (render an unrecognized value generically, never drop it), `message` is a complete sentence, `detail` carries the raw values behind it.

### What the client report adds

Every kind in the [customer kind catalog](/documentation/agentic-payments/insights#the-kind-catalog) appears here per customer. On top of those:

| kind                                          | array    | severity | fires when                                                                                                                                                                                                                                                                |
| --------------------------------------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `payment_failures_clustered` (cross-customer) | insights | warn     | one failure code hits **two or more customers** in the window — the roll-up a per-customer report cannot see. No `customer_id`; `detail.customer_ids` lists them                                                                                                          |
| `volume_anomaly`                              | insights | warn     | a customer's executed volume in the window is ≥5× its **own** baseline (the preceding 90 days, scaled to the window), per asset — a velocity observation, never a screening verdict. A customer with no prior volume in that asset has no baseline and is never anomalous |
| `recipient_dormant`                           | insights | info     | a previously-paid recipient has gone unpaid for 90+ days — `detail` carries `days_since_last_use`, `last_used_at` and `prior_payments`. At most the **5 most-dormant payees per customer** are reported. Payees with no payment history are not dormant, just unused      |

**Failure items carry the failure code.** Clustered failure items add `detail.failure_code` — the same stable code the [`scheduled_payment.failed` webhook](/documentation/agentic-payments/webhooks#failure_code-reference) carries — whenever the stored reason maps to a documented code; every failure item, including the `payments_failed` singleton summary, carries `detail.docs_url` pointing at that reference, so a UI can link straight from a finding to what to do about it. Cross-customer clusters group on the code (or on the reason text when no code applies), so two customers denied by different mandate rules still surface as one finding.

## Filters

Every filter is optional and only **narrows** the report; the response shape never changes.

| parameter        | effect                                                                                                                                                                                                                          |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `window_days`    | the reporting window, `1`–`90` (default `14`). Drives the KPIs, the series and the previous-window comparison                                                                                                                   |
| `customer_id`    | scope the **whole computation** to one customer (drill-down). The snapshot then also carries that customer's `balances`. An unknown or foreign customer is a `404`, exactly like the customer report; a malformed id is a `400` |
| `wallet_id`      | keep only items whose `evidence` references this wallet; a malformed id is a `400`                                                                                                                                              |
| `kind`           | comma-separated list — keep only items of these kinds (`kind=volume_anomaly,recipient_dormant`)                                                                                                                                 |
| `severity`       | comma-separated list of `info`, `warn`, `critical`; anything else is a `400`                                                                                                                                                    |
| `responsibility` | comma-separated list — keep only items with these labels                                                                                                                                                                        |

Multi-value filters are **OR within a field and AND across fields**: `severity=critical,warn&responsibility=payment_ops` keeps critical-or-warn items that belong to payment ops. `kind`, `severity`, `responsibility` and `wallet_id` drop only items — `snapshot`, `series`, `facets` and `customers[]` are the facts of the book and stay whole, so a filtered view still shows the right totals.

```bash theme={null}
# Compliance-flavoured findings only, over the last 30 days
curl "https://api.platform.dakota.xyz/insights?window_days=30&responsibility=compliance" \
  -H "X-API-Key: $DAKOTA_API_KEY"

# Everything about one customer, balances included
curl "https://api.platform.dakota.xyz/insights?customer_id=2vWxCustomer0000000000000000" \
  -H "X-API-Key: $DAKOTA_API_KEY"
```

## Scan cap

One request scans up to **100 customers**, in your customer list's order (name, then id). `snapshot.customers` makes that visible: `scanned < total` means the cap truncated the book and the report describes that prefix of it — truncation is never silent. `with_activity` counts customers with any non-cancelled scheduled payment within the 180-day read, or any finding; `with_critical` counts customers carrying at least one critical item. Use `?customer_id=` to compute a full report for any customer, scanned or not.

The payments read behind each customer reaches back **180 days** — the widest window plus the anomaly baseline behind it. A recipient silent for longer than that ages out of `recipient_dormant` rather than being reported forever.

## Degradation, not errors

A deployment without a balance index simply omits balance-derived parts (`total_usd`, `balances`, shortfall math); the rest of the report still computes. Missing data degrades the report — it never 500s.

## Customer or client report?

Render the **client report** for the operator's view: KPI tiles with deltas, charts from `series`, a findings feed sorted by severity, and a customers table that drills into `?customer_id=`. Render the **customer report** — or the drilled-down client report, which carries the same items for that customer — inside a single customer's own experience. The two always agree: the client report is built from the same per-customer computation.
