On this page
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.jsonin 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.
Azure DevOps
Add a pipeline variable named
GRAPHWAKE_KEYand mark it secret. Azure does not hand secret variables to scripts on its own, which is why the snippet maps it inenv.Bitbucket Pipelines
Add a secured repository variable named
GRAPHWAKE_KEY.Jenkins
Add a credential of kind Secret text with the ID
graphwake-key. The snippet binds it toGRAPHWAKE_KEYwithwithCredentials.
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.
- 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=PullRequestas 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’sprtrigger. - With the Graphwake extension installed in your organisation, the
GraphwakeUpload@0task does the same with two inputs,spec: openapi.jsonandkey: $(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.
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: *graphwakebitbucket-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.
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.
| Platform | Pull request build | Live build | Set the live branch with |
|---|---|---|---|
| Azure DevOps | BUILD_REASON=PullRequest | Other builds of a live branch | A pipeline variable GRAPHWAKE_LIVE_BRANCH, or the task’s liveBranch input |
| Bitbucket Pipelines | BITBUCKET_PR_ID | BITBUCKET_BRANCH is a live branch | A repository variable GRAPHWAKE_LIVE_BRANCH |
| Jenkins (multibranch) | CHANGE_ID | BRANCH_NAME (or GIT_BRANCH) is a live branch | GRAPHWAKE_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 log | Meaning |
|---|---|
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 sent | A feature branch or a tag. On main, check the live branch setting. |
invalid or missing ingest key | Check that GRAPHWAKE_KEY reaches the step. A rotated key stops working after 24 hours. |
this service takes its spec from a URL, not from CI | The 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.
Next steps
- How the agent classifies builds, finds merged pull requests and keeps the key out of logs: inside the Graphwake CI agent.
- What the alert says and where it lands: API change notifications in Slack and Microsoft Teams.
- Who should hear about a breaking change, and why: API breaking change alerts.
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 trialSources and last verified
Sources checked on 2026-11-01.