7 min read
Using StateAnchor with Speakeasy or Fern
What StateAnchor adds to your existing pipeline
Speakeasy and Fern are excellent SDK and documentation generators. The gap they don’t address: they generate artifacts from whatever spec they receive, correct or not. If a breaking change lands in your spec, Speakeasy regenerates your SDK with the breaking change already baked in — it has no way to know it shouldn’t.
StateAnchor sits upstream of Speakeasy in your CI pipeline and blocks the push if the change would break consumers. Speakeasy only runs on commits StateAnchor approves.
The combined workflow
git push → StateAnchor gate evaluates (ERR blocks, PASS continues) → on PASS: Speakeasy regenerates SDK and docs from verified spec
Your spec is the same spec Speakeasy has always read. StateAnchor reads it first, scores the change, and either blocks (ERR) or allows the workflow to continue (PASS / WARN). Speakeasy never sees a spec that StateAnchor rejected.
Setup: adding StateAnchor to your Speakeasy workflow
Step 1: Add stateanchor.yaml to your repo ~5 minutes
Create stateanchor.yamlin your repo root. Map the endpoints your OpenAPI spec describes. For gate-only mode — no artifacts generated by StateAnchor, zero conflict with Speakeasy — set outputs: [].
service: payments-api
version: "2.4.0"
server:
base_url: https://api.acme.com/v2
auth:
type: bearer
endpoints:
- name: createCharge
method: POST
path: /charges
description: Create a new charge
body:
amount: integer
currency: string
- name: getCharge
method: GET
path: /charges/{id}
description: Retrieve a charge by ID
outputs: [] # gate-only: StateAnchor gates but generates nothingStep 2: Add the StateAnchor GitHub Action before your Speakeasy action ~3 minutes
In your CI workflow, place the StateAnchor gate step before the Speakeasy generation step. When StateAnchor returns ERR, the workflow fails and Speakeasy never runs. When StateAnchor passes, the workflow continues and Speakeasy runs normally.
# .github/workflows/api-pipeline.yml
name: API Pipeline
on:
pull_request:
jobs:
gate-and-generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# StateAnchor gate runs first -- ERR verdict fails the workflow
- uses: stateanchor/sync-action@v1
with:
api-key: ${{ secrets.STATEANCHOR_API_KEY }}
mode: gate-only
# Speakeasy only runs if StateAnchor passed
- uses: speakeasy-api/sdk-generation-action@v1
with:
speakeasy_api_key: ${{ secrets.SPEAKEASY_API_KEY }}mode: gate-only) for 3–5 days to build confidence in the gate decisions before enabling enforcement.Step 3: Make the gate a required status check ~2 minutes
In your GitHub repo settings, add the StateAnchor gate check as a required status check on your main branch. This ensures no PR can merge without a gate PASS — and since Speakeasy runs sequentially after the gate, your generated SDKs and docs always reflect a spec that passed the safety check.
What you keep
- Your Speakeasy SDK output format, language targets, and structure
- Your hosted Speakeasy documentation
- Your Speakeasy dashboard and configuration
- Your existing CI/CD setup — StateAnchor adds a step before Speakeasy, it doesn’t modify existing ones
- Your team’s workflow — the gate runs automatically on every push
What changes
One thing: breaking API changes are caught before Speakeasy runs, not after your SDK consumers report failures.
Gate-only mode
If you want StateAnchor enforcement without any artifact generation from StateAnchor, use gate-only mode. Set outputs: []in stateanchor.yaml. The gate runs, enforces, and logs — no SDKs or docs generated by StateAnchor, zero artifact conflict with Speakeasy.