Copyright (c) 2026 MindMesh Academy. All rights reserved. This content is proprietary and may not be reproduced or distributed without permission.

2.1.3. workflow_call: Inputs and Secrets for Reusable Workflows

💡 First Principle: workflow_call converts a workflow into a function — it declares a typed parameter list (inputs), an explicit secrets contract (secrets), and a return signature (outputs) — and everything a caller wants it to know must pass through that interface, because nothing is inherited implicitly.

This is the mechanism behind organization-wide standard pipelines, and its interface is exam-dense. The called workflow declares what it accepts:

# .github/workflows/reusable-deploy.yml (the callee)
on:
  workflow_call:
    inputs:
      environment:
        type: string          # string | number | boolean only
        required: true
    secrets:
      deploy_token:
        required: true
    outputs:
      deployed_url:
        description: 'URL of the deployment'
        value: ${{ jobs.deploy.outputs.url }}

And the caller invokes it at the job level with uses: — not as a step:

jobs:
  call-deploy:
    uses: my-org/ci-workflows/.github/workflows/reusable-deploy.yml@v2
    with:
      environment: production
    secrets:
      deploy_token: ${{ secrets.PROD_DEPLOY_TOKEN }}
      # or, to forward everything the caller has:
      # secrets: inherit

Three details carry most of the exam weight. First, workflow_call inputs support only string, number, and boolean — there is no choice type here (that's workflow_dispatch only), a classic swapped-answer distractor. Second, secrets are not inherited by default: either map them explicitly by name, or use secrets: inherit — which passes all of the caller's secrets and should be used deliberately, since it widens what the callee can see. Third, nesting is limited: a chain of reusable workflows may go up to ten levels deep, and a caller's tree may reference at most 50 unique reusable workflows in total.

Outputs flow back up through the job: the callee's job sets an output, the callee's workflow_call.outputs maps it to a workflow-level name, and the caller reads it as needs.call-deploy.outputs.deployed_url.

⚠️ Exam Trap: A reusable workflow is called by a job (jobs.<id>.uses:), never by a step, and that job cannot also define steps: — it either calls the reusable workflow or runs its own steps, not both. Any answer showing uses: ./.github/workflows/x.yml inside a steps: list is wrong; that syntax is for actions.

Reflection Question: secrets: inherit is convenient and works instantly. Describe the blast radius you accept by using it with a reusable workflow maintained by another team.

See how it connects
Alvin Varughese
Written byAlvin Varughese
Founder18 professional certifications