# How to Integrate gh-stack with GitHub's Stacks REST API for PR Linking

> Learn how to integrate gh-stack with GitHub's Stacks REST API for seamless PR linking. Discover how the typed client interface maps CLI commands to HTTP endpoints.

- Repository: [GitHub/gh-stack](https://github.com/github/gh-stack)
- Tags: how-to-guide
- Published: 2026-08-02

---

**`gh-stack` integrates with GitHub's Stacks REST API through a typed client interface that maps CLI commands to HTTP endpoints like `/repos/{owner}/{repo}/stacks`, enabling automated linking of pull requests to stacks.**

The `github/gh-stack` CLI extension automates the management of stacked pull requests by synchronizing local Git state with GitHub's Stacks REST API. This integration allows developers to create stacks, add PRs, and retrieve metadata using either the command line or direct Go API calls.

## Architecture of the GitHub Stacks REST API Integration

The integration follows a layered architecture that separates API client logic from command handling and local state management.

### The Client Interface Layer

All Stacks REST API interactions are defined in [`internal/github/client_interface.go`](https://github.com/github/gh-stack/blob/main/internal/github/client_interface.go) and implemented in [`internal/github/github.go`](https://github.com/github/gh-stack/blob/main/internal/github/github.go). The `ClientOps` interface declares methods that correspond directly to REST endpoints:

- **`CreateStack`** – POST `/repos/{owner}/{repo}/stacks`
- **`AddToStack`** – POST `/repos/{owner}/{repo}/stacks/{stack_id}/prs`
- **`GetStack`** – GET `/repos/{owner}/{repo}/stacks/{stack_id}`
- **`Unstack`** – DELETE `/repos/{owner}/{repo}/stacks/{stack_id}`

According to the `github/gh-stack` source code, the real implementation in [`internal/github/github.go`](https://github.com/github/gh-stack/blob/main/internal/github/github.go) handles HTTP authentication, request serialization, and response parsing for each of these operations.

### Local Stack State Management

The tool maintains a local representation of each stack in `.git/gh-stack`, a JSON file managed by [`internal/stack/stack.go`](https://github.com/github/gh-stack/blob/main/internal/stack/stack.go). When you execute a command that modifies stack state, the flow synchronizes this local file with the remote API:

- Read current stack metadata from `.git/gh-stack`
- Execute the corresponding `ClientOps` method
- Update the local JSON file with the API response
- Report success or failure to the user

## Linking Pull Requests Using the Stacks API

The `gh stack link` command provides the primary user interface for associating a pull request with an existing remote stack.

### The Link Command Implementation

In [`cmd/link.go`](https://github.com/github/gh-stack/blob/main/cmd/link.go), the link command parses CLI flags such as `--stack-id` and `--pr-number`, validates the current repository context using `git.CurrentBranch()`, and invokes the API client. The implementation constructs the proper repository owner and name parameters before calling `github.ClientOps.AddToStack`.

```go
// Simplified flow from cmd/link.go
stackID := getStackIDFlag()
prNumber := getPRNumberFlag()
err := cfg.GitHubClientOverride.AddToStack(cfg.RepoOwner, cfg.RepoName, stackID, prNumber)

```

### API Communication Flow

When you run `gh stack link --stack-id 12345`, the execution follows this path:

```bash
gh stack link --stack-id 12345

```

The internal flow proceeds through these steps:

1. **CLI** parses arguments and loads configuration
2. **Link command** extracts the PR number from the current branch or flags
3. **Git client** confirms repository context via `git.CurrentBranch()`
4. **HTTP POST** to `/repos/{owner}/{repo}/stacks/{stack_id}/prs` via `ClientOps.AddToStack`
5. **Local update** of `.git/gh-stack` file in [`internal/stack/stack.go`](https://github.com/github/gh-stack/blob/main/internal/stack/stack.go)
6. **Output** displays success or error status

### Programmatic API Usage

You can integrate the Stacks REST API directly in Go applications by importing the client:

```go
import (
    "github.com/github/gh-stack/internal/github"
    "github.com/github/gh-stack/internal/config"
)

func linkPR(cfg *config.Config, stackID int64, prNumber int) error {
    // cfg.GitHubClientOverride can be swapped in tests; normally it is the real client
    return cfg.GitHubClientOverride.AddToStack(cfg.RepoOwner, cfg.RepoName, stackID, prNumber)
}

```

## Error Handling for API Failures

The extension implements structured error handling defined in [`cmd/utils.go`](https://github.com/github/gh-stack/blob/main/cmd/utils.go). When the Stacks REST API returns a non-200 status or connection fails, the client returns `ErrAPIFailure` with exit code 4. This sentinel error ensures the CLI provides clear diagnostics when the remote API is unreachable or returns validation errors.

## Testing the Integration

Unit tests in [`cmd/link_test.go`](https://github.com/github/gh-stack/blob/main/cmd/link_test.go) validate the linking logic without making live API calls. The test suite uses [`internal/github/mock_client.go`](https://github.com/github/gh-stack/blob/main/internal/github/mock_client.go), which implements the `ClientOps` interface with stubbed responses. This allows verification that the correct parameters are passed to `AddToStack` and that the command handles various API response scenarios correctly.

## Summary

- **`gh-stack`** integrates with GitHub's Stacks REST API through the `ClientOps` interface defined in [`internal/github/client_interface.go`](https://github.com/github/gh-stack/blob/main/internal/github/client_interface.go)
- **Local state** stays synchronized in `.git/gh-stack` via [`internal/stack/stack.go`](https://github.com/github/gh-stack/blob/main/internal/stack/stack.go)
- **PR linking** executes through [`cmd/link.go`](https://github.com/github/gh-stack/blob/main/cmd/link.go), which calls `AddToStack` to POST to `/repos/{owner}/{repo}/stacks/{stack_id}/prs`
- **Error handling** uses typed `ExitError` codes from [`cmd/utils.go`](https://github.com/github/gh-stack/blob/main/cmd/utils.go), returning code 4 for API failures
- **Testing** relies on [`internal/github/mock_client.go`](https://github.com/github/gh-stack/blob/main/internal/github/mock_client.go) to simulate REST API responses without network calls

## Frequently Asked Questions

### What HTTP method does gh-stack use to link a PR to a stack?

`gh-stack` uses an HTTP **POST** request to the endpoint `/repos/{owner}/{repo}/stacks/{stack_id}/prs`. The `AddToStack` method in [`internal/github/github.go`](https://github.com/github/gh-stack/blob/main/internal/github/github.go) constructs this request with the PR number in the request body, as implemented in the `github/gh-stack` source code.

### Where does gh-stack store local stack metadata?

Local stack metadata is stored in `.git/gh-stack`, a JSON file in the repository's Git directory. The [`internal/stack/stack.go`](https://github.com/github/gh-stack/blob/main/internal/stack/stack.go) package handles reading and writing this file to ensure the local state remains synchronized with the remote Stacks API.

### How does gh-stack handle API authentication?

According to the implementation in [`internal/github/github.go`](https://github.com/github/gh-stack/blob/main/internal/github/github.go), the GitHub client handles authentication by utilizing the GitHub CLI's built-in authentication mechanisms. The client automatically includes the necessary headers and tokens when making requests to the Stacks REST API endpoints.

### Can I use gh-stack programmatically without the CLI?

Yes, you can import the client directly from `github.com/github/gh-stack/internal/github` and call methods like `CreateStack` or `AddToStack` with a valid configuration object. The [`mock_client.go`](https://github.com/github/gh-stack/blob/main/mock_client.go) implementation demonstrates how to swap the client for testing or custom integrations.