Guide

Breaking change alerts from Azure DevOps, Bitbucket Pipelines and Jenkins

Last checked 2026-11-148 min read

To get breaking change alerts from Azure DevOps, Bitbucket Pipelines or Jenkins, add one step to the pipeline that builds your API: it runs the open-source Graphwake CI agent, which sends the service’s OpenAPI spec to Graphwake with the build’s context. On a pull request build the spec is stored as a draft, so the teams subscribed to a changed endpoint can hear about a breaking change before the merge. On a build of the live branch it becomes the live spec. Setup takes the service’s ingest key stored as a CI secret named GRAPHWAKE_KEY, the snippet for your platform below, and the correct live branch.

ExamplePull request #1284 · orders-api
#team-checkoutGraphwake in Slack

Breaking change in PR #1284

GET /orders/{id} · orders-api

Owned by payments-team

  • total was removed
  • currency is now required

Author: dana-k

orders-api3 breaking changes
  • checkoutSlack #team-checkout
  • mobile-appSlack #mobile-alerts
  • reportingEmail
  • searchquiet, not subscribed
In Graphwake: impact79/100High
A breaking change reaches only the three teams that subscribed to the endpoints it touches. search stays quiet. The impact score is in Graphwake, not in the message.
On this page
  1. Before you start
  2. Step 1: store the ingest key as a secret
  3. Step 2: add the step
  4. Step 3: set the live branch
  5. Step 4: confirm it worked
  6. How Graphwake does it
  7. Next steps
  8. Questions

Before you start

  • A service in Graphwake that takes its spec from CI. In Graphwake, add a service and choose CI pipeline under “Where the spec comes from”. Graphwake shows the service’s ingest key (dsk_...) only once, so copy it straight away.
  • A build step that writes the spec. The agent sends the spec as it is, YAML or JSON. It does not generate it. The snippets below expect openapi.json in the working directory. Recipes for ASP.NET Core, Spring Boot, FastAPI, NestJS and Go are in the agent README.
  • One key per service. A repository with several services runs the step once per service, each with its own spec and its own key.

If you are on GitHub Actions or GitLab CI, the same agent has a ready-made action and template, described in inside the Graphwake CI agent. For a diff that comments on the pull request instead, see OpenAPI diff in CI.

Step 1: store the ingest key as a secret

Every snippet reads the key from the GRAPHWAKE_KEY environment variable. Keep it in your CI’s secret store and pass it as that variable. The agent also accepts --key, but command-line arguments show up in process lists, so use the variable.

  1. Azure DevOps

    Add a pipeline variable named GRAPHWAKE_KEY and mark it secret. Azure does not hand secret variables to scripts on its own, which is why the snippet maps it in env.

  2. Bitbucket Pipelines

    Add a secured repository variable named GRAPHWAKE_KEY.

  3. Jenkins

    Add a credential of kind Secret text with the ID graphwake-key. The snippet binds it to GRAPHWAKE_KEY with withCredentials.

The agent hides the key in everything it prints. If the key leaks or is lost, rotate it from the service’s Source tab in Graphwake and update the secret. The old key keeps working for 24 hours, which gives you time to update every pipeline.

Step 2: add the step

Azure DevOps

Add this script step to the pipeline’s steps, after the step that writes openapi.json. It downloads the agent release, checks it against checksums.txt and runs it.

yaml
  - bash: |
      VERSION=0.1.0
      BASE=https://github.com/graphwake/agent/releases/download/v$VERSION
      ARCHIVE=graphwake_${VERSION}_linux_amd64.tar.gz
      curl -fsSLO "$BASE/$ARCHIVE" && curl -fsSLO "$BASE/checksums.txt" &&
        sha256sum --check --ignore-missing checksums.txt &&
        tar -xzf "$ARCHIVE" graphwake &&
        ./graphwake upload --spec openapi.json || echo "graphwake: agent not run, spec not sent"
    displayName: Upload spec to Graphwake
    continueOnError: true
    env:
      GRAPHWAKE_KEY: $(GRAPHWAKE_KEY)
