SemiLayerDocs

Analyze → chart image

/semilayer analyze <name> turns any analyze you've declared on a lens into a posted chart image. The platform renders a self-contained SVG, converts it to PNG server-side, uploads it to a static host, and posts the URL as a Slack image block or Discord embed image.

/semilayer analyze byCategory --lens=products --kind=bar

A single line in chat → a real chart in the channel. No Console detour, no copy-paste of a query.

What you need first

1
A lens with at least one declared analyze

The analyze block lives in the lens config under analyses.<name>. Same shape the Console + Beam client consume — chat reads from the same registry.

lenses:
  products:
    fields:
      id: { type: string, primaryKey: true }
      category: { type: string }
      price_cents: { type: integer }
    analyses:
      byCategory:
        kind: metric
        dimensions:
          - field: category
        measures:
          items: { agg: count }
        sort:
          - measure: items
            dir: desc
    grants:
      analyze.byCategory: public
2
A grant on the analyze

The chat user needs analyze.<name> access. The grants.analyze.<name> field can be public, member, admin, or a list of role names. The chat layer reuses the same RBAC the API uses — no separate "chat permission."

3
Bot installed + you're linked + scope resolves

See Install and Scoping. Without these the command returns an ephemeral pointer instead of a chart.

Chart kinds

--kindBest for
barDefault. Categorical comparison. Up to 20 bars; overflow shows "showing N of M".
lineTime-series with multiple measures. X-axis ordered by your sort.
areaSame as line but filled. Cumulative metrics.
donutShare of whole. Up to 10 slices, rest collapses into "+N more".
pieSame as donut but filled center.
treemapHierarchical share. Slice-and-dice layout, up to 30 leaves.

The renderer picks sensible axis labels, color palette (the SemiLayer brand gradient), and number formatting (1,234 for counts, $1.2k for currency-shaped measures).

Public vs ephemeral

Analyze defaults to ephemeral in shared channels (only the typer sees it) so a casual /semilayer analyze doesn't drown the channel.

/semilayer analyze byCategory --lens=products              # ephemeral
/semilayer analyze byCategory --lens=products --public     # in_channel

When fired by a /semilayer watch … (see below), the result is always public to the watch's target channel — that's the point.

What gets posted

Slack — header (<analyze name> · <lens>) → image block (the chart) → context block with the bucket count and a "Open in Console" deep link.

Discord — embed with title, image attachment, footer with the bucket count, and an Open in Console link in the body.

Click the Open in Console link to drill into the buckets — the same analyze, but with the drill-down UI, sortable table, and per-bucket export.

Watching an analyze

The natural pair to /semilayer analyze is /semilayer watch analyze — fire that chart automatically every morning instead of typing the command.

/semilayer watch analyze --name=byCategory --every=daily --at=09:00 --lens=products

The fired post carries a footer (🔁 Watch <id> · daily at 09:00 · created by you) so the channel can spot scheduled posts and stop / pause them with /semilayer watch stop <id>. See Watch & Remind.

ℹ️

Watched analyzes currently render as a markdown table instead of a chart image — the worker that fires watches does not yet ship the chart-rendering pipeline. You get the bucket data, just rendered differently. Tracked on the chatops roadmap.

Failure modes

"Analyze <name> is not declared on lens <lens>." — The analyze name doesn't exist on the lens config. Check analyses.<name> and confirm you pushed the latest config (semilayer push).

"Need more context: lens, project, env." — Scope didn't resolve. Pass the missing flags explicitly, set them with /semilayer config set, or pin them with /semilayer here set.

"You don't have access to that analyze." — The grants.analyze.<name> doesn't include your role. Ask an org admin to update the lens config.

"Analyze took too long — posted as table fallback." — The chart renderer has a 10s budget. If the analyze runs longer (large candidate set, complex aggregation), the markdown-table fallback fires so you still get an answer.

API equivalent

Everything /semilayer analyze does is a thin wrapper over the platform's analyze API:

POST /v1/analyze/<lens>/<name>
{
  "where": { ... }   // optional, mirrors the --where flag
}

The response is the same AnalyzeResult shape the Beam client returns. The chat layer renders it; the API hands it back as JSON.