Workflow Inputs and Outputs

Passing Inputs, Secrets and Outputs Between Workflows

The caller uses the reusable workflow in place of runs-on and steps, passing inputs under with and secrets under secrets, and reads outputs through needs:

The jobs of .github/workflows/catalog.yml, the callerYAML
jobs:
  check:
    uses: ./.github/workflows/catalog-check.yml
    with:
      genre: ${{ inputs.genre }}
    secrets:
      shelf-key: ${{ secrets.SHELF_API_KEY }}
  report:
    needs: check
    runs-on: ubuntu-24.04
    steps:
      - run: echo "The reusable workflow counted ${{ needs.check.outputs.count }} books"
Running the caller, then reading its jobs and the called job's logShell
gh workflow run catalog.yml -f genre=Fiction
gh run view 36136368677 --json jobs --jq '.jobs[] | "\(.name): \(.conclusion)"'
gh api repos/{owner}/{repo}/actions/jobs/108075307016/logs --allow-escape-sequences \
  | cut -d' ' -f2- | grep -E '^(##\[group\]Run |##\[notice\]|Node v|added )'
Output
https://github.com/binarybehemoth/booknest/actions/runs/36136368677
check / count: success
report: success
##[group]Run actions/checkout@v7
##[group]Run ./.github/actions/setup-booknest
...
##[group]Run ./.github/actions/count-books
##[notice]1 Fiction book(s) in the seed data
...
Node v24.21.0, key ***, 1 books

The called job appears as check / count, caller job then callee job. The value travelled through four hops: the JavaScript action's output, the job's outputs, the workflow's outputs under workflow_call, and the caller's needs.check.outputs.count. The secret arrived masked. Secrets must be passed explicitly (or all at once with secrets: inherit, simpler but broader), and the caller's workflow-level env does not cross into the called workflow; vars does.