> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pipefort.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bulk remediation campaigns

> Open auto-fix pull requests across many repositories at once and track every PR from one place.

Fixing findings one repo at a time doesn't scale past a handful of
repositories. A **bulk remediation campaign** applies Pipefort's
[auto-fixes](/webapp/finding-triage) across every repository you select in one
action: the campaign scans each repo's workflow files, opens (or reuses) a fix
pull request per remediable file, and records the outcome per repository in a
tracker you can export as CSV.

## Launching a campaign

On the **Remediation** page:

1. Name the campaign (e.g. *Pin actions to SHA — Q3*).
2. Select the repositories to include (or **Select all**).
3. Click **Launch campaign**.

The browser then drives the campaign the same way org-wide scanning works: one
short API request per repository, a few repos in flight at a time, so each
request stays well within serverless limits. You can watch the tracker fill in
live as repos complete.

<Note>
  **Admin only.** Launching a campaign — and remediating repos within one — is
  restricted to organization **admins**, since it opens pull requests across the
  org's repositories. Any member can view campaign trackers.
</Note>

## What gets fixed

Campaigns remediate **workflow-YAML findings that have an auto-fix** — the
same fixer registry behind the per-finding **Fix** button (pinning actions to
SHAs, adding least-privilege `permissions`, adding timeouts/concurrency,
removing debug logging, and so on). For each repository, the campaign:

* fetches and scans the repo's CI workflow files in memory (nothing is cloned),
* honors your [rule settings](/webapp/rule-settings) — disabled rules are
  never "fixed",
* opens one pull request per remediable workflow file — every fixable finding
  in that file is applied in a single commit, so a workflow with five issues
  gets one PR to review, not five — or reuses the existing Pipefort fix PR if
  one is already open,
* records the outcome.

Repo-settings findings (branch protection, secret scanning, …) are **not**
included — those are one-click direct fixes on the repository page, not pull
requests.

## Per-repo status

| Status        | Meaning                                                               |
| ------------- | --------------------------------------------------------------------- |
| **Pending**   | Not processed yet.                                                    |
| **Running**   | Remediation in flight.                                                |
| **PR opened** | At least one fix PR/MR was opened (or reused).                        |
| **Partial**   | Some files got fix PRs, but others failed — the row's error says why. |
| **No change** | Nothing auto-fixable — already remediated or no findings.             |
| **Failed**    | An error prevented remediation (see the row's error message).         |

The campaign itself is **running** until every repo reaches a terminal state,
then **completed**. The tracker shows the rollup (repos with PRs, no-change,
failed, and total PRs opened) plus each PR's link, and **Export CSV** downloads
the whole table — one row per repository with its status, PR count, fixes
applied, PR URLs, and any error.

### Retrying

Launching a campaign never opens the same pull request twice: each repository is
claimed before its fixes run, so a double-click, a page refresh mid-run, or a
second admin working in another tab won't duplicate the work.

Repositories that **failed** can be retried — relaunching remediation for them
picks the campaign back up and returns it to **running**. Repositories that
already finished are left alone.

## GitLab

Campaigns are provider-neutral: for GitLab-hosted repositories the campaign
opens **merge requests** through the same flow.

## API

Campaigns are driven by three endpoints — see the
[API reference](/webapp/api-reference#post-apicampaigns):

* `POST /api/campaigns` — create a campaign over selected repos (admin only).
* `POST /api/campaigns/{id}/repos/{repo_id}/remediate` — remediate one repo
  (admin only; the SPA loops this with bounded concurrency).
* `GET /api/campaigns/{id}` — the tracker: campaign, per-repo statuses, and
  the rollup.

Campaign rows are also readable directly from the database
(`remediation_campaigns`, `campaign_repos`), scoped to your organizations.
