> For the complete documentation index, see [llms.txt](https://docs.nullify.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nullify.ai/connectors/buildkite/configuration.md).

# Configuration

Connecting Buildkite takes about five minutes and three things: a Buildkite API token, your organization slug, and a one-time GitHub permission acknowledgement. This guide walks through all three — plus an optional step for organizations that restrict who can trigger builds.

## Step 1: Create a Buildkite API token

We recommend minting the token from a **dedicated service-account user** rather than a personal account — see the [callout in the overview](/connectors/buildkite.md) for why. Log in to Buildkite **as that user**, then:

1. Go to **Personal Settings → API Access Tokens** (`https://buildkite.com/user/api-access-tokens`).
2. Click **New API Access Token**.
3. Give it a recognizable name, e.g. `nullify-ci-feedback`.
4. Scope it to the **single organization** Nullify should see.
5. Enable these three required scopes:

   * `read_organizations`
   * `read_builds`
   * `read_build_logs`

   `write_builds` is optional. It is only needed if Nullify gate annotations are enabled for your organization. Gate annotations are off by default.
6. Click **Create Token** and copy the value. Buildkite API tokens are in the form `bkua_…`.

{% hint style="warning" %}
The token is shown **only once**. Copy it before you leave the page — if you lose it, you'll need to create a new one.
{% endhint %}

{% hint style="info" %}
**Required access.** Nullify uses `read_organizations` to verify organization access, `read_builds` for build status, and `read_build_logs` for autofix feedback. Setup verifies the required scopes before saving. `write_builds` is only needed if Nullify gate annotations are enabled for your organization. Restrict the token to the intended organization; agent, pipeline-management, and token-management scopes are not required.
{% endhint %}

## Step 2: Connect Buildkite in Nullify

1. In the current dashboard, open **Settings → Connectors** (legacy: **Configure → Connectors**).
2. Find **Buildkite** and open its connection flow.
3. Enter the **API token**, then use **Find organizations** and select the organization it can access. The stored organization slug identifies `buildkite.com/<org-slug>`; older forms let you enter it directly.
4. Click **Save changes**.

The Buildkite card now shows **Active** with your organization name, and unlocks **Autofix CI Feedback**.

{% hint style="info" %}
**"Active" means the token was verified and saved.** Saving performs a live Buildkite API check before Nullify stores the token and organization slug. Run the end-to-end check in [Step 6](#step-6-verify) to confirm that GitHub status delivery, log retrieval, and autofix iteration are also wired up.
{% endhint %}

## Step 3: Make sure Buildkite reports to GitHub

Nullify learns about your builds through the commit status Buildkite posts to GitHub. If that status isn't being published, Nullify has nothing to react to.

1. In Buildkite, go to **Organization Settings → Repository Providers → GitHub**.
2. Confirm the **Buildkite GitHub App** is installed (not the legacy OAuth integration).
3. In each relevant **pipeline's settings**, confirm **Publish commit statuses** (under GitHub settings) is enabled.

You can sanity-check this independently of Nullify: open any recent pull request that triggered a build and confirm a `buildkite/…` status check appears on it.

## Step 4: Acknowledge the GitHub App permission <a href="#github-app-permissions" id="github-app-permissions"></a>

To receive those commit-status events, Nullify's GitHub App needs two things:

* **Repository permission:** *Commit statuses* → **Read-only**.
* **Event subscription:** the **Status** event.

These are already configured on the Nullify GitHub App. The only action on your side is a one-time acknowledgement: GitHub asks each organization owner to **accept the updated permissions**, shown as an email and a banner on your organization's installed-apps page.

{% hint style="warning" %}
Until your organization owner accepts the updated permission, GitHub won't send build-status events to Nullify — so the autofix loop won't see your Buildkite results, even though the connector shows **Active**.
{% endhint %}

## Step 5: Let Nullify's commits trigger builds <a href="#build-creator-access" id="build-creator-access"></a>

**Skip this step unless your Buildkite organization restricts who can trigger builds.** Some orgs gate builds by the creator's team — for example an agent `environment` hook or a pipeline conditional that checks [`BUILDKITE_BUILD_CREATOR_TEAMS`](https://buildkite.com/docs/pipelines/configure/environment-variables) (or `build.creator.teams`) and fails any build whose creator isn't on an allowed team. If your org runs every push regardless of who created it, jump to [Step 6](#step-6-verify).

When Nullify opens an autofix pull request, the commit is authored by Nullify's GitHub App, using the email `<id>+nullify[bot]@users.noreply.github.com`. Buildkite maps that commit author to a Buildkite user by email and fills `BUILDKITE_BUILD_CREATOR_TEAMS` from that user's teams. So in a gated org, Nullify's Buildkite user must exist in your org and sit on an allowed team — otherwise the build is blocked before it starts, no commit status is posted, and the autofix loop never sees your CI (even though the connector shows **Active**).

1. **Invite the Nullify user.** Invite `integrations-bot@nullify.ai` to your Buildkite organization. It is a Buildkite-managed service account, like Dependabot or Renovate: it auto-accepts the invite, skips billing, and cannot log in.
2. **Add it to an allowed, non-private team.** Put the user on the team(s) your build-creator gate accepts. The team must be **non-private** — `BUILDKITE_BUILD_CREATOR_TEAMS` only lists non-private team slugs, so membership of a private team won't satisfy the check.

{% hint style="info" %}
The gate matches on the commit's **author email**, which Buildkite treats as unverified — Nullify's commits do **not** need to be GPG-signed for this to work. (This is also why the account is associated with the `…+nullify[bot]@users.noreply.github.com` address rather than a regular mailbox.) This Buildkite user is **separate** from the API-token service account in Step 1: the token account authenticates Nullify's log reads; this user lets Nullify's commits run a build.
{% endhint %}

## Step 6: Verify <a href="#step-6-verify" id="step-6-verify"></a>

End-to-end, the simplest way to confirm everything works:

1. Push a commit to an open pull request so Buildkite runs a build.
2. Confirm the `buildkite/…` status appears on the GitHub PR (this proves Steps 3–4).
3. If a build fails on a Nullify autofix PR, watch Nullify pick up the failure and push a follow-up commit — that confirms log fetching and iteration are working.

## Troubleshooting

| Symptom                                                                      | Likely cause and fix                                                                                                                                                                 |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Autofix PR opened, but Buildkite never runs a build** on Nullify's commits | Your org gates build creators by team, and Nullify's Buildkite user isn't invited, isn't on an allowed team, or is on a **private** team. See [Step 5](#build-creator-access).       |
| Connector saved but autofix never reacts to Buildkite failures               | The GitHub permission acknowledgement (Step 4) is still pending, or commit statuses aren't being published (Step 3). Check both.                                                     |
| **No `buildkite/…` status on the GitHub PR**                                 | The Buildkite GitHub App isn't installed, or the pipeline doesn't have **Publish commit statuses** enabled. See Step 3.                                                              |
| **Setup rejects the token naming missing scopes**                            | Add the named scopes to the token in Buildkite (or mint a new one) and save again.                                                                                                   |
| Builds are detected but **logs aren't fetched**                              | The token is missing the `read_build_logs` scope, or it was created in the wrong organization. Re-check Step 1 and rotate the token if needed.                                       |
| **Status shows Active but nothing happens**                                  | The token and organization were verified when saved. Check the GitHub permission acknowledgement and commit-status publishing in Steps 3–4, then run the end-to-end check in Step 6. |
| Token was created by someone who has since left                              | The token stops working when their account is deactivated. Re-mint it from a dedicated service-account user (see the overview) and update it in Step 2.                              |

## Rotating the token

1. Create a new token in Buildkite (Step 1) — from the same service-account user.
2. In Nullify, open **Settings → Connectors → Buildkite**, enter the new token, and select **Save changes**.
3. Revoke the old token in Buildkite.

There's no downtime: Nullify uses the new token as soon as you save it.

## Removing the integration

1. Go to **Settings → Connectors → Buildkite**.
2. Click **Disconnect**.

This removes your stored token and organization slug from Nullify, stopping authenticated build-status reads, log retrieval, and any gate annotations through this connection. It does not delete your Buildkite pipelines.
