Skip to main content
@qawolf/ci-sdk is a TypeScript package for notifying QA Wolf of a deploy from your CI pipeline and gating the pipeline on the result. It calls the same public API the Webhook page documents, with typed inputs taken from the published API contract. SDK functions do not throw. Each returns a result object with an outcome field, and your pipeline branches on it to decide whether the step passes or fails. The guide for wiring it into a pipeline is Node.js SDK. This page is the function-by-function reference. This page documents 3.6.0. deployment.reportStatus needs 3.1.0 or later, so a pipeline on an earlier version has only the legacy functions and upgrades before wiring one up. Use 3.6.0 or later for deployment.waitForVerdict: earlier versions fail on every call (before 3.4.1), fail while a run is still executing (before 3.5.1), or keep waiting until timeout on a run that needs a person (before 3.6.0).

Installation

Requires Node.js 24.14.1 or later, below 25. The package is published as ESM only: import it with import, or from CommonJS with await import("@qawolf/ci-sdk"). require("@qawolf/ci-sdk") does not resolve.

makeQaWolfSdk

The entry point for every function. Pass your API key to initialize.

Options

A second argument injects dependencies. Each is optional: Returns an object with these functions:
  • deployment.reportStatus — reports a deployment. A deployment’s first success report is what evaluates triggers and starts a run.
  • deployment.waitForVerdict — blocks until QA Wolf can say whether the deployment’s tests passed.
  • environment.promote and environment.terminate — close a preview environment, keeping or discarding its flow changes.
  • promotion.get — reads how a promotion settled.
  • uploadBuild — uploads an Android or iOS app build.
  • generateSignedUrlForRunInputsExecutablesStorage and generateSignedUrlForTempTeamStorage — signed upload URLs for files a run needs.
  • notifyTerminatedEphemeralEnvironment — deprecated; performs a promote through the legacy webhook.
  • attemptNotifyDeploy, pollCiGreenlightStatus and makePollCiGreenlightStatusIterator — the legacy functions. They report through the deploy_success webhook, which triggers do not see.
detectProviderDeploymentId is a standalone export rather than a method; see below.

API keys and workspaceId

Every call acts on exactly one workspace.
  • A workspace API key belongs to one workspace. deployment.reportStatus still requires workspaceId, and it has to name that workspace; naming another fails with unauthorized.
  • An organization API key or a user API key can reach several workspaces, so workspaceId picks one.
Find the id under Workspace Settings → API Keys. The legacy functions and the signed URL functions take workspaceId as an optional field; see each function for what happens when it is omitted.

deployment.reportStatus

Reports a deployment to QA Wolf through the public deployment.reportStatus API. The parameters are the API’s own input, so Webhook and Deployments describe how each field is read. Example:

Parameters

The input is validated strictly: a field the contract does not know, or environment carrying both id and name, is an invalid-request. ReportDeploymentParams is the exported type.
Send environment.ephemeral: true for every preview deploy. A report that omits it is accepted and creates a permanent environment, one per pull request, that is never torn down.

Result

Branch on outcome: The response carries the deployment only, never the resulting runs. deployment.waitForVerdict is how a pipeline learns what the runs decided.
outcome: "reported" means QA Wolf recorded the deployment, not that a trigger matched it. Whether a run starts is decided afterwards, by your triggers.

detectProviderDeploymentId

Composes a providerDeploymentId from the variables your CI system already sets. The id is distinct for every job definition and every re-run, so a retried job or a second job deploying alongside the first gets its own deployment rather than reporting into one whose first success already evaluated triggers. Example:

Options

Result

The helper is optional. providerDeploymentId is always required on the report, and any value that changes on every re-run and differs between concurrent deploys works.

deployment.waitForVerdict

Blocks until QA Wolf can say whether a deployment’s tests passed. Give it the deployment.id the report returned; resolving that to the runs the triggers created, and those runs to a status, happens inside. On GitHub Actions, qawolf/wait-for-verdict-action runs this wait as a workflow step. See GitHub Actions. Example:
Set timeout below your CI job’s own time limit. When the job’s limit fires first, the job is killed with no run link and no explanation. When timeout fires first, the result is timed-out with the stage, the elapsed time and every run URL the wait knew about.

