# GitHub Actions

> Workflows that comment a cost summary on a pull request, run a scheduled cost check with the Python or TypeScript SDK, and upload a daily report as a build artifact.

Source: https://docs.levelfour.ai/guides/github-actions


Three workflows, each a whole file you copy into `.github/workflows/`. One comments a cost summary on every pull request, one runs a scheduled check with the Python or TypeScript SDK, and one uploads a daily report as a build artifact.

> **Note**
>
> Nothing on this page uses the `l4` CLI. Terraform and CloudFormation cost estimates and diff checks inside a workflow live on [CI/CD Integration](/guides/ci-cd).

## Prerequisites

* A LevelFour API key. [Authentication](/authentication) covers where keys are created, the two key formats, and rotation.
* The key stored as a GitHub Actions secret named `LEVELFOUR_API_KEY`.

> **Tip**
>
> Every request on this page reads, so a `read` key covers all three workflows. See [Key scopes](/authentication#key-scopes).

## Comment a cost summary on a pull request

The job needs `pull-requests: write`, which is the only permission it asks for.

````yaml title=".github/workflows/cost-report.yml"
name: Cost Report
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  cost-report:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - name: Get savings summary
        id: summary
        env:
          LEVELFOUR_API_KEY: ${{ secrets.LEVELFOUR_API_KEY }}
        run: |
          RESPONSE=$(curl -s \
            -H "Authorization: Bearer $LEVELFOUR_API_KEY" \
            https://api.levelfour.ai/api/v1/recommendations/savings-by-provider)
          echo "body<<EOF" >> $GITHUB_OUTPUT
          echo "## Cloud Cost Summary" >> $GITHUB_OUTPUT
          echo "" >> $GITHUB_OUTPUT
          echo '```json' >> $GITHUB_OUTPUT
          echo "$RESPONSE" | python3 -m json.tool >> $GITHUB_OUTPUT
          echo '```' >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Comment on PR
        uses: actions/github-script@v7
        with:
          script: |
            const body = process.env.BODY;
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: body,
            });
        env:
          BODY: ${{ steps.summary.outputs.body }}
````

> **Success**
>
> A working run leaves one comment on the pull request, headed **Cloud Cost Summary**, with the response JSON fenced underneath it.

## Scheduled cost check

The same job runs in Python or in TypeScript, on a weekday schedule, and prints what it finds to the job log. Each SDK reads `LEVELFOUR_API_KEY` from the environment, so the client is constructed with no arguments. [Python SDK](/sdks/python) and [TypeScript SDK](/sdks/typescript) cover the rest of each client.

**Python**

```yaml title=".github/workflows/cost-check.yml"
name: Cost Check (Python)
on:
  schedule:
    - cron: '0 9 * * 1-5'

jobs:
  report:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - run: pip install levelfour

      - name: Run cost check
        env:
          LEVELFOUR_API_KEY: ${{ secrets.LEVELFOUR_API_KEY }}
        run: |
          python3 -c "
          from levelfour import LevelFour

          client = LevelFour()
          summary = client.recommendations.get_savings_by_provider()
          print(summary)

          overview = client.recommendations.get_overview()
          print(overview)

          costs = client.costs.get_summary()
          print(costs)
          "
```

**TypeScript**

```yaml title=".github/workflows/cost-check.yml"
name: Cost Check (TypeScript)
on:
  schedule:
    - cron: '0 9 * * 1-5'

jobs:
  report:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: '22'

      - run: npm install levelfour

      - name: Run cost check
        env:
          LEVELFOUR_API_KEY: ${{ secrets.LEVELFOUR_API_KEY }}
        run: |
          npx tsx -e "
          import { LevelFourClient } from 'levelfour';
          const client = new LevelFourClient();
          const summary = await client.recommendations.getSavingsByProvider();
          console.log(JSON.stringify(summary, null, 2));

          const costs = await client.costs.getSummary();
          console.log(JSON.stringify(costs, null, 2));
          "
```

## Daily report as a build artifact

The job writes `costs.json` and `recommendations.json`, then uploads both under the artifact name `cost-report-<run id>`.

```yaml title=".github/workflows/daily-cost-report.yml"
name: Daily Cost Report
on:
  schedule:
    - cron: '0 9 * * 1-5'

jobs:
  report:
    runs-on: ubuntu-latest
    steps:
      - name: Fetch cost summary
        env:
          LEVELFOUR_API_KEY: ${{ secrets.LEVELFOUR_API_KEY }}
        run: |
          curl -s \
            -H "Authorization: Bearer $LEVELFOUR_API_KEY" \
            https://api.levelfour.ai/api/v1/costs/summary \
            | python3 -m json.tool > costs.json

      - name: Fetch recommendations
        env:
          LEVELFOUR_API_KEY: ${{ secrets.LEVELFOUR_API_KEY }}
        run: |
          curl -s \
            -H "Authorization: Bearer $LEVELFOUR_API_KEY" \
            "https://api.levelfour.ai/api/v1/recommendations?page=1&page_size=100" \
            | python3 -m json.tool > recommendations.json

      - name: Upload report
        uses: actions/upload-artifact@v4
        with:
          name: cost-report-${{ github.run_id }}
          path: |
            costs.json
            recommendations.json
```

> **Warning**
>
> The recommendations request fetches page one and stops there. An organization with more recommendations than that page holds gets a truncated file and no warning, because the workflow never reads the pagination metadata the response carries. [Pagination](/pagination) has the fields to check and how to walk the rest.

## Next

* [CI/CD Integration](/guides/ci-cd) is the `l4` CLI equivalent: Terraform and CloudFormation cost estimates and diffs that gate a pull request
* [Authentication](/authentication) covers key creation, formats and scopes
* [Python SDK](/sdks/python) and [TypeScript SDK](/sdks/typescript) are the clients behind the scheduled check
* [Scheduled Execution](/guides/scheduled-execution) monitors savings execution on a recurring schedule
