Build your integrationReleases

Publish your packages

Turn a reviewed Target Draft into a release, then publish from repository-owned automation without giving Typeship registry credentials.

Publishing distributes a merged Release to its registry. Typeship prepares the package in a Draft pull request; you review and merge it, and your repository's workflow publishes the accepted version.

You need a repository Delivery, permission to configure publishing in that repository, and the registry permissions for the package name. Use these states to track the result:

StateMeaning
Latest releaseThe most recent versioned Release, shown in the Console and recorded as an immutable rel_*.
DraftThe Target pull request, including its cumulative changes and proposed version.
Publishing statusThe result for the GitHub Release and each npm, PyPI, Go, or MCP Registry destination after merge. Each can succeed or fail independently.
  1. 1 · API contract
    Connect the source you maintain
    Single- or multi-file OpenAPI or GraphQL from GitHub or a URL
  2. 2 · Generated products
    Choose what your users need
    CLI · MCP · TypeScript · Python · Go release independently
  3. 3 · Regeneration
    Build each package from the same source
    Every result records the exact API contract revision it used
  4. 4 · GitHub review
    Approve source; release destinations
    Source PR: API understanding · destination PR: release readiness
  5. 5 · Release
    Accept code, then release when needed
    Package changes create a release. Changes only to tests or checks carry forward without one. Your repository publishes when configured
API contract → generated packages → GitHub source review and destination release → your registry. Repository Deliveries use GitHub, and Typeship never receives your registry tokens.

Merged code, releases, and publishing

Typeship carries the code you last merged into the next Draft, including your edits. The latest release in the Console shows the most recent versioned Release. Publishing status shows whether that release reached each configured destination. These can change at different times.

For example, start with the latest release at 1.0.0 and make two checked merges:

MergeLast merged packagePackage version and releaseConsole and registry result
Add an exported helper with testsIncludes the merged helperAt least 1.1.0 for compatible public functionality; a new rel_* is createdThe latest release shows 1.1.0. If publishing is configured, each registry outcome starts pending and can succeed or fail independently.
Add another test without changing generator output, runtime code, manifests, exports, or documentationIncludes the merged helper and testStays 1.1.0; no new rel_*The latest release still shows the 1.1.0 release. Publishing does not start again, and existing publishing statuses are unchanged.

Inspect GET /drafts/{draft_id} for package checks and release readiness, using the inspection commands; inspect the Target’s release history and publishing status for versioned and registry state. A package manifest or documentation edit still needs a release, even if you made it while adding tests.

Choose the Draft version

Every Target has its own release line. Typeship compares the Draft with the latest release and immediately selects the minimum SemVer version required by the changes:

  • breaking API or package contract: major, or minor before 1.0.0
  • compatible consumer functionality: minor
  • documentation or implementation-only change: patch
  • first release: 0.1.0-alpha.1

New source commits update the same Draft and recalculate that minimum from the cumulative diff. OpenAPI info.version and GraphQL metadata remain API versions; they never set a package version.

Choose a larger version in any of three places:

  • the Target page in the Console
  • PATCH /drafts/{draft_id} with {"version_next":"2.0.0"}
  • the Draft PR title release: 2.0.0

The title must match release: X.Y.Z exactly, including case. Typeship accepts it only from a repository writer and regenerates the same Draft without rewriting its commits. A version below the Draft's required bump fails instead of downgrading the release.

The Draft PATCH requires version_next: a SemVer string selects that exact version, and null restores automatic selection. Omitting version_next, or sending {} or [] for it, is invalid. To reject an intervening change, send the ETag from your last Draft read in the If-Match header; a mismatch returns 412 precondition_failed before saving the selection or starting generation. Omitting If-Match applies the selection to the current Draft.

A 502 means the version selection was saved but regeneration failed. Retrieve the Draft before retrying; repeating the same selection resumes unfinished generation. If you sent If-Match, use the ETag from that retrieval.

For AI agents

