Skip to content

Reporting

The reporting views in the Sill dashboard summarise the activity recorded in the per-site signed audit envelope — agent traffic by identity, lifecycle stage counts, top agents, top skills, blocked attempts, and (under Transactional) revenue and settlement outcomes. All figures are tenant-scoped reads computed server-side against the active site’s audit records.

A central design constraint of the surface is that the time window is a closed enum: 24h, 7d, 30d, 90d, 1y. The dashboard never sends a free-form from/to, and the API rejects anything outside that set. The reason is honest: a widened window is both a SQL planner surprise (a single request can scan tens of thousands of rows) and an indirect cost surface for Transactional connector calls. The closed enum keeps every bucket count bounded and predictable.

TokenWindow lengthBucket granularityBucket count
24hlast 24 hourshour24
7dlast 7 daysday7
30dlast 30 daysday30
90dlast 90 daysday90
1ylast 365 daysweek~52

The server resolves the granularity from the range token; the dashboard does not choose it. The traffic series is zero-filled server-side via generate_series, so a bucket with no rows surfaces as human: 0, agent: 0 rather than a gap. Bucket boundaries are aligned to UTC, and all bucket_start timestamps are ISO-8601 UTC.

The dashboard renders the reporting payloads as several discrete widgets, all driven from the same active site and range.

Three tiles at the top of the dashboard:

  • Revenue from agents — sum of settled minor units in the window, in the modal currency of those settlements. Discovery-only sites show $0 with currency null.
  • Agent calls — count of audit records where an agent identity was matched in the window.
  • Failed / disputed — count of transactional audit records whose settlement outcome is failed or disputed. This tile is flagged “lower is better”: a decrease renders lime (good), an increase renders coral (concerning). On a Discovery-only site this is typically 0.

Each tile carries a delta versus the prior equal-length window. When that prior window has zero denominator, the delta renders as rather than +Inf% — a deliberate honesty invariant.

Five independent per-stage counts for the window — Discovered, Browsed, Paid, Fulfilled, Returned. These are independent counts, not a funnel: a flow where Browsed < Paid is normal because each stage is matched on its own predicate (agent class, skill id, mandate intent, settlement outcome). The Carted stage is intentionally hidden until its predicate is wired to a real agent flow.

A time-series chart of human-versus-agent visit counts across the window. The human series counts visits where visitor_class = 'human_likely'; the agent series sums matched_agent plus unknown_agent. The chart renders as inline SVG (no charting framework). Hover surfaces the exact counts for a bucket.

The top five agents by call count in the window, each with a friendly name resolved from the seeded identity registry, a call count, and (under Transactional) the sum of settled minor units attributed to that agent. Unknown or human-likely rows surface keyed on their visitor class.

The top eight skills by invocation count in the window. The skill id is the unified name — the connector skill or the MCP tool_name it dispatched as. Rows with no skill id collapse into the Discovered stage on the stage-activity strip rather than being shown here.

The top eight rejection reasons across reject-class audit records in the window — decision IN ('rejected', 'verification_rejected', 'rejected_post_verify', 'escalated_rejected') — sourced from a sanitised, closed-enum classification field. A reject row with no reason is bucketed as unspecified so the widget honestly reports the count of blocked attempts even when the writer did not record a reason.

The active site’s visibility score (0–100), drawn from the same per-site report used by the Visibility view. The widget reads the score from the same overview payload as the KPI tiles, and links to the full Visibility view for the per-field breakdown.

The default dashboard view on a Transactional site, showing the KPI tiles, the stage-activity strip, the human-vs-agent traffic chart, and the agent visibility ring.

The range selector. All widgets re-fetch when the range changes.

The dashboard issues three reads per range selection — one for the overview tiles, one for the traffic series, one for the breakdowns composite — and renders the typed responses into the widgets above.

flowchart LR
  A[Dashboard widgets] -->|GET overview / traffic / breakdowns ?range=...| B[Sill API]
  B -->|tenant-scoped read under RLS| C[(audit_record)]
  C -->|aggregate over projected columns| B
  B -->|typed JSON, snake_case, ISO-8601 UTC| A

The breakdowns endpoint returns the lifecycle counts, top agents, top skills, and blocked attempts in one round trip so the four widgets render off a single fetch. The closed range enum is validated server-side; an unknown token is rejected. Cross-tenant or unknown site_id returns 404, which the dashboard surfaces as an empty state — never as someone else’s data.

All three endpoints are read-only GETs under the active site. They are documented in the API reference; the shapes below are illustrative.

