Webhook icon
Schedule icon
If icon
Fail icon
Download icon
Commands icon
Docker icon
OutputValues icon
SlackIncomingWebhook icon
Pause icon
Set icon

Gate API Releases on OpenAPI Breaking Changes

Compare OpenAPI specs before release, post the changelog, and require approval for each breaking change.

Categories
CoreInfrastructure

A field type that changes, a parameter that becomes required, or an endpoint that disappears breaks every client that depends on it, and code review rarely catches it because the diff looks harmless. This blueprint puts a contract check between CI and production. It compares the spec about to ship with the one live today, tells the team what changes for consumers, and lets a breaking change through only when the API owner signs off on it.

How it works

  1. release_candidate (Webhook) is called by CI with the service, release, and both spec URLs. nightly_staging_check (Schedule) compares staging with production every weekday morning, so breaking changes surface (and can be approved) days before the release.
  2. validate_request (If) checks the service name and URLs; download_base and download_revision fetch both specs (JSON or YAML) with retries.
  3. diff runs oasdiff in a container. It resolves $refs and classifies more than 250 kinds of change as breaking (3), warning (2) or compatible (1), each with a stable fingerprint.
  4. summary keeps the breaking changes at or above block_on that have not been approved before for this service. Approvals live in the KV store under openapi_approved_<service>.
  5. report posts the changelog for every change, so consumers learn about additions too.
  6. gate (If) runs only when there are unapproved breaking changes. It asks the API owner, then waits (Pause, 2 hours):
    • Approve these breaking changes records each change's fingerprint, the release, the note and the execution in the KV store; the run succeeds and the release ships.
    • Block release, or no answer, fails the run with the first breaking change in the message.

Call it from CI

id=$(curl -s -X POST "$KESTRA/api/v1/main/executions/webhook/company.team/openapi-breaking-change-gate/$KEY" \
  -H 'Content-Type: application/json' \
  -d '{"service":"orders-api","release":"'$TAG'","base_spec_url":"https://api.example.com/openapi.json","revision_spec_url":"'$PREVIEW_URL'/openapi.json"}' | jq -r .id)
until state=$(curl -s "$KESTRA/api/v1/main/executions/$id" | jq -r .state.current); [[ $state =~ SUCCESS|FAILED|KILLED ]]; do sleep 15; done
[ "$state" = SUCCESS ]

Error handling

  • Approvals are per change, not per release. A CI retry of the same release passes without asking again ("2 breaking already approved"). A new breaking change in a later build is still caught, even if the release name is the same.

  • Silence blocks. With no decision within 2 hours the release is blocked, with a message naming the change, for example "DELETE /orders/{id}: api removed without deprecation".

  • Every failure fails the gate:

    • an unreachable or missing spec (404) fails after retries;
    • an invalid spec fails at diff with oasdiff's parse error;
    • a malformed request fails before downloading.

    In every case the errors alert says the release should not ship until an execution succeeds.

  • block_on: WARN also gates potential breaks, such as removing a deprecated endpoint before its sunset date.

What this blueprint teaches about Kestra

  • A webhook-driven gate whose execution state (SUCCESS or FAILED) is the answer CI waits for.
  • Flow variables that merge a webhook body with input defaults, so one flow serves every service.
  • Running a CLI tool in a container with the Docker task runner, with downloaded files as inputFiles and its JSON as outputFiles.
  • Using the KV store for approvals with memory: jq merges the fingerprints and keeps a bounded history.

Prerequisites

  • URLs where both specs can be downloaded: the production service's /openapi.json and the candidate's (a preview environment, or a CI artifact URL).
  • A Kestra worker that can run Docker containers (Docker task runner) to pull tufin/oasdiff.
  • A Slack incoming webhook.

Secrets

  • SLACK_WEBHOOK_URL: Slack incoming webhook.
  • OPENAPI_GATE_WEBHOOK_KEY: a long random string, the webhook key CI uses in the URL ($KEY in the example above).

Inputs

  • service, release (STRING): names in messages and the KV key; a webhook body overrides them.
  • base_spec_url, revision_spec_url (STRING): production and candidate specs; a webhook body overrides them.
  • block_on (SELECT, default ERR): ERR or WARN.
  • kestra_url (STRING): link in the approval message.

Outputs

  • breaking_changes (JSON): changes that needed approval in this run.
  • changes (JSON): the full oasdiff changelog.

Quick start

  1. Add the Slack secret and set the input defaults to your production and staging spec URLs.
  2. Run the flow: it posts the changelog between the two, and asks for approval if staging breaks the contract.
  3. Add the OPENAPI_GATE_WEBHOOK_KEY secret and the webhook call to your deployment pipeline, before the production step. Enable nightly_staging_check once the spec URLs are yours.
  4. Review approvals with kv('openapi_approved_orders-api').history.

Plugins used

See How

New to Kestra?

Use blueprints to kickstart your first workflows.