azure-pipelines.yml, from the agent README.
  • Run it on pull requests and on main. The agent treats a build with BUILD_REASON=PullRequest as a pull request build. For Azure Repos, pull request builds come from a build validation branch policy on the target branch; for a GitHub repository, from the pipeline’s pr trigger.
  • With the Graphwake extension installed in your organisation, the GraphwakeUpload@0 task does the same with two inputs, spec: openapi.json and key: $(GRAPHWAKE_KEY).
  • Closed pull requests. Azure does not report a pull request closed without merging. Drafts of a pull request that never merges close on their own after 14 days.

Bitbucket Pipelines

Define the step once with an anchor and run it for every pull request and for builds of main. Replace the first script line with your own spec build, or put the step after the one that produces openapi.json.

yaml
definitions:
  steps:
    - step: &graphwake
        name: API spec
        script:
          - ./build-openapi.sh   # produce openapi.json
          - >-
            VERSION=0.1.0; BASE=https://github.com/graphwake/agent/releases/download/v$VERSION;
            ARCHIVE=graphwake_${VERSION}_linux_amd64.tar.gz;
            (curl -fsSLO "$BASE/$ARCHIVE" && curl -fsSLO "$BASE/checksums.txt" &&
            sha256sum --check --ignore-missing checksums.txt && tar -xzf "$ARCHIVE" graphwake &&
            ./graphwake upload --spec openapi.json) || echo "graphwake: agent not run, spec not sent"
pipelines:
  pull-requests:
    '**':
      - step: *graphwake
  branches:
    main:
      - step: *graphwake
bitbucket-pipelines.yml, from the agent README. The step’s image needs curl, sha256sum and tar.

The agent reads BITBUCKET_PR_ID on pull request builds and BITBUCKET_BRANCH on branch builds. If your live branch is not main or master, rename the branches entry and set the live branch (Step 3).

Jenkins

Use a multibranch pipeline, so Jenkins sets CHANGE_ID, CHANGE_URL and CHANGE_AUTHOR on pull request builds and BRANCH_NAME on branch builds. Add this stage to the Jenkinsfile after the stage that writes openapi.json. The agent runs from its image, so the Jenkins agent needs Docker.

text
stage('API spec') {
  steps {
    withCredentials([string(credentialsId: 'graphwake-key', variable: 'GRAPHWAKE_KEY')]) {
      sh 'docker run --rm -e GRAPHWAKE_KEY -e BRANCH_NAME -e CHANGE_ID -e CHANGE_URL -e CHANGE_AUTHOR -e GIT_COMMIT -e JENKINS_URL -v "$PWD:/w" -w /w ghcr.io/graphwake/agent:0.1.0 upload --spec openapi.json || true'
    }
  }
}
Jenkinsfile, from the agent README. The -e flags pass the build’s variables into the container.

The image has no git. On Jenkins that means the agent cannot check that the build is still the branch tip, and it cannot find the merged pull request, so a merge shows up in Graphwake as a separate live change. To get both, download the binary in an sh step instead, with the checksum-checked script from the README’s Install section; it then uses the git in your workspace. The same script works if the Jenkins agent has no Docker. If you keep the image, pin it by digest (ghcr.io/graphwake/agent@sha256:...).

Step 3: set the live branch

A build of a live branch publishes the live spec, a pull request build stores a draft, and any other build sends nothing. None of these three platforms tells the agent its default branch, so the agent treats main and master as live unless you configure something else.

