> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qa.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# GitHub pull request testing

> Review a GitHub pull request on a preview URL, after merge, or against a mobile build.

Use this when the change is a GitHub pull request. To run a test plan you already have, use [GitHub regression testing](/regression-testing/github).

## Set it up

<CardGroup cols={2}>
  <Card title="GitHub App" icon="robot" href="#github-app">
    Automatic review on every pull request. Needs a preview URL from a
    deployment or a `@qa.tech` comment.
  </Card>

  <Card title="Change Review Action" icon="github" href="#change-review-action">
    Start the review from a workflow, after a deploy job or on a label. The App
    must still be installed.
  </Card>

  <Card title="After merge" icon="code-merge" href="#after-merge">
    No preview environment. Review the merged pull request against staging or
    production.
  </Card>

  <Card title="Post-merge agent" icon="robot" href="#post-merge-agent">
    After a reviewed pull request merges, promote tests from that review into
    the suite.
  </Card>

  <Card title="Mobile build" icon="mobile" href="#mobile-build">
    Upload the simulator or emulator build and pin the review to it.
  </Card>
</CardGroup>

To publish preview URLs from Actions, create [GitHub Deployments](/configuration/github-deployments). A preview behind Vercel deployment protection needs the [Vercel preview bypass](/configuration/vercel-preview-protection).

## What works on GitHub

| Capability | GitHub |
| :- | :- |
| Review on a preview URL, before merge | Yes. The GitHub App, or the Change Review Action when the review must wait for your deploy job. |
| Review after merge, with no preview URL | Yes. A workflow on the default branch runs the review against the deployed environment. |
| Review a mobile build for that pull request | Yes. Upload the binary from Actions and pin the review to it. The App alone tests the default build. |
| Run a regression test plan | Not from this guide. The Test Run Action does that. See [GitHub regression testing](/regression-testing/github). |
| Block the workflow until the review finishes | Yes on the Change Review Action. The automatic GitHub App posts the review and does not fail the workflow. |

## What this does not do

* The GitHub App does not test the pull request's mobile binary. CI has to upload that build.
* The automatic App does not fail the GitHub check's workflow job. Use the Change Review Action when the job should wait.
* This setup does not run a test plan you picked. That is [GitHub regression testing](/regression-testing/github).

## GitHub App

The App starts a review when a pull request opens or updates, and when someone comments `@qa.tech`. Install and repository settings are on [GitHub App](/configuration/github-app).

What a review does, how it selects tests, and when it creates tests are the same on every CI. See [How a review works](/pr-testing/overview#how-a-review-works).

### What the GitHub App posts

The GitHub App needs write permissions to communicate test results back to your PR. Here is what it posts and when:

| Operation | When | Description |
| :- | :- | :- |
| **PR comment (in progress)** | When the agent starts testing | Comment with link to the QA.tech conversation and details (branch, commit SHA, event type). Lets you track progress. |
| **PR comment (quota limit)** | When the organization is out of credits | Single comment with upgrade link. No tests run. |
| **PR review** | After tests complete | Native GitHub review with verdict (approve/request changes/comment), summary, test results table, and evaluation details. Replaces any pending review from the bot. |
| **Reaction (eyes emoji)** | When processing starts | Added to the triggering comment or PR to acknowledge it has been seen. |
| **Commit status** | During and after test run | GitHub check named **QA.tech / PR Review** on the PR commit. See [PR Review check](#pr-review-check) below for when it appears and what each state means. |

<Tip>
  If you see permission errors when installing the app, ensure the repository
  grants write access for pull requests and statuses. The app only writes when
  tests run or when the organization is out of credits.
</Tip>

### PR Review check

QA.tech registers a GitHub check run named **QA.tech / PR Review** on PR commits. You can require it in branch protection as a merge gate. Whether a check appears on PR open or sync depends on the [integration settings](/configuration/github-app#integration-settings) and how the review was triggered.

| Situation | Check created on PR open/sync? | What you see |
| :- | :- | :- |
| **Auto-run on PRs** enabled (default) | Yes | `in progress` while the agent reviews, then a final verdict (approve, request changes, or comment) |
| **Auto-run on PRs** disabled | No | No check until you explicitly trigger a review (see below) |
| Draft PR and **Include draft PRs** disabled | Yes (skipped) | Skipped check titled **Draft PR** - auto-review does not run on drafts until the PR is marked ready for review, or until you comment `@qa.tech` |
| Comment `@qa.tech` on the PR | Yes (when the review starts) | `in progress`, then final verdict |
| [Change Review Action](/pr-testing/github#change-review-action) or [Start change review chat API](/api-reference/chat/start-change-review-chat) | Yes (when triggered) | `in progress`, then final verdict. Runs even when **Auto-run on PRs** is off |

<Note>
  **Auto-run on PRs** controls only the automatic PR-open trigger. It does not block explicit triggers: `@qa.tech` comments, the Change Review Action, and the change-review API always start a review (and create or update the check) when invoked.

  When **Auto-run on PRs** is off, QA.tech does **not** post a skipped check that says auto-review is disabled. The PR simply has no QA.tech check until you trigger a review manually or from CI.
</Note>

### Triggering a PR Review

#### Automatic trigger (simple)

With **Run automatically on PRs** enabled, QA.tech starts a review when a PR is opened or updated and its preview deployment is ready. This is the simplest option once a repository is configured.

<Note>
  Automatic triggering works best when you have a **single application**
  deployed via [GitHub Deployments](/configuration/github-deployments) (or a
  provider that creates them, such as Netlify or Vercel) and you don't need
  extra control over when reviews run. If you have multiple applications per PR,
  don't use deployment-based previews, or need to gate reviews on labels, paths,
  or a specific deploy job, use the [Change Review
  Action](#change-review-action) instead.
</Note>

#### Manual trigger with an `@qa.tech` comment

You can trigger a review at any time by commenting `@qa.tech` on the pull request. This works on the main PR conversation and on inline code review comments, even when **Run automatically on PRs** is turned off or a previous review was abandoned.

The mention is case-insensitive (`@QA.tech`, `@qatech`, and similar variants all match). When QA.tech picks up the comment, it reacts with an eyes emoji to acknowledge it.

| Comment | What happens |
| :- | :- |
| `@qa.tech` | Re-runs the review. QA.tech waits for a ready preview deployment on the PR's latest commit, then starts automatically. |
| `@qa.tech test the checkout flow` | Runs a focused review. Any instructions after the mention are passed to the agent to steer what it tests. |
| `@qa.tech https://preview.example.com` | Runs immediately against the URL in the comment, which is treated as the preview deployment to test (skips waiting for a deployment). |

<Note>
  Without a URL in the comment, QA.tech waits for a ready preview deployment on
  the PR's latest commit. It posts a short "waiting for a preview deployment"
  comment and starts the review automatically once one is ready. Include a
  preview URL in the comment to start immediately against that environment.
</Note>

#### Re-run from the GitHub checks UI

Every review posts a status check on the PR commit. You can re-trigger a review by using GitHub's **Re-run** button on the QA.tech check, the same way you re-run any other CI check.

## Change Review Action

The Change Review Action still needs the [GitHub App](/configuration/github-app) installed at the organization level with access to the repository. The action does not replace that connection: without it, the agent cannot read the PR or post the review.

Start the review from a workflow when it must wait for a deploy job, a label, or a preview URL you pass in. It runs the same review agent and posts the same native PR review as the App. Turn off **Run automatically on PRs** in **Settings → Integrations → GitHub App** when the workflow drives the review, so each pull request is not reviewed twice.

The action calls [`POST /v1/chat/change-review`](/api-reference/chat/start-change-review-chat) with the PR URL and your per-PR environment overrides. It starts a review and creates the **QA.tech / PR Review** check even when **Auto-run on PRs** is off. A review with no preview environment is [After merge](#after-merge). A mobile binary is [Mobile build](#mobile-build). Pass extra testing guidance with the `context` input the same way as on [API pull request testing](/pr-testing/api#pass-extra-context).

### Setup

You do **not** need to map the repository to a project in QA.tech (that mapping only powers the App's automatic trigger); the action passes the repo and preview URLs inline.

<Steps>
  <Step title="Install the GitHub App">
    If you haven't already, follow [Install GitHub App](/configuration/github-app#how-to-set-it-up) to connect the App at the organization level under **Settings → Organization → Connections** and grant it access to the repository you want reviewed. Without the App connection (or repo access), the action's chat will start but the agent has no permission to read your PR. You do **not** need to create a repository mapping under **Settings → Integrations → GitHub App** for the action; that mapping is only needed for the App's automatic PR trigger.
  </Step>

  <Step title="Disable Auto-run on PRs (optional)">
    This step only applies if you have also mapped the repository under **Settings
    → Integrations → GitHub App** to enable the App's automatic trigger. If you
    want the action to be the only thing that triggers reviews on this repo, open
    that page and turn off **Auto-run on PRs**. With auto-run off, QA.tech does
    not create a **QA.tech / PR Review** check on PR open; the check appears when
    this action (or a `@qa.tech` comment) runs. Leave auto-run on if you want both
    the automatic trigger and on-demand action runs. If you never mapped the
    repository, there is no automatic trigger to disable.
  </Step>

  <Step title="Configure Secrets">
    Add a secret to your GitHub repository (**Settings → Secrets and variables →
    Actions**). Create the token in **Organization Settings → API Keys**. Store it
    as `QATECH_API_TOKEN`. Pass `project_short_id` from the project URL or
    Settings (for example `proj_abc123`).
  </Step>

  <Step title="Find your application short IDs">
    Copy application short IDs from **Settings → Applications & Envs** (three-dot
    menu → **Copy Short ID**), for example `app_abc123`. **Test Plans → API
    Integration** is a shortcut only when the app is already on a plan. New mobile
    apps often have no plan yet. Projects with more than one application should
    pass one entry per app so the agent knows which URL to test against.
  </Step>

  <Step title="Create Workflow">
    Create `.github/workflows/qatech-change-review.yml`:

    ```yaml theme={null}
    name: QA.tech Change Review
    on:
      pull_request:

    jobs:
      review:
        runs-on: ubuntu-latest
        steps:
          - uses: QAdottech/run-action/change-review@v4
            with:
              project_short_id: 'proj_abc123'
              api_token: ${{ secrets.QATECH_API_TOKEN }}
              applications_config: |
                {
                  "applications": {
                    "YOUR_APP_SHORT_ID": {
                      "environment": {
                        "url": "https://preview-${{ github.event.number }}.example.com"
                      }
                    }
                  }
                }
    ```

    On `pull_request` events the action reads the PR URL from `github.event.pull_request.html_url` automatically, so `pr_url` is optional.
  </Step>
</Steps>

### Change Review Action reference

#### Inputs

| Input | Description | Required | Default |
| :- | :- | :- | :- |
| `project_short_id` | QA.tech project short ID (for example `proj_abc123`) | Yes | - |
| `api_token` | QA.tech API token | Yes | - |
| `applications_config` | JSON of `{ "applications": { "<applicationShortId>": { "environment": { ... }, "devicePresetShortId": "..." } } }`. Each application must include an `environment`. | Yes | - |
| `blocking` | Wait for the assistant reply and expose it as an output. Fails the step on `FAILED` or `CANCELLED`. | No | `false` |
| `context` | Free-form context appended to the review. | No | - |
| `pr_url` | Pull request URL to review. Defaults to the PR URL of the current `pull_request` event. | No | Auto-detected on PR events |
| `api_url` | Custom API URL. | No | `https://api.qa.tech` |

Each environment in `applications_config` must include one of:

* `url` (plus optional `name`) to point the application at a preview URL.
* `shortId` to reference an existing environment short ID.
* `applicationBuildShortId` to reference a previously uploaded mobile build.

Optional `customHeaders` on any of those shapes persist host-pattern header rules on the environment (auth and protection bypass). Omit the field to leave stored headers unchanged; pass `[]` to clear them. See [Environment custom headers](/core-concepts/applications-and-environments#custom-headers).

See [Start change review chat API](/api-reference/chat/start-change-review-chat) for the request schema this maps to.

#### How runs are attributed

Runs started by a change review are labelled with the branch, commit, and author of the change under review, the same way Test Run Action runs are. The action reads `branch`, `commit_hash`, `commit_message`, `actor`, and `repository` from the workflow's `github` context and sends them with the request, so no extra inputs are needed.

When the action cannot supply them — for example on older action versions — QA.tech falls back to the head branch and head commit recorded for the pull request, so PR-mode reviews stay attributable either way.

#### Outputs

The agent posts its review natively on the PR. These outputs let you link to or log the chat conversation from your workflow; they are not the PR review body.

| Output | Description |
| :- | :- |
| `chat_created` | `"true"` when the chat conversation was created successfully. |
| `chat_short_id` | Short ID of the change review chat conversation (e.g. `chat_abc123`). |
| `chat_url` | Dashboard URL of the chat. Always set, even when not blocking. |
| `chat_status` | Final status of the agent's assistant message: `COMPLETED`, `FAILED`, `CANCELLED`, or `TIMED_OUT`. Only set when `blocking: true`. |
| `chat_response` | The agent's final assistant text in the chat (free-form, may summarize what was done). Not the GitHub PR review body. Only set when `blocking: true`. |

#### How polling works

When `blocking: true`, the action polls [`GET /v1/chat/{chat_short_id}`](/api-reference/chat/get-chat-conversation) every 20 seconds until the latest assistant message reaches a terminal status:

| Assistant status | Action behavior |
| :- | :- |
| `INITIATED` | Continues polling. |
| `PARTIAL` | Continues polling. |
| `COMPLETED` | Sets `chat_status` and `chat_response`. Step succeeds. |
| `FAILED` | Sets `chat_status` and `chat_response`. Step fails via `core.setFailed`. |
| `CANCELLED` | Sets `chat_status` and `chat_response`. Step fails via `core.setFailed`. |
| *(60 minutes elapsed)* | Sets `chat_status` to `TIMED_OUT`. Step fails via `core.setFailed`. |

Polling stops after an internal 60-minute timeout. Set a shorter [`timeout-minutes`](https://docs.github.com/en/actions/using-jobs/using-conditions-to-control-job-execution#jobsjob_idstepstimeout-minutes) on the step if you need a tighter cap.

### Change review implementation patterns

#### Block the workflow until the review finishes

By default the action returns as soon as the chat conversation is created and the agent starts working. Set `blocking: true` to wait for the agent to finish (and post its native PR review) before the workflow step completes. The step fails when the review ends in `FAILED` or `CANCELLED`, which makes it suitable as a deployment gate.

```yaml theme={null}
- uses: QAdottech/run-action/change-review@v4
  id: review
  with:
    project_short_id: 'proj_abc123'
    api_token: ${{ secrets.QATECH_API_TOKEN }}
    blocking: true
    applications_config: |
      {
        "applications": {
          "YOUR_APP_SHORT_ID": {
            "environment": {
              "url": "https://preview-${{ github.event.number }}.example.com"
            }
          }
        }
      }

- name: Log conversation
  if: always()
  run: |
    echo "Status: ${{ steps.review.outputs.chat_status }}"
    echo "Conversation URL: ${{ steps.review.outputs.chat_url }}"
    echo "Final assistant message: ${{ steps.review.outputs.chat_response }}"
```

The native review is posted to the PR by the agent itself. `chat_response` is the agent's final assistant text in the chat (which may summarize what it did but is not the GitHub review body); use it for logging or further automation, not as a replacement for the PR review.

#### Route multiple applications to per-PR preview URLs

The most common reason to reach for this action over the GitHub App's automatic trigger: projects with more than one application per PR (for example a frontend, a backend, and an admin dashboard). Pass one entry per application so the agent knows which preview URL to use for each. Each entry must include an `environment` with at least one of `url`, `shortId`, or `applicationBuildShortId`.

```yaml theme={null}
- uses: QAdottech/run-action/change-review@v4
  with:
    project_short_id: 'proj_abc123'
    api_token: ${{ secrets.QATECH_API_TOKEN }}
    applications_config: |
      {
        "applications": {
          "app_frontend": {
            "environment": {
              "url": "https://preview-${{ github.event.number }}-web.example.com",
              "name": "PR-${{ github.event.number }}"
            }
          },
          "app_backend": {
            "environment": {
              "url": "https://preview-${{ github.event.number }}-api.example.com",
              "name": "PR-${{ github.event.number }}"
            }
          }
        }
      }
```

#### Override the device preset

Pass `devicePresetShortId` alongside `environment` to run the review against a specific [device preset](/test-features/device-presets):

```yaml theme={null}
applications_config: |
  {
    "applications": {
      "app_frontend": {
        "environment": { "url": "https://preview.example.com" },
        "devicePresetShortId": "preset_mobile_abc123"
      }
    }
  }
```

#### Add free-form context

Use the `context` input to guide the review with PR-specific or repo-wide instructions. You can also generate this context automatically with an AI agent — see [PR Testing best practices](/best-practices/pr-testing#pass-generated-context-to-the-change-review-action).

```yaml theme={null}
- uses: QAdottech/run-action/change-review@v4
  with:
    project_short_id: 'proj_abc123'
    api_token: ${{ secrets.QATECH_API_TOKEN }}
    context: |
      Focus on the new Apple Pay flow. Test login is shared with the existing
      checkout test and uses test-user@example.com.
    applications_config: |
      { "applications": { "YOUR_APP_SHORT_ID": { "environment": { "url": "https://preview.example.com" } } } }
```

#### Review a different PR

Set `pr_url` when the workflow runs outside a `pull_request` event (for example on `workflow_dispatch`):

```yaml theme={null}
on:
  workflow_dispatch:
    inputs:
      pr_url:
        description: PR to review
        required: true

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: QAdottech/run-action/change-review@v4
        with:
          project_short_id: 'proj_abc123'
          api_token: ${{ secrets.QATECH_API_TOKEN }}
          pr_url: ${{ inputs.pr_url }}
          applications_config: |
            { "applications": { "YOUR_APP_SHORT_ID": { "environment": { "shortId": "env_staging" } } } }
```

## After merge

No preview environment. Review the merged pull request against staging or production.

If you do not have per-PR preview environments, run the change review **after** merging to your main branch. The workflow deploys to staging/production, extracts the PR number from the merge commit, and reviews the merged PR's changes against the deployed environment. Results are posted back on the merged PR as a comment.

```yaml theme={null}
name: Deploy and Review
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # Your existing deploy steps here
      - run: echo "Deploy to staging..."

  change-review:
    needs: deploy
    runs-on: ubuntu-latest
    steps:
      - name: Extract PR number from merge commit
        id: pr
        env:
          COMMIT_MSG: ${{ github.event.head_commit.message }}
        run: |
          PR_NUMBER=$(echo "$COMMIT_MSG" | grep -oE '\(#[0-9]+\)' | tail -1 | grep -oE '[0-9]+')
          if [ -n "$PR_NUMBER" ]; then
            echo "url=https://github.com/${{ github.repository }}/pull/$PR_NUMBER" >> "$GITHUB_OUTPUT"
            echo "found=true" >> "$GITHUB_OUTPUT"
          fi
      - name: Start QA.tech change review
        if: steps.pr.outputs.found == 'true'
        uses: QAdottech/run-action/change-review@v4
        with:
          project_short_id: 'proj_abc123'
          api_token: ${{ secrets.QATECH_API_TOKEN }}
          pr_url: ${{ steps.pr.outputs.url }}
          context: 'This PR has already been merged and deployed. Test the changes and report any issues found as a comment.'
          applications_config: |
            {
              "applications": {
                "YOUR_APP_SHORT_ID": {
                  "environment": {
                    "shortId": "env_staging"
                  }
                }
              }
            }
```

Notes:

* Pass `environment.shortId` for the existing staging environment; pass `url` only when staging is not already an environment in the project.
* The PR number is extracted from GitHub's default merge/squash commit message format (`(#123)`).
* Direct pushes to `main` without a PR are skipped automatically (the review step is gated on `steps.pr.outputs.found`).
* Results are posted back on the merged PR as a comment, since the PR is already closed.

## Post-merge agent

When a pull request is **merged**, QA.tech can run an autonomous **post-merge agent** to keep your test suite healthy — before the internal review retrospective runs. Configure it under **Settings → Integrations → GitHub App** with the **Run post-merge agent** toggle, which reveals a **Post-merge prompt** you can edit. These settings live on the GitHub App integration (alongside Auto-run on PRs and other merge-review options).

**New GitHub integrations** default the agent on with the default prompt prefilled. **Existing integrations** keep the agent off until you enable it (the default prompt is still prefilled in the form).

You can also pass per-PR options when starting a change review via the [Start change review chat API](/api-reference/chat/start-change-review-chat) or the [Change Review Action](#change-review-action). See [API and CI options](#post-merge-via-api-or-ci) below.

The key idea: **the prompt is the agent's entire goal.** The agent only changes tests when the prompt asks it to. If you point the prompt at something else (for example, "open a Linear issue summarizing the merge"), that is all it does — it will not touch your tests. The default prompt focuses on conservative regression maintenance.

The agent runs in the same conversation as the PR review, so it already has the review history — the tests that ran, their results, and the diff — as context. It reuses your existing test tools; there is nothing new to configure beyond the prompt.

### What it can do

Depending on your prompt, the agent can:

* **Promote review tests** into the regression suite. Only tests this PR's own reviews created — surfaced to the agent as "main candidates," still drafts and not last seen failing — can be promoted. Promoting activates a test (and its dependencies) and labels it `regression` + `auto-added` automatically, so you can filter for them in the test list. Promoting nothing is a normal outcome.
* **Update** tests whose behavior the merge changed so they match the merged state.
* **Convert to draft** tests that the merge made stale or irrelevant — cautiously, and only with strong, diff-grounded evidence. If another active test depends on one being drafted, the agent must confirm and draft the whole chain together; it never archives tests automatically.
* **Add tests to a test plan** (for example Smoke or Regression) when your prompt asks for it and defines the criteria.
* **Use connected integrations** — for example open a tracker issue, post or read a Slack update (when **Enable Slack Tools in Chat** is on), or (when you have connected MCP servers) run one of their tools — when your prompt asks for something other than test maintenance.

Before promoting or creating anything, the agent checks for an equivalent existing test to avoid duplicates, and it only keeps tests that will run reliably in a regression suite (no hard-coded names, IDs, or other data tied to a single PR). Labels are applied automatically on promotion — you do not manage them in the prompt.

### Example prompts

Use these as inspiration — copy, combine, or adapt them:

**Conservative regression maintenance (default):**

```
Now that this pull request is merged, update tests the merge changed, promote the
main-flow candidate tests that passed during review, and convert clearly stale tests to
draft. Skip edge cases. Never keep tests with hard-coded PR-specific data.
```

**Only cover normal end-user flows, not admin tooling:**

```
Maintain tests for this merge, but only for flows a normal end user would hit. Do not
promote or create tests that exercise admin-only screens or internal tooling.
```

**Add to the Smoke plan when criteria are met:**

```
Keep the regression suite healthy for this merge. If a promoted test covers a critical,
high-traffic flow (login, checkout, signup) and is stable, also add it to the "Smoke"
test plan.
```

**Be more aggressive about removing stale tests:**

```
After this merge, be proactive about converting tests to draft when the PR removed or
replaced the feature they cover. Explain your reasoning for each one you draft.
```

**Do something other than test maintenance:**

```
When this PR merges, open a Linear issue in the QA team summarizing the user-facing
changes and any test gaps you noticed during review. Do not modify any tests.
```

**Post a Slack summary after merge:**

Requires [Slack tools in chat](/integrations/slack#slack-tools-in-chat) to be enabled
for the project.

```
When this PR merges, post a short Slack summary of the user-facing changes to the
project's default Slack channel. Do not modify any tests.
```

<Note>
  The post-merge agent runs before the internal review retrospective, which
  always runs afterward regardless of whether the post-merge agent is enabled,
  skipped, or fails. Non-merged (closed) PRs skip the post-merge agent and go
  straight to the retrospective.
</Note>

### Post-merge via API or CI

When you trigger a merge review with [`POST /v1/chat/change-review`](/api-reference/chat/start-change-review-chat) (`mode: "pr"`), you can pass optional `postMerge` options. Those options are stored on the review conversation and **override** the GitHub App integration settings for that PR only when it merges.

```bash theme={null}
curl -sSf -X POST "https://api.qa.tech/v1/chat/change-review" \
  -H "Authorization: Bearer $QATECH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "pr",
    "prUrl": "https://github.com/acme/web/pull/42",
    "vcsProviderId": "github",
    "applicationOverrides": [
      { "id": "app_...", "environmentId": "env_..." }
    ],
    "postMerge": {
      "enabled": true,
      "prompt": "After merge, promote stable main-flow review tests and draft clearly stale ones."
    }
  }'
```

| Field | Required | Description |
| :- | :- | :- |
| `postMerge.enabled` | Yes (when `postMerge` is sent) | `true` runs the post-merge agent after merge; `false` skips it even if the integration has it on |
| `postMerge.prompt` | No | Full goal for the agent. Omit or leave empty to use the default maintenance prompt |

Omit `postMerge` entirely to use the GitHub App integration toggle and prompt. From CI, pass the same JSON body to the change-review API (or wrap it in your own workflow step).

Promoted tests are labelled `auto-added` for provenance today; a durable, structured link back to the originating pull request, and per-test-plan opt-in controls for automatic additions, are planned follow-ups.

## Mobile build

Store `QATECH_API_TOKEN` under **Settings → Secrets and variables → Actions**.

Upload the build first. The shared upload script is on [API pull request testing](/pr-testing/api#upload-the-pr-build).

### Run a change review on the PR

Use this when you want the review agent to select tests from the diff and post a native GitHub review. Install the GitHub App at the organization level with access to the repository. Turn off **Auto-run on PRs** for that repo in **Settings → Integrations → GitHub App** so QA.tech does not also start a review against the default environment.

```yaml theme={null}
name: QA.tech mobile change review
on:
  pull_request:

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build APK
        run: ./gradlew assembleDebug

      - name: Upload build to QA.tech
        id: upload
        env:
          QATECH_API_TOKEN: ${{ secrets.QATECH_API_TOKEN }}
          APK_PATH: app/build/outputs/apk/debug/app-debug.apk
        run: |
          APP_ID="app_gXeBl2"
          FILE_NAME=$(basename "$APK_PATH")
          UPLOAD_RESPONSE=$(curl -sSf -X POST "https://api.qa.tech/v1/applications/$APP_ID/builds/upload-url" \
            -H "Authorization: Bearer $QATECH_API_TOKEN" \
            -H "Content-Type: application/json" \
            -d "{\"fileName\": \"$FILE_NAME\"}")
          UPLOAD_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.uploadUrl')
          BUILD_TOKEN=$(echo "$UPLOAD_RESPONSE" | jq -r '.buildToken')
          curl -sSf -X PUT "$UPLOAD_URL" \
            --upload-file "$APK_PATH" \
            -H "Content-Type: application/octet-stream"
          BUILD_RESPONSE=$(curl -sSf -X POST "https://api.qa.tech/v1/applications/$APP_ID/builds" \
            -H "Authorization: Bearer $QATECH_API_TOKEN" \
            -H "Content-Type: application/json" \
            -d "{\"platform\": \"android\", \"buildToken\": \"$BUILD_TOKEN\"}")
          echo "build_short_id=$(echo "$BUILD_RESPONSE" | jq -r '.applicationBuildShortId')" >> "$GITHUB_OUTPUT"

      - uses: QAdottech/run-action/change-review@v4
        with:
          project_short_id: 'proj_abc123'
          api_token: ${{ secrets.QATECH_API_TOKEN }}
          applications_config: |
            {
              "applications": {
                "app_gXeBl2": {
                  "environment": {
                    "applicationBuildShortId": "${{ steps.upload.outputs.build_short_id }}"
                  }
                }
              }
            }
```

Without the GitHub App, the action can create a chat but never comment on the PR. Install it at the organization level with access to the repository.

The Change Review Action reads the PR URL from `github.event.pull_request.html_url` on `pull_request` events. Set `blocking: true` if the job should wait for the verdict. Inputs and outputs are in [Change Review Action reference](#change-review-action-reference).

Write a PR description that names the user-facing flows to test. The agent uses that context the same way it does for web PRs. See [PR Testing](/best-practices/pr-testing).

To run a known test plan against the same build, see [GitHub regression testing](/regression-testing/github#test-a-mobile-pull-request-build).

## Common questions

**Can I control which tests run?**

The GitHub App selects tests on its own. Steer a run by commenting `@qa.tech <instructions>` on the pull request, for example `@qa.tech test the checkout flow`. See [Triggering a PR Review](#triggering-a-pr-review). To choose the tests yourself, run a test plan with [GitHub regression testing](/regression-testing/github). You can use both.

**How do I trigger or re-run a review manually?**

Comment `@qa.tech` on the pull request. Add instructions to focus the review, or a preview URL to test a specific environment (`@qa.tech https://preview.example.com`). You can also use GitHub's **Re-run** button on the QA.tech check.

**My project has more than one application per pull request. What can I do?**

Keep the GitHub App installed, turn off **Auto-run on PRs** in **Settings → Integrations → GitHub App**, and start reviews from the [Change Review Action](#change-review-action). Pass one `applications_config` entry per application so each one gets its own preview URL. With auto-run off, the **QA.tech / PR Review** check appears when the action runs.

**Can I gate reviews on labels, paths, or a specific deploy job?**

Yes. Use the [Change Review Action](#change-review-action) and put the gating in your workflow (`if:` on labels or paths, `needs:` on a deploy job). Turn off **Auto-run on PRs** if only the action should start reviews.

**How do I review a native iOS or Android app?**

Automatic App reviews look for a preview URL. A mobile pull request is a new binary. Upload the build from CI and pin the review to it. See [Mobile build](#mobile-build). Turn off **Auto-run on PRs** for that repository so the App does not also review the default build.