Options

Every field except deploymentId is optional. Durations are in milliseconds. The stages onProgress receives, in order:
  • waiting-for-evaluation — QA Wolf has not yet evaluated the deployment’s triggers.
  • waiting-for-run — triggers matched and at least one is still creating a run. Carries matchedTriggerCount.
  • waiting-for-verdict — every run is known and at least one is still executing. Carries runs.

Result

Branch on outcome: not-tested is a separate answer from passed. A deployment no trigger matched was not tested, which is a different fact from tests passing, and your pipeline decides what it is worth. reason is "no-trigger-matched" when every trigger was evaluated and none matched, or "no-run-created" when a trigger matched and something stopped it from creating a run; each entry in triggers then carries triggerName, a message for the log and, for no-run-created, a stable reason code to branch on. needs-review means a person must answer before QA Wolf can decide: QA Wolf does not call a run passed or failed while a failure still waits for a diagnosis. The wait returns as soon as only a person can move the run, either because QA Wolf asked a question about each failure without a diagnosis or because the workspace does not investigate runs. Each run’s runUrl takes your build log to the run page, where someone answers the questions. After that the run settles to passed or failed. The example above exits 1 on it. superseded is what a cancel-in-progress pipeline looks like from the older job: the newer deployment’s job carries the verdict. Reserve a distinct exit code for it when your CI should treat the job as skipped rather than failed. Each entry in runs: DeploymentVerdict, VerdictRun, VerdictStage, TriggerNote and NotTestedReason are the exported types. The same shape is published as deploymentVerdictSchema in @qawolf/api-contracts.

environment.promote and environment.terminate

Close a preview environment that deployment.reportStatus created, once its pull request is done. The two are not interchangeable:
  • environment.promote — the work was accepted, typically on merge. The environment’s flow changes are merged into its base environment, and the environment is terminated once they land.
  • environment.terminate — the work was abandoned, typically on a close without merge. The flow changes are discarded and the environment’s runs are canceled.
Terminating an accepted preview throws away the flow work done for that pull request. Promoting an abandoned one merges flows nobody kept into the base environment that every later preview copies from. With a connected GitHub or GitLab integration, QA Wolf hears about the pull request closing from the code host, and neither call is needed. Example:

Parameters

Result

environment.promote answers "requested" with promotion, in pending, because the merge runs in the background; read how it settled with promotion.get, below. environment.terminate answers "terminated" with environment, whose terminatedAt is set. Both share these refusals: Calling environment.promote again while a promotion is open returns that same promotion rather than opening a second one. An environment whose promotion is still being applied cannot be terminated until it settles; one whose promotion is conflicted can.

promotion.get

Reads how a promotion that environment.promote requested settled. Example:
promotion.status is one of:
  • pending and resolving — not settled yet. Poll again.
  • promoted — the flow changes landed in the base environment. Carries promotedCommitHash.
  • no-changes — there was nothing to merge.
  • conflicted — the flow changes clash with the base environment. conflict.paths names the files. A person resolves it in QA Wolf, and the environment stays until they do.
  • abandoned — the environment was terminated before the promotion settled.
conflict is present while the status is conflicted or resolving, and settledAt once it is promoted, no-changes or abandoned. Refusals are the same as for environment.promote, with not-found meaning no promotion matches promotionId. A promotion usually settles within a minute or two. The SDK has no wait helper for it, so a pipeline that needs the settled status polls promotion.get while it reads pending or resolving.

uploadBuild

Uploads an Android .apk or .aab, or an iOS .ipa, of up to 1 GB, and registers it as a QA Wolf build. QA Wolf reads the app id, version and build number from the file. The file is streamed from disk, so a large build is not held in memory. Example:

Parameters

Result

"uploaded" carries build with id, appId, appName, platform, version, buildNumber, fileName, sizeBytes and url. Otherwise: A failed call creates no build. Each call uploads the file as a new build, so retrying is calling uploadBuild again. To start runs that install the build, pass result.build.id to deployment.reportStatus as builds. See Upload builds.