How the CI step classifies a buildA CI build is classified three ways. A pull request build stores the spec as a draft so consumer teams hear before merge. A live-branch build makes the spec live, sends notifications and settles the PR draft as merged. Anything else sends nothing.CI buildPull requestbuildLive-branchbuildAnything elseDraft specstored as draftLive specPR draft: mergedNothing sentConsumer teamshear before mergeNotificationssent on mergeno spec, no alert
What the step does with each build. Only pull request builds and live-branch builds send anything.
PlatformPull request buildLive buildSet the live branch with
Azure DevOpsBUILD_REASON=PullRequestOther builds of a live branchA pipeline variable GRAPHWAKE_LIVE_BRANCH, or the task’s liveBranch input
Bitbucket PipelinesBITBUCKET_PR_IDBITBUCKET_BRANCH is a live branchA repository variable GRAPHWAKE_LIVE_BRANCH
Jenkins (multibranch)CHANGE_IDBRANCH_NAME (or GIT_BRANCH) is a live branchGRAPHWAKE_LIVE_BRANCH in the stage’s environment, passed into the container with -e GRAPHWAKE_LIVE_BRANCH

The value is a comma-separated list and globs are allowed: main,release/* publishes from main and from every release/ branch. Before a live send the agent checks that the build is still the tip of its branch, where it can, so an old re-run does not replace a newer live spec.

Step 4: confirm it worked

Run the pipeline on a pull request that touches the API, then on main. The step prints one line, which tells you what happened:

Line in the CI logMeaning
graphwake: spec sent (pull request #42)Draft stored for the pull request.
graphwake: spec sent (pull request #42 merged, live)The spec is live and the pull request’s notifications are settled.
graphwake: spec sent (live)The spec is live.
graphwake: not a pull request or live-branch build, nothing sentA feature branch or a tag. On main, check the live branch setting.
invalid or missing ingest keyCheck that GRAPHWAKE_KEY reaches the step. A rotated key stops working after 24 hours.
this service takes its spec from a URL, not from CIThe service is set up to poll a URL. Use a service whose source is CI pipeline.
spec rejected: ...The file is not valid OpenAPI, or the service no longer exists.

Then open the service in Graphwake. Its Source tab shows the CI platform the agent detected and the time of the last upload. To see an alert end to end, turn on Alerts on open pull requests under Settings → Alerts, subscribe a test team to an endpoint, and open a pull request that removes a response field from it. The team hears about it in its Slack, Microsoft Teams or email channel, and the impact score shows up on the change in Graphwake.

How Graphwake does it

  • Told while it is a pull request

    A pull request build stores a draft, so the teams that call a changed endpoint can hear about a breaking change before the merge.

  • Only the subscribed teams

    Consumer teams subscribe to the endpoints they call. A breaking change goes to those teams in Slack, Microsoft Teams or email, not to a company-wide channel.

  • One agent, five CI systems

    The same open-source agent runs in Azure DevOps, Bitbucket Pipelines, Jenkins, GitHub Actions and GitLab CI, with the same two settings: the spec and the key.

  • Only the spec

    The step sends the OpenAPI file and the build’s context. Your source code is not sent, and each change set gets an impact score with public rules.

See who your next breaking change reaches.Paste a spec URL or add one CI step. 7-day free trial. Cancel before day 7 and you pay nothing.
Start free trial

Next steps

Questions

How do I send an OpenAPI spec to Graphwake from Azure DevOps?

Store the service’s ingest key as a secret pipeline variable GRAPHWAKE_KEY, then add the script step above (or the GraphwakeUpload@0 task, if your organisation has the Graphwake extension) after the step that writes the spec. Run the pipeline on pull requests and on main.

Does Bitbucket Pipelines need anything special?

A secured repository variable GRAPHWAKE_KEY, and the step in both pull-requests and the live branch under branches. The step’s image needs curl, sha256sum and tar.

Does the Jenkins setup work with a plain pipeline job?

Use a multibranch pipeline: it sets CHANGE_ID on pull request builds and BRANCH_NAME on branch builds, which is how the agent tells them apart. On any other CI, pass --pr on pull request builds and --live on release builds.

We release from release branches, not main. What do I set?

Set GRAPHWAKE_LIVE_BRANCH to a comma-separated list with globs, for example main,release/*. Builds of those branches publish the live spec.

Pick an endpoint. See who it reaches. 7-day free trial. Cancel before day 7 and you pay nothing.

Start free trial

Sources and last verified

Sources checked on 2026-11-01.