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

# Pull request sync

> Auto-link GitHub pull requests to Ravenna tickets by branch name and move ticket status through the PR lifecycle.

Connect a Ravenna ticket to a GitHub pull request just by naming your branch after the ticket. Ravenna links the PR to the ticket, and moves the ticket through **Open → In Progress → Done** as the PR is opened, merged, and reopened.

## What you can do

* Reference a ticket from a branch name and have Ravenna attach the PR as a [ticket link](/documentation/tickets/links)
* Move a ticket to **In Progress** automatically when a matching PR is opened
* Move a ticket to **Done** automatically when the PR is merged
* Reopen a ticket automatically when its merged PR is reopened
* Leave the ticket alone when a PR is closed without merging

## Branch naming convention

Include the ticket display id anywhere in your branch name. The format is the channel prefix, a hyphen, and the ticket number:

```text theme={"system"}
PREFIX-NUMBER
```

Examples that all resolve to `ENG-42`:

```text theme={"system"}
ENG-42
ENG-42-fix-login-timeout
taylor/ENG-42/retry-webhook-verification
feature/ENG-42
```

The prefix comes from the ticket's [channel](/documentation/tickets/channels) — for example, a channel with prefix `HELP` produces ticket ids like `HELP-123`, and `HELP-123` in a branch name resolves back to it. Matching is case-insensitive.

### Disambiguating reused prefixes

Channel prefixes are unique per workspace, not per organization. If two workspaces in your organization both use the same prefix, `ENG-42` alone is ambiguous and Ravenna will skip the branch rather than guess.

To force a match, append the ticket's 8-character short id to the branch:

```text theme={"system"}
ENG-42-a7k3m9pq
```

The short id is visible on the ticket detail page. When present, Ravenna resolves the ticket by short id alone, so the match is unambiguous even across workspaces.

## PR lifecycle to ticket status

Ravenna reacts only to lifecycle events that indicate real progress. All other pull request activity (edits, review requests, label changes, pushes) is ignored.

| PR event                             | Ticket transition | Notes                                                                                                                                                                       |
| ------------------------------------ | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Opened** (non-draft)               | → **In Progress** | Only if the ticket is currently in an **Open** group. Tickets already **In Progress**, **Done**, or **Closed** are left alone so a follow-up PR cannot drag them backwards. |
| **Ready for review** (draft → ready) | → **In Progress** | Same guardrails as **Opened**. Draft PRs are ignored until they are marked ready.                                                                                           |
| **Merged**                           | → **Done**        | Regardless of prior status.                                                                                                                                                 |
| **Reopened**                         | → **Open**        | Regardless of prior status.                                                                                                                                                 |
| **Closed without merging**           | No change         | Abandoned work does not resolve a ticket.                                                                                                                                   |

When a status transition happens, Ravenna posts a private note on the ticket that names the pull request, so the audit trail shows why the status moved.

## Setup

Pull request sync is part of the Ravenna GitHub App. Installing the App on your organization is what enables both branch-based linking and the PR-lifecycle status transitions.

<Steps>
  <Step title="Install the Ravenna GitHub App">
    From **Settings → Integrations**, connect GitHub and complete the App installation on the organization that owns the repositories you want synced. An organization owner must approve the install.

    You can grant access to all repositories or pick a specific subset. Only PRs in repositories the App can see will be processed.
  </Step>

  <Step title="Configure the webhook secret (self-hosted only)">
    On Ravenna Cloud, the webhook secret is already configured for you.

    On self-hosted deployments, set `GITHUB_WEBHOOK_SECRET` to the value configured on your GitHub App's webhook settings. Without it, Ravenna cannot verify signatures on inbound deliveries and pull request events are rejected. GitHub's **Recent Deliveries** tab shows the verification failure when this variable is missing.
  </Step>

  <Step title="Confirm channel prefixes">
    Make sure each channel that engineers work out of has a memorable prefix set. The prefix is what maps a branch name back to a ticket. Change prefixes under **Settings → Channels** (see [Channels](/documentation/tickets/channels)).
  </Step>
</Steps>

<Callout icon="info" color="#2778ab">
  The same GitHub App also powers the [knowledge base sync](/integrations/github/knowledge). Installing it once enables both features — there is nothing extra to configure to opt into pull request sync.
</Callout>

## How it works

<AccordionGroup>
  <Accordion title="Ticket linking" icon="link">
    When Ravenna resolves a ticket from a PR's branch name, it attaches the PR's HTML URL as a [ticket link](/documentation/tickets/links) on that ticket. The link is created once per PR, so re-deliveries and repeated pushes do not create duplicates.
  </Accordion>

  <Accordion title="Draft pull requests" icon="pencil">
    Opening a PR as a draft does not transition the ticket. Ravenna waits for the `ready_for_review` event and applies the same **In Progress** transition then.
  </Accordion>

  <Accordion title="Status guardrails" icon="shield">
    The **In Progress** transition only fires when the ticket is currently in an **Open**-group status. If the ticket is already **In Progress**, **Done**, or **Closed**, Ravenna skips the transition. This prevents a stray follow-up PR from resurrecting resolved work or churning a ticket that is already being worked on.

    **Merged** and **Reopened** transitions have no such guardrail — they always run.
  </Accordion>

  <Accordion title="Repositories without a matching ticket" icon="circle-slash">
    If a branch name does not contain a `PREFIX-NUMBER` token, or the referenced ticket does not exist, Ravenna silently skips the event. There is no error and no notification — the PR just does not get linked. This is intentional, so day-to-day PRs on branches like `main` or `dependabot/npm/lodash-4.17.21` do not create noise.
  </Accordion>

  <Accordion title="Status configuration" icon="settings">
    Ravenna moves the ticket to the channel's default status in the target status group (**Open**, **In Progress**, or **Done**). If a channel does not have a status configured for the target group, that transition is skipped and logged.
  </Accordion>
</AccordionGroup>

## Related

* [Ticket links](/documentation/tickets/links) — how automatic and manual links behave, including the GitHub PR link created by this sync
* [Channels](/documentation/tickets/channels) — configuring prefixes that show up in branch names
* [Knowledge sync](/integrations/github/knowledge) — the other feature powered by the same GitHub App
