DashboardSign inStart your trial

In this guide · OperateClose guide

Grafana

Chart Rills pipeline, run, and approval metrics in Grafana with the Infinity data source and a scoped Rills API key.

Put Rills metrics on the same screen as the rest of your GTM reporting: leads waiting for outreach, outbound runs by status, approval queue depth, and decision volume. Grafana reads them through the query API using the Infinity data source plugin, so no Rills database access is involved.

This guide uses a scoped Rills API key stored in the data source’s secure settings. That’s the setup Rills currently supports for Grafana. Other sign-in options may be added later; this page will be updated if they are.

#Before you start

  • A Grafana instance (self-hosted or Grafana Cloud) where you can install plugins and add data sources. Plan features and free-tier limits are set by Grafana and can change.
  • The Infinity data source plugin installed.
  • A Rills workspace admin or owner to create the key.

#1. Create a key for the dashboard

In Settings → API & MCP access, create a key named after the dashboard and grant only what its panels read:

PanelsScopes
Workflow runs and durationsworkflows:read
Approval queue and decisionsapprovals:read
Lead or pipeline counts from a tabletables:read, payloads:read
Form submission volumeforms:read, payloads:read

A run-health and approval dashboard doesn’t need payloads:read. Copy the key when it’s shown; you’ll paste it into Grafana once and nowhere else. See Authentication.

#2. Test the key with cURL

Confirm the key and scopes before touching Grafana:

curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer $RILLS_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"dataset":"approvals","filters":[{"field":"status","op":"eq","value":"pending"}],"measures":[{"alias":"pending","fn":"count"}]}' \
  "https://rills.ai/api/v1/query"

A working key returns {"data":[{"pending":...}],"meta":...}. A 401 means the key is wrong or revoked; a 403 names the missing scope.

#3. Add the data source

In Grafana, add a new Infinity data source:

  1. Under Authentication, choose Bearer Token and paste the key. Grafana stores it encrypted and never sends it to viewers’ browsers.
  2. Under Allowed hosts, add https://rills.ai so the key is only ever sent to Rills.
  3. Set the base URL to https://rills.ai/api/v1.
  4. Save and test.

Configure the key here, never inside a panel query, so it stays out of dashboard JSON and exports.

If you provision Grafana from files, keep the key in an environment variable:

apiVersion: 1
datasources:
  - name: Rills API
    uid: rills-api
    type: yesoreyeram-infinity-datasource
    jsonData:
      auth_method: bearerToken
      url: https://rills.ai/api/v1
      allowedHosts:
        - https://rills.ai
    secureJsonData:
      bearerToken: ${RILLS_API_KEY}

#4. Build a panel

Every Rills panel is a POST to /query. In the panel’s query editor:

SettingValue
TypeJSON
ParserBackend
SourceURL
FormatTable (stat and table panels) or Time Series
MethodPOST
URLhttps://rills.ai/api/v1/query
Body typeRaw, content type JSON
Root selectordata

Use the Backend parser. It keeps requests and the key on the Grafana server and is required for alerting.

To follow the dashboard’s time picker, use Grafana’s time macros in timeRange:

{
  "dataset": "workflowExecutions",
  "resourceId": "00000000-0000-4000-8000-000000000000",
  "timeRange": { "from": "${__from:date:iso}", "to": "${__to:date:iso}" },
  "timeBucket": { "field": "createdAt", "interval": "day" },
  "groupBy": ["status"],
  "measures": [{ "alias": "runs", "fn": "count" }],
  "orderBy": [{ "field": "bucket", "direction": "asc" }]
}

Then add columns: bucket as Timestamp, status as String, and runs as Number. Results come back in long format (one row per day and status). To draw one line per status, add Grafana’s Partition by values transformation on status.

Keep the dashboard range within 366 days and choose a bucket interval that fits it. A year of minute buckets is far more rows than the 1,000-row limit.

#Starter panels

The bodies for these panels are in Query workspace data. Replace each fixed timeRange with the macros above.

PanelVisualizationQuery
Leads not contactedStattableRows, eq filter on your contact-status field, count
Daily outbound runsTime seriesworkflowExecutions for one workflow, day bucket, grouped by status
Run durationTime seriesworkflowExecutions, day bucket, avg and p95 of durationMs
Pending approvalsBar chart or tableapprovals, status eq pending, grouped by priority
Approval outcomesTime seriesapprovals, day bucket on decidedAt, grouped by decisionKind
Form submissionsTime seriesformSubmissions, day bucket, count

For Pending approvals, the range filters on when requests were created. Give the panel its own longer window in Query options → Relative time (for example 30d) so older pending requests still count.

#5. Set the refresh interval

You choose the refresh interval, within the API’s rate limits; there is no unlimited refresh. Each panel costs 10 request units per refresh against a budget of 1,200 per minute per key.

  • Start at 30 seconds.
  • For multi-panel dashboards, stay at 10 seconds or slower.
  • Before going faster, multiply panels by refreshes per minute by 10 and compare it with the RateLimit-Remaining header.
  • Use a separate key per dashboard so one busy screen can’t throttle another.

#Troubleshooting

You seeCauseFix
No dataThe query matched nothing. Grouped and bucketed queries return an empty array.Widen the time range or check filter values.
401Key wrong, deleted, or pasted with extra spaces.Create a new key and update the data source.
403Key lacks a scope the dataset needs.Edit the key’s scopes in Rills.
400 QUERY_TOO_EXPENSIVEMissing time range, range over 366 days, or an unsupported field or operator.See query rules.
429Refresh too fast for the number of panels.Increase the refresh interval or split dashboards across keys.
503 QUERY_TIMEOUTThe query ran past 5 seconds.Shorten the range or remove a grouping.