Read GET /drafts/{draft_id} before changing a version. Send its ETag back in If-Match; on 412 precondition_failed, reread and reconsider the cumulative diff. Use version_next: null to return to automatic selection. Never edit generated package metadata directly.

Read Draft readiness

GET /drafts/{draft_id} describes the Draft's latest commit, head_sha. Read it again after the Draft changes; an assessment of an earlier commit does not authorize the new one. compatibility and version are null until the Draft has a generated change.

FieldMeaning
status and reasonWhat to do next. See Follow the Draft status.
errorsEach problem blocking an action_required Draft, with a code, message, and suggested_action, for example checks_failed naming the failed check, draft_title_invalid, or version_too_low. Empty otherwise.
compatibility.apiAPI surface comparison against the latest release: compatible, breaking, or unknown.
compatibility.packagePackage and supported SDK source comparison against the latest release: compatible, breaking, or unknown.
version.bump_requiredMinimum assessed major, minor, or patch bump, or null when none has been determined.
version.correctWhether version_next satisfies the assessed change, or null when no verdict is available.
version.previousVersion used for comparison, or null before the first release.

An unknown comparison means analysis is incomplete or unavailable. Check customizations for the individual checks and conflicts behind the combined decision. After review, apply typeship:breaking-approved to the Draft for an unknown comparison or a break without an approved source pull request. A known API break approved on its source pull request needs no second label when the version bump is sufficient. The label cannot approve an insufficient version bump.

Enable publish on merge

Open the Target in the Console, enable Publish after merge, and save, or set publish_on_merge: true on its repository Delivery. The next Draft adds the publishing manifest and support files:

.github/workflows/typeship-release.yml
.github/workflows/typeship-republish.yml
.typeship/targets/<target-id>.json
.typeship/README.md

One root workflow serves every Target in a monorepo. The repository owns the shared workflows. Typeship installs missing workflows and proposes release-note reader upgrades through the Draft while preserving other team changes.

Files Typeship writes

Typeship proposes generated package files in the Target's configured directory. It also writes these support files in the Draft. For a root Target, <target-directory>/ is empty.

PathPurpose
<target-directory>/.typeship/surface.jsonAccepted API and package baseline for later Drafts.
<target-directory>/.typeship/.gitattributesMarks the surface baseline as generated in GitHub reviews.
<target-directory>/CHANGELOG.mdCustomer history and one Typeship-owned entry for the pending release.
.typeship/README.mdFile inventory and release flow; installed when publishing is enabled.
.typeship/run-package-checks.mjsRuns the configured Target checks.
.typeship/targets/<target-id>-checks.jsonCheck commands and package directory for one Target.
.typeship/targets/<target-id>-surface.mjsChecks the Target's generated public surface.
.typeship/targets/<target-id>.jsonAccepted source identity, version, release notes, tag, and destinations; present when publishing is enabled.
.typeship/conflicts/<target-id>.jsonTemporary conflict details for Draft review; removed after resolution.
.github/workflows/typeship-<target-id>-checks.ymlRuns the package checks on Draft updates.
.github/workflows/typeship-release.ymlPublishes accepted releases; present when publishing is enabled.
.github/workflows/typeship-republish.ymlRetries exact accepted releases; present when publishing is enabled.

GitHub requires workflow files in .github/workflows/. Typeship installs missing workflows, and your repository owns later changes. The package files in the configured Target directory are what the release publishes. Support files contain release and check metadata, with no private Organization data. A Draft removes the old root VERSIONING.md when it still matches Typeship's installed copy; review a customized copy before removing it yourself.

The Draft adds one plain Markdown changelog entry for the pending release and preserves customer text around it. You can edit that entry. Typeship keeps your text when regenerating, updates its version heading if needed, and copies the final entry into the accepted manifest's release_notes for the GitHub Release. If you customized the old changelog reader, update it to read release_notes from .typeship/targets/<target-id>.json at the accepted commit before publishing. Typeship warns when a workflow still references CHANGELOG.md.

