Skip to main content

Documentation Index

Fetch the complete documentation index at: https://qawolf-mktg-5566-document-qawolfci-sdk.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

@qawolf/ci-sdk provides a TypeScript SDK (CJS and ESM compatible) for interacting with the QA Wolf API from your CI pipeline.

Installation

npm install @qawolf/ci-sdk
Requires Node.js 18 or later. For older versions, pass a fetch polyfill to makeQaWolfSdk:
import { fetch } from "undici";

const sdk = makeQaWolfSdk({ apiKey: process.env.QAWOLF_API_KEY }, { fetch });

makeQaWolfSdk

The entry point for all SDK functions. Pass your QAWOLF_API_KEY to initialize.
import { makeQaWolfSdk } from "@qawolf/ci-sdk";

const {
  attemptNotifyDeploy,
  pollCiGreenlightStatus,
  makePollCiGreenlightStatusIterator,
  notifyTerminatedEphemeralEnvironment,
  generateSignedUrlForRunInputsExecutablesStorage,
} = makeQaWolfSdk({
  apiKey: process.env.QAWOLF_API_KEY,
});
SDK functions do not throw. They return a result object with an outcome field. Always inspect the outcome to determine whether your CI step should pass or fail.

attemptNotifyDeploy

Notifies QA Wolf of a successful deployment to trigger a test run. See webhooks/deploy_success for the underlying endpoint.
import { type DeployConfig, makeQaWolfSdk } from "@qawolf/ci-sdk";

const { attemptNotifyDeploy } = makeQaWolfSdk({
  apiKey: process.env.QAWOLF_API_KEY,
});

const deployConfig: DeployConfig = {
  branch: undefined,
  sha: undefined,
  deploymentType: "staging",
  deploymentUrl: undefined,
  hostingService: undefined,
  commitUrl: undefined,
  deduplicationKey: undefined,
  variables: undefined,
};

const result = await attemptNotifyDeploy(deployConfig);
if (result.outcome !== "success") {
  process.exit(1);
}

const runId = result.runId;
// Store runId as a CI job output to pass to pollCiGreenlightStatus.

DeployConfig fields

FieldTypeDescription
branchstringGit branch name.
shastringGit commit SHA.
deploymentTypestringRequired if the target trigger matches on deployment type.
deploymentUrlstringOverrides the environment URL. Available in tests as process.env.URL.
hostingServicestring"GitHub" or "GitLab". Defaults to "GitHub".
commitUrlstringPass with sha if no hosting service repo is configured.
pullRequestNumbernumberGitHub PR number, for PR testing.
mergeRequestNumbernumberGitLab MR number, for MR testing.
repository.namestringRepository name.
repository.ownerstringRepository owner or organization (GitHub).
repository.namespacestringRepository namespace or group (GitLab).
ephemeralEnvironmentbooleanPass with deploymentUrl for ephemeral environments without a code-hosting integration.
deduplicationKeystringCustom key controlling run cancellation behavior.
variablesobjectKey/value pairs that override environment variables for triggered runs.

Result fields

FieldDescription
outcome"success", "failed", or "aborted".
runIdID of the triggered run. Pass to pollCiGreenlightStatus.
failReasonPresent when outcome is "failed". Reason the notification failed.
abortReasonPresent when outcome is "aborted". Reason the notification was aborted.
httpStatusHTTP status code if the notification failed or was aborted due to a network error.
A run is only created if there is a matching trigger in your QA Wolf configuration. outcome: "success" means the notification was accepted, not that a run was created.

pollCiGreenlightStatus

Polls the CI greenlight endpoint until the run completes and returns whether it is safe to release.
import { makeQaWolfSdk } from "@qawolf/ci-sdk";

const { pollCiGreenlightStatus } = makeQaWolfSdk({
  apiKey: process.env.QAWOLF_API_KEY,
});

const { outcome } = await pollCiGreenlightStatus({
  runId,
  onRunStageChanged: (current, previous) => {
    console.log(current, previous);
  },
  abortOnSuperseded: false,
});

if (outcome !== "success") {
  process.exit(1);
}

Options

FieldTypeDescription
runIdstringThe run ID returned by attemptNotifyDeploy.
onRunStageChangedfunctionOptional callback fired when the run stage changes.
abortOnSupersededbooleanDefaults to false. When true, polling aborts if the run is superseded.
pollTimeoutnumberTimeout in milliseconds. Defaults to two hours.

Result fields

FieldDescription
outcome"success", "failed", or "aborted". Only "failed" indicates bugs were found.
abortReasonPresent when outcome is "aborted". Reason polling stopped.
httpStatusHTTP status code if polling was aborted due to a network error.

Advanced: makePollCiGreenlightStatusIterator

An async generator for fine-grained control over the polling lifecycle. Use this when you need custom early-exit logic — for example, proceeding after a time limit or when bug counts are within an acceptable threshold.
import { makeQaWolfSdk } from "@qawolf/ci-sdk";

