> For the complete documentation index, see [llms.txt](https://docs.aviator.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aviator.co/verify/how-to-guides/registering-an-external-preview.md).

# Registering an external preview

Some teams already deploy an ephemeral environment for every pull request from their own CI. An **endpoint preview** lets Verify use that environment instead of building one: your pipeline deploys the app as it always has, then registers the resulting URL with Aviator, and verification runs against it.

Aviator never builds or tears down an endpoint preview. It correlates the registration to the verification session, waits for it when a run needs it, drives the app at that URL, and stops using it when the registration expires.

For the alternative — Aviator booting a container from a preview image — see [Creating a preview](/verify/how-to-guides/creating-a-preview.md). For the concept, see [Concepts: Previews](/verify/concepts/previews.md).

**Time:** \~20 minutes

**Prerequisites:**

* The repo connected to Aviator — see [Connect a repository](/verify/how-to-guides/connect-a-repository.md)
* A CI pipeline that already deploys a reachable per-PR environment
* An Aviator API token — see [API authentication](https://github.com/aviator-co/gitbook-docs/tree/main/api/reference/authentication.md)

### Step 1: Declare an endpoint preview

Endpoint previews are configured in your **Aviator Verify settings** (**Verify → Settings → Verify**, with the repo selected). Add a `preview` entry with `method: endpoint`:

```yaml
verify:
  preview:
    - name: ci
      method: endpoint
      wait_timeout_sec: 1800     # how long a run waits for a registration
      expires_default_sec: 7200  # registration lifetime when a registration omits expires_at
```

Endpoint entries take none of the sandbox fields (`image`, `port`, `setup`, `teardown`, `secrets`) — the environment isn't Aviator's to build. See [Preview YAML reference](/verify/reference/preview-yaml.md) for the full schema.

Registrations are rejected until this entry exists: a repo with no `method: endpoint` preview configured returns `400`.

### Step 2: Register the preview from CI

Make the registration the **last step of the deploy job**, after the environment is deployed and reachable. Registering earlier means Verify may start driving a URL that isn't serving yet.

`POST https://api.aviator.co/api/v1/verify/preview-environment`

Authenticate with `Authorization: Bearer <api token>`, the same as the rest of the [REST API](/api/reference/json-api.md).

#### Request body

| Field         | Type    | Required | Description                                                                                                                                                                                                                            |
| ------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `repository`  | object  | yes      | `{"org": "...", "name": "..."}` — the repository the preview was built from.                                                                                                                                                           |
| `branch`      | string  | yes      | The branch the preview was built from. Used to correlate the registration when no PR number is supplied.                                                                                                                               |
| `pr_number`   | integer | no       | The pull request number. Preferred: when present it takes precedence over `branch` for correlation.                                                                                                                                    |
| `commit_sha`  | string  | yes      | The commit the environment is running: the full SHA or an abbreviation of at least 7 characters. A registration only drives verification while this matches the head of the branch being verified.                                     |
| `preview_url` | string  | yes      | Absolute `http(s)` base URL of the live environment. Must be publicly reachable from Aviator — URLs pointing at localhost, private, or link-local addresses are rejected. (On-premise installations can permit private address space.) |
| `secrets`     | object  | no       | Credentials for driving the app as a signed-in test user, up to 16 KB. See [Credentials](#credentials-for-signing-in).                                                                                                                 |
| `expires_at`  | string  | no       | ISO 8601 timestamp, at most 7 days ahead. When omitted, the config's `expires_default_sec` applies.                                                                                                                                    |

`branch` is required even when you send `pr_number`, because every CI job knows its branch — including jobs that run before a pull request exists.

#### Responses

| Status | Meaning                                                                                                                                          |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `202`  | Registration accepted. The body carries `registration_id`, `status`, `url`, `message`, and `expires_at`.                                         |
| `400`  | Invalid payload — including a repo whose Verify config declares no `method: endpoint` preview, or a `preview_url` that isn't publicly reachable. |
| `401`  | Missing or invalid API token.                                                                                                                    |
| `404`  | Repository not found for this account.                                                                                                           |

A `202` body looks like:

```json
{
  "registration_id": 412,
  "status": "attached",
  "url": "https://app.aviator.co/r/1042",
  "message": "Preview registered and attached to this Verify session.",
  "expires_at": "2026-09-05T18:00:00+00:00"
}
```

`status` tells you what the registration found:

* **`attached`** — a verification session already existed for this pull request or branch, and the registration is now driving it. `url` links to that session.
* **`buffered`** — the registration arrived before the session existed. It's held and attaches automatically once the session is created, so CI outrunning session creation is expected and safe.

Registrations are append-only: re-POSTing records a new registration and the newest one wins, so blind CI retries are safe.

### Step 3: Add the CI step

A GitHub Actions example — adapt the shell and variables to your provider:

```yaml
- name: Register preview with Aviator
  run: |
    curl -sS -X POST https://api.aviator.co/api/v1/verify/preview-environment \
      -H "Authorization: Bearer $AVIATOR_API_TOKEN" \
      -H "Content-Type: application/json" \
      -d @- <<EOF
    {
      "repository": {"org": "acme-corp", "name": "webapp"},
      "branch": "${GITHUB_HEAD_REF}",
      "pr_number": ${{ github.event.pull_request.number }},
      "commit_sha": "${{ github.event.pull_request.head.sha }}",
      "preview_url": "https://pr-${{ github.event.pull_request.number }}.preview.acme.dev",
      "secrets": {"TEST_USER_PASSWORD": "..."}
    }
    EOF
```

Store the token as a CI secret (`AVIATOR_API_TOKEN` above) — never inline it in the workflow file.

### Credentials for signing in

The optional `secrets` object carries the credentials Verify needs to **drive your app as a signed-in user** — a session cookie value, a test user's password. These are for the user agent operating the app, not infrastructure secrets; an endpoint preview is already deployed by the time Aviator sees it, so it never needs your database password or third-party keys.

Your [Verify skill](/verify/how-to-guides/writing-a-skill-md.md) references them with the same placeholder syntax as account secrets:

```markdown
3. Fill the password field with `{{ secrets.TEST_USER_PASSWORD }}`.
```

The contract:

* **Resolution.** Registered values resolve alongside your account secrets. On a name collision, the registration's value wins for that run.
* **Lifetime.** Values are scoped to the registration — they're deleted when it expires or is deregistered.
* **Handling.** Encrypted at rest, never shown to the verification agent: values are substituted outside the model conversation and scrubbed from captured evidence.

### What a verification run does

When a run reaches work that needs the preview and no current registration exists, it **waits** — the session shows that it's waiting for CI to register a preview — for up to `wait_timeout_sec`.

* **The registration arrives in time.** Verification proceeds against the registered URL.
* **The wait times out.** Verification still completes, using code analysis only; runtime scenarios aren't exercised. This is a degraded result, not a failure — and a registration that lands afterwards starts a follow-up verification automatically.
* **The registration doesn't match the branch head.** A registration whose `commit_sha` isn't the head of the branch being verified never drives the run. The session shows which commit is registered against which commit it's verifying — usually a sign the deploy job is lagging behind a newer push.

Tune `wait_timeout_sec` to your deploy time, with headroom for a queued runner. Too short and runs degrade on a slow build; too long and a broken deploy job holds the run open.

### Re-registering

Registering again is the normal way to move a preview forward:

* **A new commit.** Registering a different `commit_sha` re-verifies it. If that commit is the branch head and was previously verified without a preview, a follow-up verification starts automatically.
* **The same commit at a new URL.** A redeploy — for example, a rebuilt environment on a fresh hostname — makes the new URL current without triggering a re-run.

### Expiry and deregistration

A registration stops driving runs at `expires_at` (or after `expires_default_sec` when the registration didn't set one). At that point its credentials are removed and no new run uses it.

To release it earlier — from a CI teardown job, or when the environment is destroyed on PR close — send a `DELETE` to the same path with the same authentication:

```bash
curl -sS -X DELETE https://api.aviator.co/api/v1/verify/preview-environment \
  -H "Authorization: Bearer $AVIATOR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"repository": {"org": "acme-corp", "name": "webapp"}, "branch": "feature/checkout", "pr_number": 412}'
```

Deregistration is idempotent: an unknown or already-deleted registration returns `200` with `{"deleted": 0}`, so teardown jobs can run unconditionally. Registered credentials are deleted immediately.

### Next steps

* [Preview YAML reference](/verify/reference/preview-yaml.md) — every preview field
* [Writing a Verify skill](/verify/how-to-guides/writing-a-skill-md.md) — teach the runner how to drive your app
* [Concepts: Previews](/verify/concepts/previews.md) — how previews fit into verification


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.aviator.co/verify/how-to-guides/registering-an-external-preview.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