notifyTerminatedEphemeralEnvironment

Deprecated since 3.4.0. Despite its name, this function performs a promote: the legacy environment_terminated webhook merges the environment’s flow changes into its base and then terminates it. Use environment.promote, above, for accepted work and environment.terminate to discard abandoned work, which this function has never done. It keeps working, prints no warning and has no removal date.
Tells QA Wolf that an ephemeral environment has been terminated. QA Wolf stops the runs targeting it and promotes any flow changes to the base environment. See webhooks/environment_terminated for the underlying endpoint. With a connected GitHub or GitLab integration, QA Wolf hears about the pull request closing from the code host, and this call is redundant. Example:

Input fields

Pass exactly one of the following to identify the environment: environmentAlias and deploymentUrl also take workspaceId. It is optional; an organization or user key that omits it acts on the organization’s first-created workspace, and the result carries a warning naming it. NotifyTerminatedEphemeralEnvironmentInput is the exported type.

Result fields

generateSignedUrlForRunInputsExecutablesStorage

Deprecated for Android and iOS builds. Use uploadBuild instead, so every run records which build it tested. It keeps working, prints no warning, and is still the way to upload Windows and Linux app files. A zipped app that is not an .ipa also stays on this path for now.
Generates a signed URL for uploading a run input executable (APK, AAB, DEB, IPA, ZIP, CSV, PDF) to QA Wolf. See v0/run-inputs-executables-signed-urls for the underlying endpoint, and Legacy build uploads for the pipeline it belongs to. To upload an Android or iOS build, use uploadBuild. Example:

Input fields

Response fields

A failed request from this function reports abortReason: "client-network-error" and httpStatus: 0 whatever the real status was. The SDK logs the real status on the line above, so read the log rather than branching on abortReason. A 400 from an organization or user key most often means workspaceId was not sent.
generateSignedUrlForTempTeamStorage takes the same input and returns the same fields, for files that are not executables.

Legacy functions

attemptNotifyDeploy and pollCiGreenlightStatus report through the legacy deploy_success webhook, which feeds legacy triggers only. A trigger never sees a deployment they send. They were deprecated in 3.1.0 and are still exported in 3.6.0: legacy describes the path they report through, not an old version of the package. They keep working unchanged, print no warning, and have no removal date. A new integration uses deployment.reportStatus and deployment.waitForVerdict.
Moving a pipeline off them: deploymentType becomes environment.name; branch and sha move into metadata; providerDeploymentId is new and required, and subsumes deduplicationKey; hostingService disappears, since the code host comes from your integration; variables becomes environmentVariables. There is no skipped outcome, because whether a trigger matched is not known at report time; deployment.waitForVerdict answers not-tested instead.

attemptNotifyDeploy

Notifies QA Wolf of a successful deployment through webhooks/deploy_success. Example:

DeployConfig fields

DeployConfig is a union of GitHubDeployConfig, GitLabDeployConfig and EphemeralDeployConfig. Every field is declared on the type, so pass undefined for the ones you do not use.

Result fields

pollCiGreenlightStatus

Polls v0/ci-greenlight for a run id until the run completes. The run id comes from attemptNotifyDeploy, so this function only works for pipelines on the legacy path. Example:

Options

Result fields

makePollCiGreenlightStatusIterator

An async generator over the same poll, for a pipeline that needs to stop early on its own criteria. Takes the same options as pollCiGreenlightStatus except onRunStageChanged. Example:
Each iteration is either a status update or an abort:
Handle every run stage in the switch. The default: status.runStage satisfies never arm makes TypeScript error when a stage is added and the code does not handle it, instead of silently passing the job.

Versioning

The package follows SemVer.
  • Depend on it with the ^ range operator. Patch and minor releases do not introduce breaking changes.
  • A major version bump marks a breaking change, announced in advance.
  • Only top-level exports are covered. New fields in response types, and changes to log output, are not breaking changes.
Minimum versions for the functions on this page: The changelog is in the package’s README on npm.
Last modified on November 9, 2026