const { makePollCiGreenlightStatusIterator } = makeQaWolfSdk({
  apiKey: process.env.QAWOLF_API_KEY,
});

let underReviewStartTime: number | null = null;

const iterator = makePollCiGreenlightStatusIterator({ runId: "your-run-id" });

for await (const iteration of iterator) {
  if (iteration.isAborted) {
    console.error(`Poll aborted: ${iteration.abortReason}`);
    process.exit(1);
  }

  const { status, stageChanged } = iteration;

  switch (status.runStage) {
    case "initializing":
      break;
    case "underReview":
      if (stageChanged) underReviewStartTime = Date.now();
      const timeInReview = underReviewStartTime ? Date.now() - underReviewStartTime : 0;
      if (timeInReview > 10 * 60 * 1000) {
        if (status.blockingBugsCount > 5) process.exit(1);
      }
      break;
    case "completed":
      if (!status.greenlight) process.exit(1);
      return;
    case "canceled":
      process.exit(1);
    default:
      status.runStage satisfies never;
      throw new Error(`Unexpected run stage: ${status.runStage}`);
  }
}
Each iteration yields either a status update or an abort notification:
FieldPresent whenDescription
isAbortedalwaysfalse for status updates, true for abort notifications.
statusisAborted: falseCurrent CiGreenlightStatus from the API.
previousStatusisAborted: falseStatus from the previous iteration. undefined on first iteration.
stageChangedisAborted: falsetrue if the run stage changed from the previous iteration.
elapsedMsalwaysMilliseconds elapsed since polling started.
abortReasonisAborted: trueWhy polling was aborted.
httpStatusisAborted: trueHTTP status code if applicable.
Handle all run stages in your switch statement. The default: status.runStage satisfies never pattern provides compile-time safety — TypeScript will error if a new stage is added and your code doesn’t handle it.

notifyTerminatedEphemeralEnvironment

Notifies QA Wolf that an ephemeral environment has been terminated. Stops all runs targeting the environment and triggers flow promotion. See webhooks/environment_terminated for the underlying endpoint.
import {
  type NotifyTerminatedEphemeralEnvironmentInput,
  makeQaWolfSdk,
} from "@qawolf/ci-sdk";

const { notifyTerminatedEphemeralEnvironment } = makeQaWolfSdk({
  apiKey: process.env.QAWOLF_API_KEY,
});

const terminateConfig: NotifyTerminatedEphemeralEnvironmentInput = {
  deploymentUrl: "https://preview-123.example.com",
};

const result = await notifyTerminatedEphemeralEnvironment(terminateConfig);
if (result.outcome !== "success") {
  process.exit(1);
}

const environmentId = result.environmentId;

Input fields

Pass one of the following to identify the environment:
FieldTypeDescription
environmentIdstringID of the ephemeral environment.
environmentAliasstringAlias of the ephemeral environment.
deploymentUrlstringPreview URL used when the environment was created.

Result fields

FieldDescription
outcome"success", "failed", or "aborted".
environmentIdID of the terminated environment.

generateSignedUrlForRunInputsExecutablesStorage

Generates a signed URL for uploading a run input executable (APK, AAB, IPA, ZIP, CSV, PDF) to QA Wolf. See v0/run-inputs-executables-signed-urls for the underlying endpoint.
import { makeQaWolfSdk } from "@qawolf/ci-sdk";
import fs from "fs/promises";

const { generateSignedUrlForRunInputsExecutablesStorage } = makeQaWolfSdk({
  apiKey: process.env.QAWOLF_API_KEY,
});

const signedUrlResponse = await generateSignedUrlForRunInputsExecutablesStorage({
  destinationFilePath: "app-staging",
});

if (
  !signedUrlResponse?.success ||
  !signedUrlResponse.uploadUrl ||
  !signedUrlResponse.playgroundFileLocation
) {
  throw new Error("No upload URL received from QA Wolf");
}

const fileBuffer = await fs.readFile("./path/to/build.apk");

await fetch(signedUrlResponse.uploadUrl, {
  method: "PUT",
  body: fileBuffer,
  headers: { "Content-Type": "application/octet-stream" },
});

Input fields

FieldTypeDescription
destinationFilePathstringFilename and extension, optionally including directories. Reach out to your QA Wolf representative for the correct value.

Response fields

FieldDescription
successtrue if the signed URL was generated successfully.
uploadUrlPre-signed URL for uploading the file via PUT.
playgroundFileLocationFile path without team ID. Use as a variables value in attemptNotifyDeploy.

Versioning

This package follows SemVer. Notes:
  • Use the ^ range operator — patch and minor updates will not introduce breaking changes.
  • Major version bumps indicate a breaking API change. QA Wolf will give advance notice.
  • Only top-level exports are covered by SemVer.
  • New fields in API response types are not considered breaking changes.
Last modified on May 28, 2026