Terminal window
curl -H "Cookie: sill_session=..." \
"https://api.sill.so/v1/sites/01EXAMPLE00000000000000000/reporting/overview?range=7d"
{
"range": "7d",
"window": {
"from": "2026-06-15T00:00:00.000Z",
"to": "2026-06-22T00:00:00.000Z"
},
"agent_calls": { "count": 4892, "delta_pct": 38.4 },
"revenue_minor": { "total_minor": 824700, "currency": "usd", "delta_pct": 62.1 },
"disputes_pending":{ "count": 3, "delta_pct": null },
"visibility": { "score": 87, "delta": null }
}
Terminal window
curl -H "Cookie: sill_session=..." \
"https://api.sill.so/v1/sites/01EXAMPLE00000000000000000/reporting/traffic?range=24h"
{
"range": "24h",
"granularity": "hour",
"window": {
"from": "2026-06-21T00:00:00.000Z",
"to": "2026-06-22T00:00:00.000Z"
},
"points": [
{ "bucket_start": "2026-06-21T00:00:00.000Z", "human": 12, "agent": 4 },
{ "bucket_start": "2026-06-21T01:00:00.000Z", "human": 8, "agent": 6 }
]
}
Terminal window
curl -H "Cookie: sill_session=..." \
"https://api.sill.so/v1/sites/01EXAMPLE00000000000000000/reporting/breakdowns?range=30d"
{
"range": "30d",
"window": {
"from": "2026-05-23T00:00:00.000Z",
"to": "2026-06-22T00:00:00.000Z"
},
"lifecycle": {
"discovered": 21525, "browsed": 9447, "carted": 0,
"paid": 2143, "fulfilled": 1813, "returned": 53
},
"by_agent": [
{ "agent_id": "agent_anthropic_claude", "visitor_class": "matched_agent", "calls": 8412, "revenue_minor": 412100, "currency": "usd" }
],
"by_skill": [
{ "skill_id": "browse_catalog", "calls": 6204 }
],
"by_block_reason": [
{ "reason": "rate_limit_exceeded", "count": 14 }
]
}

Field names are snake_case and timestamps are ISO-8601 UTC throughout. Monetary fields are integer minor units; the modal currency for the window appears as a separate field. delta_pct: null means the prior window had a zero denominator, not zero change.

A fresh site with no agent traffic yet renders honest empty states — the KPI tiles show 0 with deltas, the stage activity strip and the traffic chart show empty cards with explanatory text, and the visibility ring suggests publishing the agent card. The dashboard never silently substitutes preview numbers for real ones.

A SAMPLE toggle in the dashboard header replaces every widget with stable, clearly-labelled synthetic data so you can see what the page will look like before real agents arrive. While the toggle is on, every tile and chart carries a SAMPLE chip; toggling it off restores the live view.

A fresh site renders empty-state cards rather than substituting preview numbers for real ones.

  • No arbitrary date ranges. The window is a closed enum; the API rejects free-form from/to. If you need a longer window, the audit bundle export is the route for archival or evidence work — it carries the signed records themselves, not a summary.
  • No PII enrichment. Top-agent rows are keyed on the agent identity in the seeded registry or on visitor class; no end-user identifiers are surfaced.
  • No latency, throughput, or SLA tiles. Performance metrics are not asserted as a measured product claim today.
  • No cross-site rollups. Each report is scoped to a single site under the dashboard’s active tenant. Multi-site accounts switch sites via the site picker.

Why is the range a closed enum and not a date picker? Predictable bucket counts. A widened window is a real attack surface (a single request scanning the entire audit_record table) and a vendor-cost surface for any Transactional connector hit. The closed enum bounds both.

Why does my delta render as ? The prior equal-length window had a zero denominator. The tile shows rather than +Inf% so a first week of traffic does not read as “infinite growth.”

Why does “Failed / disputed” go lime when it goes down? Lower is better on that tile. The chip colour reflects the desirable direction, while the arrow glyph still tracks the actual direction so you can tell at a glance whether the number rose or fell.

Is the data signed? The reporting payload itself is a server-computed summary and is not signed — it is a projection of already-projected, mutable columns on audit_record, served under tenant isolation. The underlying audit records are individually signed and Merkle-chained; for cryptographically verifiable evidence see the audit log and export and Verify a signature.

Where do the visibility numbers come from? The visibility ring reads the same per-site report rendered in full on the Visibility view, which scores the presence of agent-readable fields against a deterministic rubric. The dashboard never fabricates a missing-field count — drill into Visibility for the per-field breakdown.

  • Audit log and export — the underlying signed audit records and the HTML bundle.
  • Sites and onboarding — adding a site and the Visibility score.
  • Guardrails — the policy surface whose decisions feed the blocked-attempts widget.
  • Agent card — the seeded identity registry that resolves top-agent rows to friendly names.
  • Transactional overview — the Phase-2 surface that populates revenue, fulfilled, and disputed counts.
  • Verify a signature — verifying signed agent cards and ARD manifests against the public JWKS.
  • API endpoints — the underlying reporting routes.

External references: