A API de merge assíncrono do GitHub (async merge API) é GA desde 1º de outubro de 2026 e é a única forma programática de fazer merge de pull requests empilhados (stacked pull requests). Sua automação envia um PUT para merge-async, recebe um UUID e consulta o resultado: o merge é executado em segundo plano, não dentro de uma única requisição. Se você mantém um bot de merge, um script de releases ou um agente que integra PRs, isso muda o que significa “terminado”.
O GitHub apresenta a API assíncrona como a via recomendada para merges programáticos, em vez do endpoint REST síncrono e das mutações GraphQL. Recomendado não é a mesma coisa que obsoleto: no momento da publicação desta nota, o endpoint síncrono PUT …/merge continuava documentado e funcionando. Mas o caso de uso dos stacked PRs só existe no novo. Nas palavras do próprio GitHub: “It’s also the only merge API that supports stacked pull requests.”
O que são pull requests empilhados (stacked pull requests)?
Os pull requests empilhados são uma cadeia ordenada de PRs pequenos no mesmo repositório, onde cada um aponta para a branch do PR que tem abaixo e toda a cadeia termina em uma única branch base, normalmente main. Em vez de um PR de 2.000 linhas que ninguém quer revisar, você abre três ou quatro camadas focadas que revisores diferentes podem trabalhar em paralelo.
O GitHub lançou os stacks nativos em public preview em 30 de julho de 2026. Você pode criá-los no github.com, pelo GitHub Mobile ou com a extensão CLI github/gh-stack (o comando gh stack), que também é distribuída como skill para agentes, de modo que os coding agents podem montar stacks por conta própria. Quando você faz merge de uma camada, todas as camadas não integradas que estão abaixo entram na mesma operação; os PRs de cima continuam abertos e se re-apontam automaticamente.
Duas ressalvas com data:
- A async merge API é GA; os stacked PRs em si continuavam em public preview no momento da publicação desta nota.
- No seu anúncio de julho, o GitHub indicou que o suporte de merge queue para stacks seria habilitado de forma progressiva nas semanas seguintes. Verifique se seu repositório tem isso antes de depender de
merge_queuepara um stack.
O que muda na API de merge assíncrono do GitHub?
Divide o merge em duas chamadas: uma para solicitá-lo e outra para conhecer o resultado.
Síncrono (PUT …/pulls/{n}/merge) |
Assíncrono (PUT …/pulls/{n}/merge-async) |
|
|---|---|---|
| Quando o merge é executado | Dentro da requisição | Em segundo plano |
| O que te retorna | merged: true/false e um SHA |
Um status e um UUID para consultar |
| Stacked PRs | Não suportados | Suportados (também integra as camadas inferiores) |
| Merge queue | — | merge_action: merge_queue |
| Merges complexos | Podem expirar | Projetado para evitar timeouts; alguns erros podem ser retentados |
O segundo endpoint é GET /repos/{owner}/{repo}/pulls/{pull_number}/merge-async/{uuid}, que retorna o resultado atual de uma requisição que você já fez.
Como fazer merge de um pull request com a API do GitHub?
Você envia um PUT com os mesmos campos que já conhece do endpoint síncrono, mais um novo:
commit_titleecommit_messagesha: o SHA do head com o qual o PR deve coincidirmerge_method:merge,squashourebasemerge_action(o novo):default,direct_mergeoumerge_queue. Comdefault, o GitHub escolhe a via mais apropriada.
curl -L -X PUT \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2026-03-10" \
https://api.github.com/repos/OWNER/REPO/pulls/PULL_NUMBER/merge-async \
-d '{"sha":"HEAD_SHA","merge_method":"squash","merge_action":"default"}'
O código HTTP dessa primeira chamada já te diz muito:
- 202: a requisição foi aceita e o merge será executado em segundo plano. Salve
details.uuid. - 200: o PR já estava integrado ou já está em uma merge queue.
- 409: já existe uma requisição de merge assíncrono para este PR. O GitHub te retorna o UUID dessa requisição, e suas opções podem ser diferentes das que você acabou de enviar.
- 400: o PR não pode ser integrado agora, por exemplo porque está fechado ou ainda está em rascunho.
As permissões exatas do token estão na referência oficial da API; verifique lá antes de dar acesso ao seu bot.
O que significam pending, merged, enqueued e failed?
Cada resposta traz um status com um de quatro valores, e um bot correto trata cada um como um resultado diferente:
status |
O que significa | O que seu bot deve fazer |
|---|---|---|
pending |
Aceito, não terminado | Continuar consultando com backoff |
merged |
Pronto; a resposta inclui o SHA do commit de merge | Registrar o SHA e prosseguir |
enqueued |
Entregue à merge queue, ainda não integrado | Parar de consultar o registro assíncrono e verificar o estado final do PR separadamente |
failed |
O merge não ocorreu; details.message explica por quê |
Mostrar a mensagem a uma pessoa ou para sua política de retentativas |
A armadilha é enqueued. Parece um sucesso, e um bot que anuncia “merged” nesse ponto se engana cada vez que a fila rejeita o PR mais tarde. Para confirmar o merge final, use o endpoint existente GET /repos/{owner}/{repo}/pulls/{pull_number}/merge, que responde 204 quando o PR está integrado e 404 enquanto não estiver.
Como migrar seu bot de merge para a API assíncrona?
Substituia “uma chamada e verificar merged” por uma pequena máquina de estados. Este esquema foi construído apenas a partir do contrato de requisição e resposta documentado e não foi executado contra um repositório real: teste-o primeiro em um repositório descartável.
#!/usr/bin/env bash
set -euo pipefail
API="https://api.github.com/repos/$OWNER/$REPO/pulls/$PR"
H=(-H "Accept: application/vnd.github+json"
-H "Authorization: Bearer $GITHUB_TOKEN"
-H "X-GitHub-Api-Version: 2026-03-10")
# 1. Solicitar o merge, fixado ao SHA do head que você revisou
out=$(curl -sS -L -X PUT "${H[@]}" -w '\n%{http_code}' "$API/merge-async" \
-d "{\"sha\":\"$HEAD_SHA\",\"merge_method\":\"squash\",\"merge_action\":\"default\"}")
code=$(tail -n1 <<<"$out"); body=$(sed '$d' <<<"$out")
case "$code" in
400) echo "Não é possível fazer merge: $(jq -r '.details.message' <<<"$body")"; exit 1 ;;
200) echo "Já resolvido: $(jq -r '.status' <<<"$body")"; exit 0 ;;
409) echo "Já existe uma solicitação; verifique suas opções:"; jq '.details' <<<"$body" ;;
esac
uuid=$(jq -r '.details.uuid' <<<"$body")
# 2. Consultar até chegar a um estado terminal
delay=5
while true; do
out=$(curl -sS -L "${H[@]}" -w '\n%{http_code}' "$API/merge-async/$uuid")
code=$(tail -n1 <<<"$out"); body=$(sed '$d' <<<"$out")
[ "$code" = "404" ] && { echo "Resultado expirado ou não encontrado"; exit 1; }
case "$(jq -r '.status' <<<"$body")" in
merged) echo "Integrado: $(jq -r '.details.sha' <<<"$body")"; exit 0 ;;
enqueued) echo "Na fila de merge; verifique o status final com GET $API/merge"; exit 0 ;;
failed) echo "Falhou: $(jq -r '.details.message' <<<"$body")"; exit 1 ;;
pending) sleep "$delay"; delay=$(( delay < 60 ? delay * 2 : 60 )) ;;
esac
done
O GitHub não documenta um intervalo de consulta; o backoff exponencial com limite de 60 segundos é um valor razoável, não um requisito.
Se você já integra PRs a partir do terminal, este fluxo se encaixa no que vimos em Copilot CLI estreia abas para issues, PRs e gists sem sair do terminal.
Que erros evitar com a async merge API?
- Um 202 não é uma aprovação. No momento da solicitação, o GitHub apenas realiza verificações básicas do status do PR. As regras de proteção de branch e as regras do repositório são avaliadas quando o merge realmente é executado, então uma solicitação aceita pode terminar em
failed. - Sempre envie
sha. Se você omitir, o GitHub usa o head no momento da solicitação, e o merge é cancelado se alguém fizer push enquanto isso. Enviar o SHA que você revisou deixa essa regra explícita, que é o que você quer em uma automação. - Os resultados expiram. De acordo com a referência da API no momento da publicação desta nota, um resultado assíncrono é preservado por 24 horas desde sua última atualização; depois, seu UUID retorna 404. Não use o UUID como registro de auditoria permanente: salve o resultado você mesmo.
- Os stacks fazem merge para baixo. Solicitar o merge da camada 3 integra as camadas 1, 2 e 3. Certifique-se de que a ideia do seu bot sobre “qual PR estou integrando” corresponde a isso.
- Ignorar regras não é o comportamento padrão. O anúncio menciona que você pode omitir regras de forma opcional se tiver permissão. Mantenha seu bot na rota protegida e reserve essa exceção para uma decisão deliberada de uma pessoa.
Vale a pena migrar agora?
Sim, se sua automação integra PRs grandes ou em repositórios com muita atividade, ou se seu time começa a usar stacks, especialmente gerados por agentes. A migração são dois endpoints e quatro estados. Os times que nunca fazem merge programático não perdem nada esperando; os que fazem vão querer o gerenciamento explícito de estados terminais de qualquer forma, porque é isso que faz com que o “merged” de um bot signifique realmente integrado.