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

Monorepo Structure and Release Process

Explains how the polyglot sdks repository is laid out, how the shared OpenAPI and JSON Schema contracts flow into each language's generated code, and how each package is released independently.

The sdks repository is a single polyglot monorepo: one Node.js SDK, one Python SDK, one Go SDK, and one Node-based CLI, all generated from two shared contracts instead of hand-maintained independently in each language.

Repository layout#

The repo keeps the two shared contracts at the root and one directory per shipped package:

sdks/
  .github/workflows/
  openapi/
  json-schema/
  sdk-go/
  sdk-node/
  sdk-python/
  cli-node/
  packages/api-core/
  test-fixtures/
  • openapi/primitive-api.yaml: the hand-written OpenAPI 3.1 spec, the single source of truth for the HTTP API surface consumed by every SDK and the CLI.
  • json-schema/email-received-event.schema.json: the canonical JSON Schema for the inbound webhook payload (the email.received / EmailReceivedEvent shape).
  • sdk-node/, sdk-python/, sdk-go/: the three language SDKs.
  • cli-node/: the primitive CLI, built on the Node SDK and packages/api-core.
  • packages/api-core/: the workspace-internal, never-published TypeScript package holding the generated OpenAPI client, the operation manifest, and PrimitiveApiClient. See What is API Core?.
  • test-fixtures/: shared behavioral-parity fixtures (schema validation outcomes, signature verification, auth classification, sender trust, forward compatibility) that all three SDKs must pass identically.

The two shared contracts#

Every SDK is responsible for the same behavior, driven by the same two files:

ContractFileDrives
Webhook payload shapejson-schema/email-received-event.schema.jsonTyped EmailReceivedEvent models, runtime schema validation, forward-compatibility handling of unknown event types
HTTP API surfaceopenapi/primitive-api.yamlGenerated typed clients, the operation manifest, request/response types

For the webhook side, each SDK independently:

  1. verifies the Primitive-Signature header
  2. parses the raw request body
  3. validates the payload against the canonical schema
  4. exposes a typed email.received event model
  5. preserves forward compatibility for event types the SDK doesn't recognize yet

The full webhook contract (signature format, event catalog, forward-compatibility guarantees) is explained once, in Webhook Events Overview; the normalized ReceivedEmail object built on top of it is covered in Inbound and Outbound Email Model.

For the API side, openapi/primitive-api.yaml is authored as OpenAPI 3.1. It is never edited by generators; instead it is normalized into a build artifact, primitive-api.codegen.json (OpenAPI 3.0.3), that the three language-specific code generators actually consume. That normalization step, the per-language generator pipelines, and the api-core package's role are explained in Architecture: Shared Codegen Pipeline and OpenAPI Spec Normalization and Codegen Artifacts. Running the full regeneration pipeline end to end is covered in Regenerating SDK Code from the OpenAPI Spec.

Tip

Author and edit openapi/primitive-api.yaml directly. Never hand-edit primitive-api.codegen.json; it's a generated build artifact regenerated by the api-core scripts.

SDK layers#

Each SDK layers the same generated artifacts on top of the same two contracts, but exposes them through idiomatic language surfaces:

SDKPackage pathPublished asGenerated artifacts
Nodesdk-node/@primitivedotdev/sdk (root, webhook, api, openapi, contract, parser subpaths)AJV standalone validator, TypeScript types, generated API client, OpenAPI document export
Pythonsdk-python/primitivedotdev (import name primitive)Packaged webhook schema copy, generated webhook models, generated API client under primitive.api
Gosdk-go/github.com/primitivedotdev/sdks/sdk-goEmbedded webhook schema source, generated API client under sdk-go/api
CLIcli-node/primitive (npm)Bundles the Node SDK and api-core inline

Node additionally ships contract and parser as Node-only subpaths (not exposed in Python or Go).

Change strategy#

When changing shared webhook behavior:

  1. Update json-schema/email-received-event.schema.json if the payload contract itself changes.
  2. Regenerate language-specific artifacts for all three SDKs.
  3. Update test-fixtures/ when the expected cross-language behavior changes.
  4. Run make check.
  5. Review each SDK for language-specific helper implications.

When changing the HTTP API contract:

  1. Update openapi/primitive-api.yaml.
  2. Regenerate the Node, Python, and Go API clients.
  3. Run make node-check python-check go-check.
  4. Verify the Node smoke test still exposes api and openapi subpaths, and still confirms the SDK tarball installs no primitive bin (the CLI ships that bin from the separate cli-node package).

Changing only one SDK's internal implementation, with no intended change to cross-language public behavior, does not require touching the shared fixture contract.

Tooling model#

The root Makefile is the primary task interface for every workflow; each SDK keeps its own native package tooling (pnpm, uv, go) inside its own directory:

make node-generate python-generate go-generate
make check
make build
make shared-check

Drop to a single SDK's native commands only when iterating locally on that one language before the commit that must include all three regenerated outputs:

cd sdk-node && pnpm typecheck && pnpm test
cd sdk-python && uv sync --dev && uv run pytest && uv run ruff check . && uv run basedpyright
cd sdk-go && go test ./... && go test -run TestSharedCompatibilityFixtures ./...

.github/workflows/sdk-checks.yml runs the same root make targets contributors use locally, across five categories: Node SDK, Node CLI, Python SDK, Go SDK, and shared fixture compatibility across all three languages. Using the same targets in CI and locally keeps the documented workflow and the enforced workflow aligned.

Release process#

Each package versions and releases independently, triggered by a version bump landing on main:

PackageVersion fileRelease trigger
Node SDK (@primitivedotdev/sdk)sdk-node/package.jsonPR merges with a new version
CLI (primitive, mirrored as primcli / @primitivedotdev/cli)cli-node/package.jsonPR merges with a new version
Python SDK (primitivedotdev)sdk-python/pyproject.tomlPR merges with a new version
Go SDK (github.com/primitivedotdev/sdks/sdk-go)sdk-go/VERSIONPR merges with a new value (starts at unreleased, so the first automation PR does not tag a release)

Before releasing any package#

  1. Confirm the working tree is clean.
  2. Update the relevant version metadata in a release PR.
  3. If the webhook or API contract changed, regenerate artifacts for every affected SDK.
  4. Ensure the release PR passes the required SDK Checks workflow.
  5. Review the SDK README and changelog notes for public API changes.
  6. Merge the release PR into main.
  1. 1

    Node SDK release#

    Open a PR bumping sdk-node/package.json to the target version and merge it. The Node Release workflow verifies the version bump, publishes to npm via trusted publishing/OIDC, and tags sdk-node/vX.Y.Z plus a GitHub release. Verify with npm view @primitivedotdev/sdk version, and confirm the packed tarball exposes the root, webhook, api, openapi, contract, and parser subpaths, and installs no primitive bin.

  2. 2

    CLI release#

    Open a PR bumping cli-node/package.json and merge it. The CLI Release workflow verifies the bump, publishes to npm, and tags cli-node/vX.Y.Z. The same workflow mirror-publishes two identical builds under primcli and the legacy scoped @primitivedotdev/cli, locked to the same version; both are no-ops when that version already exists, so a re-run is safe. Verify with npm view primitive version, npm view primcli version, and npm view @primitivedotdev/cli version.

  3. 3

    Python SDK release#

    Open a PR bumping sdk-python/pyproject.toml and merge it. The Python Release workflow verifies the bump, publishes to PyPI, and tags sdk-python/vX.Y.Z. Verify the release on PyPI.

  4. 4

    Go SDK release#

    Open a PR updating sdk-go/VERSION to a semantic version (e.g. 0.1.0) and merge it. The Go Release workflow creates the subdirectory-prefixed sdk-go/vX.Y.Z tag plus a GitHub release. Verify the tag resolves through the Go module proxy.

Note

Coordinate Node SDK and CLI releases when both ship in the same cycle: cut the SDK release first so its npm version is published, then bump the CLI's @primitivedotdev/sdk dependency range if needed before shipping the CLI.

Shared contract changes in a release#

If a release includes schema or shared-fixture changes:

  1. Update json-schema/email-received-event.schema.json.
  2. Regenerate SDK artifacts.
  3. Update test-fixtures/ if the behavioral contract changed.
  4. Ensure the PR passes SDK Checks again before merging.

If a release includes API spec changes:

  1. Update openapi/primitive-api.yaml.
  2. Regenerate the Node, Python, and Go API clients.
  3. Ensure the PR passes SDK Checks again before merging.

Both npm packages (@primitivedotdev/sdk and primitive, plus its mirrors) use npm trusted publishing from GitHub Actions; no npm API tokens are configured. A brand-new mirror package name needs a one-time manual npm publish to claim it before trusted publishing can take over.

Next steps#

Was this page helpful?

© Primitive SDKs

Powered by Browzer