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

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 and implemented in 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 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. 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.

In 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.

// 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:

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
  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:

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. 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 validate the linking logic without making live API calls. The test suite uses 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

Frequently Asked Questions

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 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 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, 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 implementation demonstrates how to swap the client for testing or custom integrations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →