Documentation Index: Fetch llms.txt first to discover every published page. This page is also available as Markdown at /codegen-workflow.md.
Verified · 8/11/2026

Regenerating SDK Code from the OpenAPI Spec

Run the root Makefile codegen targets to turn openapi/primitive-api.yaml into typed clients for the Node, Python, and Go SDKs in one pass.

Every typed method in the Node, Python, and Go SDKs is generated from one file: openapi/primitive-api.yaml. Change that spec (or json-schema/email-received-event.schema.json), regenerate, and all three SDKs pick up the change with matching types.

Note

Prerequisites: a clone of the sdks monorepo, Node.js with pnpm installed (for the TypeScript codegen scripts), and each SDK's own toolchain (uv for Python, Go >=1.25 for Go) available on PATH.

What gets regenerated#

The pipeline runs in two stages, both driven from openapi/primitive-api.yaml:

  1. Normalize the spec. packages/api-core's generate-openapi-artifacts.ts reads the hand-written OpenAPI 3.1 YAML and writes openapi/primitive-api.codegen.json, a codegen-friendly OpenAPI 3.0.3 JSON document. This is the file every language generator actually consumes. Full detail on this step lives on OpenAPI Spec Normalization and Codegen Artifacts.
  2. Generate per-language clients. Each SDK points its own generator at primitive-api.codegen.json and produces a typed client in its own idiom (a fetch-based TypeScript client for Node, an openapi-python-client package for Python, an ogen client for Go). See Non-TypeScript Codegen: Go and Python Clients for the Go/Python specifics.

You never hand-edit primitive-api.codegen.json. It's a build artifact; edit openapi/primitive-api.yaml and regenerate.

  1. 1

    Edit the OpenAPI spec#

    Make your change in openapi/primitive-api.yaml. Author it as OpenAPI 3.1, that's the single source of truth for every downstream client.

    # openapi/primitive-api.yaml
    paths:
      /v1/widgets:
        post:
          operationId: createWidget
          summary: Create a widget
          # ...
    

    If the change is to the inbound webhook shape instead, edit json-schema/email-received-event.schema.json, see Webhook Schema Codegen for that half of the pipeline.

  2. 2

    Regenerate every SDK from the repo root#

    Run the root Makefile targets. This is the recommended path for any change that must ship in a single commit:

    cd sdks
    make node-generate python-generate go-generate
    

    Each target wraps that language's native generate script, so it's equivalent to (but easier to keep in sync than) running them individually.

    Tip

    Iterating on a single language locally before your final commit? Run that language's native script directly instead of all three, for example pnpm --dir sdk-node generate. Just make sure the final commit that changes the OpenAPI spec regenerates all three languages before you open a PR, reviewers and CI expect the generated output and the spec to move together.

  3. 3

    Verify the regenerated output#

    Confirm the codegen artifact updated and each SDK's generated client compiles/typechecks:

    git status openapi/primitive-api.codegen.json
    make node-check python-check go-check
    

    Expect openapi/primitive-api.codegen.json to show as modified (or untouched, if your spec edit didn't change the normalized shape), and each *-check target to pass. A failing node-check after a spec edit usually means a new required field broke an existing hand-written caller, fix the call site, not the generated code.

  4. 4

    Commit the spec change and the generated files together#

    git add openapi/primitive-api.yaml openapi/primitive-api.codegen.json \
      packages/api-core/src/openapi/ sdk-node/src/api/ \
      sdk-python/src/primitive/api/ sdk-go/api/
    git commit -m "Add POST /v1/widgets and regenerate SDK clients"
    

    Commit the spec edit and every regenerated file in the same commit (or the same PR) so the repo never has an SDK whose generated code disagrees with the spec it was built from.

What each generator does under the hood#

Each language runs its own generator against openapi/primitive-api.codegen.json, then applies language-specific fixups to the output.

packages/api-core owns the TypeScript generation. Its generate script runs both stages:

// packages/api-core/package.json
{
  "scripts": {
    "generate:openapi": "tsx scripts/generate-openapi-artifacts.ts",
    "generate:api": "openapi-ts -f openapi-ts.config.ts && tsx scripts/fix-generated-api-imports.ts",
    "generate": "pnpm generate:openapi && pnpm generate:api"
  }
}

generate-openapi-artifacts.ts reads openapi/primitive-api.yaml, writes the normalized primitive-api.codegen.json, and also emits two TypeScript files consumed elsewhere in the repo: src/openapi/openapi.generated.ts (the raw OpenAPI document as a JS constant, for tooling like primitive describe) and src/openapi/operations.generated.ts (the operation manifest, one entry per operation with its command name, method, path, parameters, and inlined request/response JSON Schemas).

generate:api then runs @hey-api/openapi-ts against the normalized spec to produce the actual fetch client, followed by fix-generated-api-imports.ts, a post-processing pass that:

  • adds .js extensions to every relative import (required for the package's ESM output)
  • guards the Content-Type: application/json header on optional-body operations so it's only sent when a body is actually present
  • repairs the generated MemoryJsonValue type, which @hey-api/openapi-ts currently widens to unknown on the OpenAPI 3.1 type: "null" branch

Full detail on that fixup pass lives on Generated TypeScript Client Fixups. Both sdk-node and cli-node bundle @primitivedotdev/api-core's output inline; it is never published as its own package. See What is API Core?.

Verify the full pipeline#

Verify a regeneration by running each language's check target (typecheck, lint, and build, not just tests) before you commit:

make node-check
make python-check
make go-check

Or run everything the CI runs in one shot:

make check
Warning

Never hand-edit anything under packages/api-core/src/openapi/, sdk-node/src/api/, sdk-python/src/primitive/api/, or sdk-go/api/. Every file in those trees is regenerated on the next make *-generate run and any manual edit is silently overwritten. If the generated output is wrong, fix the generator script or the OpenAPI spec, not the output.

Next steps#

Was this page helpful?

© Primitive SDKs

Powered by Browzer