The per-Target manifest binds the package directory, version, Generation, Spec Revision, and release tag. Merging the checked Draft with package changes creates a Release, advances the latest release, and dispatches the committed workflow once with that exact Target, version, and accepted commit. Publishing starts after the exact Draft is accepted, not when its pull request is opened or updated.

Typeship never receives your npm, PyPI, or registry credentials. Prefer trusted publishing with GitHub OIDC:

  • npm: configure typeship-release.yml as a trusted publisher for the package
  • PyPI: configure the repository, workflow filename, and typeship-release environment as a trusted publisher
  • MCP Registry: mcp-publisher login github-oidc needs no dedicated registry secret for io.github.* names
  • GitHub: a Target at the repository root gets vX.Y.Z. A Target in a directory gets <component>-vX.Y.Z, where the component is its package name without an npm scope, or else the last segment of its module path or directory (sdk-v1.4.0). If a later Target's component matches an earlier one in the same repository, the later Target adds a short suffix. Adding a Target never renames an existing Target's tags.
  • Go: the workflow also creates the directory-aware <target-directory>/vX.Y.Z module tag that go get resolves from Git. A root Go module uses its single vX.Y.Z tag for both GitHub and Go.

Protect the typeship-release GitHub environment and the default branch. Keep package tests on the Draft as required checks so a merged release already passed them.

Existing repository tags

Previously released versions keep their typeship/<target-id>/vX.Y.Z tags and GitHub Releases. Typeship does not rename or delete them. The next accepted release uses the new tag format in its committed Target manifest. An installed workflow with the known Typeship tag and release-note steps is updated through the Draft; review that change before merging. If you customized those steps, update the workflow to read .typeship/targets/<target-id>.json using the dispatched Target ID and use its canonical_tag for the exact release. A manual republish can still find an older tag by checking the committed manifest at each matching version tag.

Understand failure and retry

A merge is permanent even when a registry is down. The Target retains that version as the latest release while each destination's Publication reports queued, running, completed, or failed. The Console shows the same statuses, and GET /releases/{release_id} returns every Publication with the release.

Open the Target to see the failing destination and GitHub run. Fix repository configuration, then select Retry publishing or call POST /releases/{release_id}/retry with an idempotency key. The call returns 202 with the release; get the release until each retried Publication reaches completed or failed. Retry repeats every queued or failed destination of the release together and never repeats a completed one. Retry dispatches .github/workflows/typeship-republish.yml with the accepted Target, version, and commit; it never looks up “latest” and does not generate a new version. If the first publishing result never arrives, its status stays queued and the same retry action is available.

Imported releases record packages published before adoption. Typeship did not build them, so they cannot be republished through Typeship.

A retry accepts an existing registry version only when its artifact matches the accepted release. If the same version contains different bytes, publishing fails. Choose a new version and regenerate; an existing package version cannot be overwritten.

Package identities

Set registry identity before the first release:

TargetIdentity
CLIExecutable archives in the repository’s releases
MCPconfig.mcp.registry_name plus its npm package
TypeScript SDKrepository Delivery package_name on npm
Python SDKrepository Delivery package_name on PyPI
Go SDKdestination repository and optional module_path

For existing package names and release history, follow Adopt an existing package. For several Targets in one repository, follow Publish from a monorepo.

Manual publishing

Leave publish on merge disabled when your existing release system owns the last mile. Merging a Draft with package changes still records a Release and advances the latest release; a merge confined to tests or checks only changes the code Typeship carries into later Drafts. Your workflow can publish the generated package normally; Typeship will show it as repository-only because no registry report was requested.

Private packages

A private GitHub repository does not make a package published to npm or PyPI private. The generated publishing workflow does not configure private registries or package access for you.

For internal distribution, keep Publish after merge disabled (publish_on_merge: false, the default) and configure your own release workflow for your private registry or distribution method.

On this page