← All recipes

GitHub Actions recipe · Works on every plan

Post a weekly support digest as a GitHub Issue

Every Monday, a scheduled workflow counts the week's new and closed tickets and files the summary as a fresh Issue — no dashboard to check, no manual report to write.

What this workflow does

It runs on a cron schedule (Monday 9am UTC by default — edit the cron line to change it), reads every issue updated in the last 7 days, and breaks the new ones down by category:* and sentiment:* labels before filing the summary as a new Issue labeled digest.

What it does not do

The category and sentiment breakdown only has data if AI triage is enabled (Pro plan) — on Free, those two sections read "No data" but the new/closed counts still work. This is also not Scitor's own built-in /generate-report command, which produces a richer, chart-based monthly report inside the product itself; this recipe is a simpler, fully editable alternative you own.

The workflow file

Save as .github/workflows/weekly-digest.yml.

# Weekly support digest, posted as a GitHub Issue
#
# Every Monday at 9am UTC, summarizes the past week's tickets — new vs.
# closed, and a breakdown by category and sentiment label — and files it
# as a new GitHub Issue.
#
# What this does:
#   - Runs on a schedule, not a ticket event — no label trigger, no
#     duplicate-firing risk from re-labeling.
#   - Reads every issue updated in the last 7 days via the REST API,
#     paginating through all results rather than stopping at the first
#     page (a busy repo can have more than 100 matching issues).
#   - Excludes the digest's own past issues (the `digest` label) from every
#     count, so last week's digest post doesn't get counted as a new or
#     closed ticket in this week's numbers.
#   - Counts "closed" using each issue's actual `closed_at` timestamp, not
#     just "state is currently closed" — an issue closed a month ago that
#     merely got a comment this week would otherwise be miscounted as
#     closed this week.
#   - Counts labels prefixed `category:` and `sentiment:` — these only
#     exist if AI triage (Pro plan) is enabled; on the Free plan the
#     digest still posts, but those two sections read "No data".
#   - Creates the `digest` label on first run if it doesn't already exist
#     in the repo, so this doesn't fail on a fresh install.
#
# What this does NOT do:
#   - It is not Scitor's built-in `/generate-report` command (that's a
#     richer, chart-based monthly report available in the product itself —
#     see support.scitor.io/features). This is a simpler, fully custom
#     digest you own and can edit freely.
#   - It does not post to Slack by itself — combine it with the Slack
#     recipe/integration if you want the digest to also land in a channel.
#
# No secrets required, but it does write: it reads issues in the repo it
# runs in and creates one new "digest" Issue each run, using the
# workflow's default GITHUB_TOKEN with `issues: write` permission.
#
# Source: adapted from Scitor's own documented example at
# https://support.scitor.io/guides/github-actions#weekly-support-digest

name: Weekly support digest
on:
  schedule:
    - cron: '0 9 * * 1' # Every Monday at 9am UTC

jobs:
  digest:
    runs-on: ubuntu-latest
    permissions:
      issues: write
    steps:
      - uses: actions/github-script@v7
        with:
          script: |
            const oneWeekAgoDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
            const oneWeekAgo = oneWeekAgoDate.toISOString();

            // Paginate through every matching issue — a busy repo can easily
            // have more than one page (100) of issues updated in a week.
            const allIssues = await github.paginate(github.rest.issues.listForRepo, {
              owner: context.repo.owner,
              repo: context.repo.repo,
              since: oneWeekAgo,
              state: 'all',
              per_page: 100,
            });

            // Exclude the digest's own past posts from every count, and skip PRs.
            const issues = allIssues.filter((i) => !i.pull_request && !i.labels.some((l) => (typeof l === 'string' ? l : l.name) === 'digest'));

            const opened = issues.filter((i) => new Date(i.created_at) > oneWeekAgoDate);
            const closed = issues.filter((i) => i.closed_at && new Date(i.closed_at) > oneWeekAgoDate);

            const byCategory = {};
            const bySentiment = {};
            for (const issue of opened) {
              for (const label of issue.labels) {
                const name = typeof label === 'string' ? label : label.name;
                if (name.startsWith('category:')) byCategory[name] = (byCategory[name] || 0) + 1;
                if (name.startsWith('sentiment:')) bySentiment[name] = (bySentiment[name] || 0) + 1;
              }
            }

            const categoryLines = Object.entries(byCategory)
              .sort((a, b) => b[1] - a[1])
              .map(([k, v]) => `- ${k}: ${v}`)
              .join('\n') || '- No data (AI triage not enabled, or no matching tickets)';

            const sentimentLines = Object.entries(bySentiment)
              .sort((a, b) => b[1] - a[1])
              .map(([k, v]) => `- ${k}: ${v}`)
              .join('\n') || '- No data (AI triage not enabled, or no matching tickets)';

            const body = [
              '# Weekly Support Digest',
              `${new Date(oneWeekAgo).toLocaleDateString()} — ${new Date().toLocaleDateString()}`,
              '',
              '## Overview',
              `- New tickets: ${opened.length}`,
              `- Closed tickets: ${closed.length}`,
              '',
              '## By category',
              categoryLines,
              '',
              '## By sentiment',
              sentimentLines,
            ].join('\n');

            // Create the label explicitly first — don't assume it already
            // exists in the repo (e.g. on a fresh install).
            try {
              await github.rest.issues.createLabel({
                owner: context.repo.owner,
                repo: context.repo.repo,
                name: 'digest',
                color: '5319e7',
                description: 'Weekly support digest posts',
              });
            } catch (err) {
              if (err.status !== 422) throw err; // 422 = label already exists
            }

            await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: `Weekly Support Digest — ${new Date().toLocaleDateString()}`,
              body,
              labels: ['digest'],
            });

No secrets required, but it does write: it creates one new "digest" Issue each run.

Know your week's support load, automatically

Takes under 5 minutes. Free tier available. No credit card required.