This guide covers the complete process for updating, generating, testing, and publishing Outpost SDKs.
Outpost SDKs are automatically generated from the OpenAPI specification using Speakeasy. The SDK generation, testing, and publishing process is managed through GitHub Actions workflows that run automatically on version tag pushes, with manual triggering also available per SDK.
Key Point: SDK generation runs automatically when you push a version tag (e.g. v0.13.2). The release process is the primary guide. You can also trigger generation manually per SDK (see below). SDK versions are determined by Speakeasy detection (breaking vs non-breaking); for Outpost v1.0.0 the workflow sets all SDKs to 1.0.0.
- Git
- GitHub account with write access to the Outpost repository
- For local testing:
- Go SDK: Go 1.21+
- Python SDK: Python 3.9+, pip
- TypeScript SDK: Node.js 18+, npm
The following secrets must be configured in the repository (for maintainers):
SPEAKEASY_API_KEY: For SDK generationNPM_TOKEN: For publishing TypeScript SDK to npmPYPI_API_TOKEN: For publishing Python SDK to PyPI- GitHub token (automatic): For publishing Go SDK
Outpost maintains three official SDKs, each in its own directory within the sdks/ folder:
- Location:
sdks/outpost-go/ - Package:
github.com/hookdeck/outpost/sdks/outpost-go - Published: Automatically via Git tags
- Generation Workflow:
sdk_generation_outpost_go.yaml - Publish Workflow:
sdk_publish_outpost_go.yaml
- Location:
sdks/outpost-python/ - Package:
outpost-pythonon PyPI - Published: Automatically to PyPI on merge
- Generation Workflow:
sdk_generation_outpost_python.yaml - Publish Workflow:
sdk_publish_outpost_python.yaml
- Location:
sdks/outpost-typescript/ - Package:
@hookdeck/outposton npm (also@hookdeck/outpost-sdk) - Published: Automatically to npm on merge
- Generation Workflow:
sdk_generation_outpost_ts.yaml - Publish Workflow:
sdk_publish_outpost_ts.yaml
Each SDK is maintained as a sub-project within this repository and generated from the shared OpenAPI specification.
When Speakeasy runs SDK generation, it updates the following:
- Root (shared):
.speakeasy/workflow.lock— updated when generation runs (observed when running multiple targets). This is a shared file across all three SDKs. - Per-SDK: Each target updates only its own directory, e.g.:
sdks/outpost-go/.speakeasy/gen.lock,sdks/outpost-go/.speakeasy/gen.yaml, and all generated output undersdks/outpost-go/sdks/outpost-python/.speakeasy/gen.lock,sdks/outpost-python/.speakeasy/gen.yaml, and output undersdks/outpost-python/sdks/outpost-typescript/.speakeasy/gen.lock,sdks/outpost-typescript/.speakeasy/gen.yaml, and output undersdks/outpost-typescript/
Because the root .speakeasy/workflow.lock is written by generation, the three SDK generations must run sequentially (not in parallel) to avoid overwriting or conflicting on that file. The tag-triggered workflow runs Go → Python → TypeScript in order. When using the Speakeasy action with mode: pr, each run opens its own PR, so you will see three SDK PRs per release tag; they can be merged in any order after review.
All SDK generation (manual per-language and tag-triggered) goes through the same reusable workflow sdk-generate-one.yml, which calls Speakeasy’s action once per target. The manual workflows (Generate OUTPOST-GO, etc.) and the release-tag workflow all use it, so behaviour and version handling stay in one place.
The full release process (creating the tag, what runs on tag push, merging SDK PRs, creating the GitHub Release) is described in the Release process guide. When you push a version tag (e.g. v0.13.2), sdk-generate-on-release-dispatch.yml dispatches sdk-generate-on-release.yml using that tag as the workflow ref, so generation runs against the tagged commit. speakeasy-workflow-executor-v15.yml is a thin vendor of Speakeasy’s executor (see file header) so pull requests still target main. The run opens three pull requests (one for each SDK). Review and merge them in any order; see Step 3: Review Generated SDK Pull Requests and Step 4: Test the Generated SDKs below for review and testing.
All SDK changes begin with updating the OpenAPI specification:
-
Edit the specification:
# The OpenAPI spec is located at: docs/apis/openapi.yaml -
Make your changes:
- Add new endpoints, modify existing ones, or update schemas
- Follow OpenAPI 3.0 standards
- Ensure all changes are backward compatible when possible
- Add descriptions and examples for better SDK documentation
-
Validate the specification:
# Use Speakeasy CLI to lint (if installed) speakeasy lint openapi -s docs/apis/openapi.yaml -
Commit and push:
git add docs/apis/openapi.yaml git commit -m "docs: update OpenAPI spec - <description>" git push
The complete workflow from OpenAPI spec update to published SDKs:
See Updating the OpenAPI Specification above.
SDK generation is manually triggered through GitHub Actions:
-
Navigate to Actions:
- Go to the Actions tab in GitHub
-
Select the workflow:
Generate OUTPOST-GOGenerate OUTPOST-PYTHONGenerate OUTPOST-TS
-
Run the workflow:
- Click "Run workflow"
- Select the branch (typically
main) - Optional inputs:
force: Force generation even if no OpenAPI changes detected (default: false)set_version: Set a specific SDK version (e.g.,1.2.3orv1.2.3). Both formats are supported. Leave empty to keep current version. If autogenerating with each release, versions should match the Outpost release version. Note: Existing releases use thevprefix format.
- Click "Run workflow"
-
Repeat for all three SDKs that need updates
Version Bumping: The set_version input allows you to manually specify the SDK version. If left empty, the SDK will regenerate with the current version. Follow semantic versioning when setting versions.
Each workflow creates a pull request in the respective SDK repository:
-
Wait for workflows to complete (~5-10 minutes per SDK)
-
Review the PRs:
- Go: Check
sdks/outpost-gofor new PR - Python: Check
sdks/outpost-pythonfor new PR - TypeScript: Check
sdks/outpost-typescriptfor new PR
- Go: Check
-
Verify changes:
- Review the diff to ensure changes match your OpenAPI updates
- Check for any unexpected modifications
- Ensure documentation comments are properly generated
- Verify examples compile/run
- Review the
.speakeasy/gen.lockfile changes
⚠️ CRITICAL: Before testing, you MUST regenerate the TypeScript SDK. See Testing with spec-sdk-tests below.
Testing is mandatory before merging:
-
Test with spec-sdk-tests (required for TypeScript):
- See the dedicated Testing with spec-sdk-tests section below
- This step catches integration issues early
-
Run SDK-specific tests:
# In each SDK directory cd sdks/outpost-go && go test ./... cd sdks/outpost-python && pytest cd sdks/outpost-typescript && npm test
-
Manual integration testing (recommended):
- Test key functionality with a running Outpost instance
- Verify authentication works
- Test new/modified endpoints
- Check error handling
Once testing is complete and PRs are approved:
-
Merge the PR in each SDK repository
-
Automatic publishing triggers:
- Go: New Git tag is created automatically
- Python: Published to PyPI automatically
- TypeScript: Published to npm automatically
-
Verify publication:
Outpost SDKs follow Semantic Versioning (SemVer):
Note: During the BETA phase (0.x.x versions), we bump the minor version for all changes, including breaking changes. This allows for rapid iteration and API evolution without committing to long-term stability.
- Minor version (0.x.0): All changes during BETA
- Breaking changes (normally would be major version)
- New features and endpoints
- Bug fixes and improvements
- API modifications
Once Outpost reaches stable 1.0.0, we'll follow strict SemVer:
-
Major version (x.0.0): Breaking changes to the API
- Remove endpoints or parameters
- Change required fields
- Modify response structures in incompatible ways
-
Minor version (0.x.0): Backward-compatible additions
- Add new endpoints
- Add optional parameters
- Add new response fields
-
Patch version (0.0.x): Backward-compatible fixes
- Fix bugs in SDK code
- Update documentation
- Improve error messages
Important: SDK versions should generally stay in sync with each other, but can diverge if language-specific fixes are needed. When updating the OpenAPI spec that affects all SDKs, bump all SDK versions consistently.
The spec-sdk-tests/ directory contains integration tests that verify the TypeScript SDK works correctly with a running Outpost instance.
⚠️ WARNING: LOCAL FILE DEPENDENCY RACE CONDITIONThe
spec-sdk-testsproject has a local file dependency on the TypeScript SDK, which creates a critical testing race condition:"dependencies": { "@hookdeck/outpost-sdk": "file:../../../sdks/outpost-typescript" }The Problem:
- The test suite uses the TypeScript SDK from your local file system
- If you regenerate the SDK and test immediately, npm may use cached/old build artifacts
- This means you might be testing the old SDK version, not the new one
- Tests can pass with stale code, hiding actual issues
Why This Matters:
- ❌ False confidence: Tests pass but the new SDK has bugs
- ❌ Wasted time: Debugging issues that don't exist in the version you're testing
- ❌ Publishing broken SDKs: The actual published SDK differs from what you tested
- ❌ Downstream breakage: Users get a broken SDK that you never actually tested
To avoid the race condition, always follow this exact sequence:
-
Generate/update the TypeScript SDK (via GitHub Actions or locally)
-
Rebuild the TypeScript SDK:
cd sdks/outpost-typescript npm install npm run build cd ../..
-
Clean and reinstall test dependencies:
cd spec-sdk-tests rm -rf node_modules package-lock.json npm install -
Start Outpost locally (in a separate terminal):
make up # Verify it's running curl http://localhost:3333/api/v1/healthzFor more details, see Getting Started Guide.
-
Run the tests:
npm test -
Verify test output:
- All tests should pass
- Check for any warnings or deprecation notices
- Verify the SDK version being tested matches your expectations
- Review any console output for unexpected behavior
The spec-sdk-tests verify:
- SDK can authenticate with Outpost
- API endpoints return expected responses
- Request/response serialization works correctly
- Error handling behaves as expected
- New functionality from OpenAPI changes works
- Schema validation and type safety
- Integration with real Outpost instance
Always run these tests:
- ✅ After generating a new TypeScript SDK
- ✅ Before merging SDK generation PRs
- ✅ After making breaking changes to the OpenAPI spec
- ✅ When debugging SDK issues reported by users
Optional but recommended:
- After updating dependencies in the TypeScript SDK
- When testing integration with new Outpost features
- Before major releases
Publishing is fully automated after merging:
- Trigger: Merge to
mainbranch insdks/outpost-go - Workflow:
.github/workflows/sdk_publish_outpost_go.yaml - Process:
- Runs tests
- Creates a Git tag with the version from
.speakeasy/gen.lock - Pushes tag to repository
- Go users can
go getthe new version
- Verification: Check GitHub releases
- Trigger: Merge to
mainbranch insdks/outpost-python - Workflow:
.github/workflows/sdk_publish_outpost_python.yaml - Process:
- Runs tests
- Builds distribution packages
- Publishes to PyPI using
PYPI_API_TOKEN - Available via
pip install outpost-python
- Verification: Check PyPI
- Trigger: Merge to
mainbranch insdks/outpost-typescript - Workflow:
.github/workflows/sdk_publish_outpost_ts.yaml - Process:
- Runs tests
- Builds the package
- Publishes to npm using
NPM_TOKEN - Available via
npm install @hookdeck/outpost
- Verification: Check npm
After merging, monitor the publish workflows:
- Go to the Actions tab in the Outpost repository
- Watch the publish workflow execution for each SDK
- Verify successful completion
- Check package registries for the new version
If publication fails:
- Check the workflow logs for errors
- Verify secrets are correctly configured
- Ensure version numbers don't conflict with existing releases
- Look for path trigger issues (
.speakeasy/gen.lockchanges)
Note: This section is for advanced use cases. SDK building and testing is automatically handled in CI/CD when PRs are created. You typically only need this for debugging or local development of the SDKs themselves.
The GitHub Action runs the Speakeasy CLI with your .speakeasy/workflow.yaml. To use the same tooling and workflow locally (including the compile step):
- Install the Speakeasy CLI.
- Set
SPEAKEASY_API_KEY(same secret as in the repo). - From the repo root run:
This runs the same workflow as CI: generation into
speakeasy run -t outpost-python
sdks/outpost-python, then the compile step (e.g.poetry build). There is no other way to replicate the CI setup locally without pushing and triggering the workflow.
While the CI/CD pipeline handles SDK generation, you can build and test locally if needed:
cd sdks/outpost-go
go mod download
go build ./...
go test ./...cd sdks/outpost-python
pip install -e .
pip install pytest
pytestcd sdks/outpost-typescript
npm install
npm run build
npm testSymptom: Workflow fails during generation step
Solutions:
- Lint OpenAPI spec:
speakeasy lint openapi -s docs/apis/openapi.yaml - Check for syntax errors in
openapi.yaml - Ensure all
$refreferences are valid - Review workflow logs for specific Speakeasy errors
- Verify
SPEAKEASY_API_KEYsecret is configured - Check if the target exists in
.speakeasy/workflow.yaml
Symptom: PR is created but tests fail
Solutions:
- Review the test output in the workflow logs
- Check if OpenAPI changes introduced breaking changes
- Verify example code in OpenAPI spec is valid
- May need to update test fixtures in SDK repositories
- Ensure dependencies are correctly specified in generated code
Symptom: Tests fail or show unexpected behavior
Solutions:
- Did you rebuild the TypeScript SDK? (Most common issue)
cd sdks/outpost-typescript && npm run build
- Did you clean test dependencies?
cd spec-sdk-tests && rm -rf node_modules && npm install
- Is Outpost running locally? Check
http://localhost:3333/api/v1/healthz - Check for console warnings about version mismatches
- Review test logs for API errors or authentication issues
- Verify the file path in
package.jsonis correct:file:../../../sdks/outpost-typescript - Check if the TypeScript SDK build output exists in
sdks/outpost-typescript/dist
Symptom: Merge succeeded but package not published
Solutions:
- Check if publish workflow ran (Actions tab)
- Verify the publish trigger path:
.speakeasy/gen.lockshould have changed - Check secrets are configured:
SPEAKEASY_API_KEY,NPM_TOKEN,PYPI_API_TOKEN - Look for version conflicts (version already exists)
- Review publish workflow logs for specific errors
- For Go: Ensure Git tag was created successfully
- For npm/PyPI: Check authentication errors in logs
Symptom: Testing shows old SDK version or changes not reflected
Solutions:
- Rebuild the TypeScript SDK if testing with spec-sdk-tests
- Verify the PR actually contains your changes (review diff)
- Check that you're testing against the correct branch
- Clear npm/pip caches if testing locally:
npm cache clean --force pip cache purge
- Confirm
set_versionwas provided correctly in workflow trigger - Check
.speakeasy/gen.lockfor the actual version being used
Symptom: spec-sdk-tests shows import errors or missing types
Solutions:
- Ensure TypeScript SDK is built:
cd sdks/outpost-typescript && npm run build - Check build output exists:
ls sdks/outpost-typescript/dist - Reinstall test dependencies:
cd spec-sdk-tests && npm install - Check for TypeScript compilation errors in the SDK
- Verify
package.jsondependency path is correct:"file:../../../sdks/outpost-typescript" - Ensure SDK
package.jsonhas correct exports/main fields
Symptom: Publish workflow doesn't run after merge
Solutions:
- Check the workflow trigger paths in
.github/workflows/sdk_publish_*.yaml - Verify
.speakeasy/gen.lockfile was actually modified in the commit - Check workflow permissions (needs write access to contents, PRs, etc.)
- Look for branch protection rules that might block workflow runs
- Manually trigger with
workflow_dispatchif needed
- Update
docs/apis/openapi.yaml - Validate OpenAPI spec
- Commit and push changes
- Manually trigger SDK generation workflows (all 3 SDKs)
- Set version numbers via
set_versioninput (if bumping) - Wait for PR creation in each SDK repo (~5-10 min each)
- Review PRs for correctness
- Rebuild TypeScript SDK:
cd sdks/outpost-typescript && npm run build - Clean test deps:
cd spec-sdk-tests && rm -rf node_modules && npm install - Start Outpost locally
- Run spec-sdk-tests:
cd spec-sdk-tests && npm test - Run SDK-specific tests in each SDK repo
- Perform manual integration testing (optional but recommended)
- Approve and merge PRs
- Verify automatic publishing succeeds
- Test published packages from registries
# Lint OpenAPI spec
speakeasy lint openapi -s docs/apis/openapi.yaml
# Rebuild TypeScript SDK (CRITICAL before testing!)
cd sdks/outpost-typescript
npm install
npm run build
cd ../..
# Regenerate SDK (optional, requires Speakeasy CLI)
cd spec-sdk-tests
./scripts/regenerate-sdk.sh
# For Go: the script unsets GOMODCACHE/GOCACHE so Go uses your default module cache.
# If you run Speakeasy for Go directly and see "module found but does not contain package",
# unset GOMODCACHE and GOCACHE and retry (e.g. in Cursor the sandbox can set a cache path
# that breaks module resolution).
# Run spec-sdk-tests (RECOMMENDED - includes pre-flight checks)
cd spec-sdk-tests
rm -rf node_modules package-lock.json # Clean install to avoid cache
npm install
./scripts/run-tests.sh
# OR run tests directly (skips validation checks)
npm test
# Test individual SDKs
cd sdks/outpost-go && go test ./...
cd sdks/outpost-python && pytest
cd sdks/outpost-typescript && npm test
# Start Outpost locally for testing
make up
# Verify Outpost is running
curl http://localhost:3333/api/v1/healthz
# Clean caches if needed
npm cache clean --force
pip cache purge- OpenAPI Spec:
docs/apis/openapi.yaml - Generation Workflows:
.github/workflows/sdk_generation_outpost_go.yaml.github/workflows/sdk_generation_outpost_python.yaml.github/workflows/sdk_generation_outpost_ts.yaml
- Publish Workflows:
.github/workflows/sdk_publish_outpost_go.yaml.github/workflows/sdk_publish_outpost_python.yaml.github/workflows/sdk_publish_outpost_ts.yaml
- SDK Test Suite:
spec-sdk-tests/ - SDK Locations:
sdks/outpost-go/sdks/outpost-python/sdks/outpost-typescript/
- Check existing Issues for similar problems
- Review Speakeasy documentation
- Ask in GitHub Discussions
- Contact the maintainers team