Ambientes de Preview — API
Use estes endpoints para configurar e operar Ambientes de Preview aninhados em um projeto HTTP principal. Um preview tem identidade, URL, capacidade, storage e ciclo de cobrança próprios, mas só pode ser acessado pelo projeto principal ao qual pertence.
A operação é idempotente por organização, projeto principal e previewKey. PUT e DELETE podem retornar uma operação assíncrona; consulte o endpoint de operação até alcançar um estado terminal.
Autenticação e segurança
Inclua uma credencial com as permissões do projeto correspondente e, para automações, uma API Key da organização:
| Header | Obrigatório | Descrição |
|---|---|---|
x-api-key | Nas operações de automação | API Key da organização, mantida em um secret. |
x-organization-id | Com token de usuário | Organização ativa do projeto. A API Key da organização já está vinculada à organização. |
Content-Type: application/json | Em requests com body | Indica um body JSON. |
O owner precisa habilitar Ambientes de Preview no console antes que uma API Key da organização possa operar previews. A chave fica limitada ao projeto principal, aos grants concedidos e aos guardrails configurados: ela não altera o entitlement da organização, não ultrapassa os limites efetivos e não escolhe planos não permitidos. Alterar as settings exige project.preview.configure.
Para o ciclo operacional mínimo, conceda no projeto principal project.read, project.env.read, project.preview.read, project.preview.deploy e project.preview.delete. Como todo preview herda as variáveis do projeto principal, criar ou atualizar um preview também exige project.env.read ou project.image.update nesse projeto; sem uma delas, o PUT retorna 403 (PREVIEW_ENV_ACCESS_REQUIRED) antes de qualquer efeito. A chave legada do projeto já atende a esse requisito. project.preview.configure fica reservado à configuração das settings. Para custos, conceda também project.billing.read.
As respostas públicas nunca incluem variáveis de ambiente, credenciais, URLs privadas de imagem, nomes de recursos ou detalhes operacionais internos. Trate a API Key como segredo e não a registre.
URL base e identificadores
https://api.zenifra.com/v1Nos exemplos, PROJECT_ID representa o ID do projeto HTTP principal e API_KEY representa uma API Key da organização mantida em um secret. Substitua apenas esses placeholders pelos valores da sua organização.
export PROJECT_ID="seu-project-id"
export API_KEY="sua-api-key"
export USER_TOKEN="seu-token-de-usuario"
export ORGANIZATION_ID="sua-organization-id"
export BASE_URL="https://api.zenifra.com/v1"A previewKey deve usar somente letras, números, ponto, sublinhado e hífen, com 1 a 100 caracteres, e conter ao menos uma letra ou um número. Chaves como ., .. ou --- são rejeitadas com 400. Não altere silenciosamente uma chave rejeitada.
Endpoints
| Objetivo | Método e rota |
|---|---|
| Consultar settings | GET /project/:projectId/preview-environments/settings |
| Atualizar settings | PATCH /project/:projectId/preview-environments/settings |
| Listar plano herdado | GET /project/:projectId/preview-environments/plans |
| Consultar custos dos previews | GET /project/:projectId/preview-environments/billing |
| Listar previews | GET /project/:projectId/preview-environments |
| Criar ou atualizar um preview | PUT /project/:projectId/preview-environments/:previewKey |
| Consultar um preview | GET /project/:projectId/preview-environments/:previewKey |
| Remover um preview | DELETE /project/:projectId/preview-environments/:previewKey |
| Consultar uma operação | GET /project/:projectId/preview-environments/:previewKey/operations/:operationId |
Settings do projeto
Consultar settings
GET /project/:projectId/preview-environments/settingsRetorna o opt-in e os guardrails efetivos do projeto principal. A resposta pública inclui enabled, o plano herdado do projeto em default_plan/allowed_plans, default_ttl_hours, max_ttl_hours, max_active_environments, active_environments, organization_limits e a configuração dos previews nativos em github e forgejo. O Preview nunca pode escolher um plano diferente do projeto principal. A consulta exige project.preview.read e aceita sessão de usuário ou API Key da organização.
curl -sS "$BASE_URL/project/$PROJECT_ID/preview-environments/settings" \
-H "Authorization: Bearer $USER_TOKEN" \
-H "x-organization-id: $ORGANIZATION_ID"Exemplo de resposta:
{
"status": "success",
"data": {
"enabled": true,
"default_plan": "basic",
"allowed_plans": ["basic"],
"default_ttl_hours": 24,
"max_ttl_hours": 168,
"max_active_environments": 2,
"active_environments": 1,
"github": {
"enabled": true,
"target_branch": "main"
},
"forgejo": {
"enabled": false
},
"organization_limits": {
"enabled": true,
"max_active_environments": 10,
"max_per_project": 2,
"max_ttl_hours": 168,
"active_environments": 3
}
}
}Atualizar settings
PATCH /project/:projectId/preview-environments/settingsRequer project.preview.configure no projeto e aceita sessão de usuário ou API Key da organização com esse grant. max_active_environments aceita de 0 a 1000; o valor 0 é preservado e retornado como 0. A API não permite alterar o entitlement da organização nem ultrapassar o limite efetivo.
Previews não estão disponíveis quando o projeto principal usa o plano free ou um plano que não oferece previews: habilitar enabled: true nesses casos retorna 400 (preview_plan_not_allowed).
{
"enabled": true,
"default_ttl_hours": 24,
"max_ttl_hours": 168,
"max_active_environments": 2,
"github": {
"enabled": true,
"target_branch": "main"
}
}default_ttl_hours e max_ttl_hours devem estar entre 1 e 168, e o default não pode ser maior que o máximo. O preço horário sempre vem do plano do projeto principal.
Por padrão, github.enabled é false. Esse campo habilita o fluxo nativo para um projeto HTTP cuja origem seja um repositório GitHub. github.target_branch define a branch de destino dos pull requests que podem criar previews e é obrigatória quando github.enabled for true; quando o fluxo está desabilitado, ela pode ser omitida. A configuração de GitHub é independente de auto-deploy: ela não altera a branch ou a política de atualização da aplicação principal. Para projetos com origem Forgejo, o objeto forgejo aceita os mesmos campos (enabled e target_branch, obrigatória quando habilitado); veja Deploy a partir do Forgejo. Ligar os previews nativos do Forgejo em um repositório sem suporte a eles retorna 400 (forgejo_preview_unavailable); com eles já ligados, salvar outras settings ou desligá-los continua aceito.
Quando o fluxo nativo está ativo, pull requests do próprio repositório com destino na branch escolhida usam a identidade pr-<number> nos eventos opened, reopened e synchronize. O evento edited reavalia uma mudança na branch de destino e closed remove o preview. Pull requests de forks não participam, e pull requests já abertos antes da ativação não são importados automaticamente; o próximo evento relevante inicia a avaliação.
O fluxo nativo usa o runtime e os comandos já configurados no projeto e não exige GitHub Action, API Key, IMAGE ou workflow no repositório. O endpoint PUT /project/:projectId/preview-environments/:previewKey continua sendo o caminho para a Action e outras automações que publicam uma imagem pronta.
Desabilitar github.enabled ou o opt-in global, alterar github.target_branch ou desconectar/trocar o repositório remove os previews nativos afetados e invalida publicações pendentes. A aplicação principal e sua configuração de auto-deploy permanecem separadas desse cleanup.
Custos dos previews
GET /project/:projectId/preview-environments/billingRequer project.billing.read. Retorna o custo acumulado dos Previews ativos, a taxa horária atual e uma estimativa até a expiração de cada ambiente e do conjunto.
Os valores monetários são retornados em centavos de BRL, seguindo o contrato de billing existente. Por exemplo, 5.4 representa R$ 0,054. accrued_amount considera snapshots horários calculados até as_of; pending_amount identifica o valor ainda aguardando consolidação. estimated_until_expiration é uma estimativa, não uma cobrança final, e não inclui a hora corrente enquanto ela não tiver sido consolidada.
{
"status": "success",
"data": {
"currency": "brl",
"as_of": "2026-08-25T22:00:00.000Z",
"previews": [
{
"preview_id": "preview-id",
"key": "pr-42",
"status": "running",
"plan": "basic",
"hourly_rate": 5.4,
"accrued_amount": 10.8,
"settled_amount": 5.4,
"pending_amount": 5.4,
"estimated_until_expiration": 21.6,
"expires_at": "2026-08-26T02:00:00.000Z",
"billing_status": "pending"
}
],
"summary": {
"active_previews": 1,
"accrued_amount": 10.8,
"settled_amount": 5.4,
"pending_amount": 5.4,
"hourly_rate": 5.4,
"estimated_until_expiration": 21.6
}
}
}Planos
Listar planos de preview
GET /project/:projectId/preview-environments/plansRetorna o plano do projeto principal e seus preços publicados em BRL. O endpoint existe para que o Console mostre o custo correto; não há um catálogo de planos exclusivo para Preview.
curl -sS "$BASE_URL/project/$PROJECT_ID/preview-environments/plans" \
-H "Authorization: Bearer $USER_TOKEN" \
-H "x-organization-id: $ORGANIZATION_ID"O preço retornado pelo catálogo é a fonte de verdade. Não copie valores comerciais para a Action ou para a aplicação cliente.
Listar previews
GET /project/:projectId/preview-environmentsLista os previews aninhados no projeto principal. A resposta traz data.items e data.pagination.
curl -sS "$BASE_URL/project/$PROJECT_ID/preview-environments" \
-H "Authorization: Bearer $USER_TOKEN" \
-H "x-organization-id: $ORGANIZATION_ID"Cada item pode conter os seguintes campos públicos:
| Campo | Descrição |
|---|---|
id | Identificador público do preview. |
key | Chave estável que identifica o ambiente no projeto. |
status | Estado de produto, como accepted, provisioning, available, deleting ou failed. Previews já removidos não aparecem na lista. |
url | URL pública quando disponível. |
plan | Plano selecionado. |
payment_mode | O preview usa cobrança hourly. |
exposure | Exposição configurada para o ambiente. |
inherit_envs | Sempre true: os ENVs do projeto principal são herdados. Nunca contém os valores. |
updated_at e expires_at | Atualização mais recente e expiração atual. |
A lista não retorna ENVs, dados, credenciais, hashes, leases, contadores ou nomes de recursos.
Execuções nativas do GitHub
A resposta de GET /project/:projectId/preview-environments também traz data.github_runs e data.forgejo_runs, com as execuções nativas de pull requests de cada provedor (listas vazias quando não há execuções). Essas seções não alteram o formato dos itens de Preview e têm o mesmo formato entre si. A leitura usa a mesma permissão project.preview.read do projeto principal.
{
"data": {
"items": [],
"pagination": {
"page": 1,
"limit": 20,
"total": 0
},
"github_runs": {
"items": [
{
"preview_key": "pr-42",
"pull_request_number": 42,
"status": "building",
"commit_sha": "abc123...",
"branch": "feature/example",
"target_branch": "main",
"repository": "example-org/example-repository",
"updated_at": "2026-09-16T18:30:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1
}
}
}
}Os estados públicos de uma execução são queued, building, updating, available, failed, blocked, removing e removed. error pode aparecer somente quando houver uma falha e contém um erro de produto normalizado com code e message.
Enquanto a execução estiver pendente ou falhar, ela não representa um Preview disponível: ainda não há URL, aplicação em execução ou valores de cobrança do ambiente. Quando a execução chegar a available, consulte a lista ou o detalhe do Preview para obter os dados públicos do ambiente. Uma execução removing representa o encerramento do Preview associado, e removed mantém o registro histórico depois que o ambiente deixa de existir.
Se uma tentativa falhar, consulte error e envie um novo commit ou produza outro evento relevante do pull request para iniciar uma nova tentativa. A plataforma não promete repetir automaticamente uma tentativa cujo resultado não pôde ser confirmado.
Criar ou atualizar um preview
PUT /project/:projectId/preview-environments/:previewKeyCria ou atualiza o preview identificado por previewKey. O corpo de um upsert aceita:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
image | string | Sim no upsert | Referência da imagem pronta a publicar (até 2.048 caracteres). |
inherit_envs | boolean | Não | Campo de compatibilidade legada; aceita somente true, porque o Preview sempre herda os ENVs do usuário dentro da Zenifra. false é rejeitado com 400. |
ttl_hours | integer | Não | Duração em horas; default 24, mínimo 1, máximo 168. |
source | object | Não | Metadados da origem enviados pela Action oficial: provider: "github_action" e, opcionalmente, event, repository, pull_request_number, branch, base_branch e commit_sha. |
O preview usa sempre o plano do projeto principal e sincroniza sua porta, exposição e regras de acesso. O storage é novo e começa vazio para o preview. Dados, domínios personalizados e comandos customizados de imagem não são herdados.
Antes de aceitar o upsert, a API confere:
- a permissão de herdar variáveis (
project.env.readouproject.image.update), senão403(PREVIEW_ENV_ACCESS_REQUIRED); - o projeto principal existe e está em execução ou parado, senão
404(PARENT_PROJECT_NOT_FOUND) ou409(PROJECT_STATE_CONFLICTouPROJECT_OPERATION_IN_PROGRESS); tipos de projeto sem previews recebem422(PROJECT_OPERATION_NOT_SUPPORTED); - a imagem pode ser usada pelo projeto, senão
422(IMAGE_NOT_ALLOWED); - a organização não tem pagamentos pendentes, senão
402(ORGANIZATION_BILLING_BLOCKED); - o plano do projeto principal oferece previews (o plano
freenão oferece), senão400(preview_plan_not_allowed); - a chave não pertence a um preview nativo de pull request do GitHub ou do Forgejo, senão
409(GITHUB_PREVIEW_KEY_OWNEDouFORGEJO_PREVIEW_KEY_OWNED).
Se o projeto principal estiver com outra operação em andamento depois do aceite, a operação do preview aguarda até 15 minutos; passado esse tempo, ela termina como failed e a versão anterior do preview é mantida. Depois de uma troca de plano do projeto principal, o próximo upsert ajusta o preview ao novo plano.
curl -sS -X PUT \
"$BASE_URL/project/$PROJECT_ID/preview-environments/pr-42" \
-H "Authorization: Bearer $USER_TOKEN" \
-H "x-organization-id: $ORGANIZATION_ID" \
-H "Content-Type: application/json" \
-d '{
"image": "docker.io/library/nginx@sha256:6784fb0834aa7dbbe12e3d7471e69c290df3e6ba810dc38b34ae33d3c1c05f7d",
"ttl_hours": 24
}'A API responde 202 com { "status": "accepted", "data": { "operation": {...}, "preview": {...} } }; operation.operation_id identifica a operação a consultar. Se houver uma operação ativa com payload incompatível, a API retorna 409 (PREVIEW_OPERATION_CONFLICT) e a operação existente deve ser consultada.
Consultar um preview
GET /project/:projectId/preview-environments/:previewKeyExige project.preview.read ou project.preview.deploy. Retorna o estado atual, URL, plano, preço horário em BRL quando disponível, herança de ENVs, timestamps e a operação corrente. Não retorna valores de variáveis de ambiente. Um preview inexistente ou já removido retorna 404.
curl -sS \
"$BASE_URL/project/$PROJECT_ID/preview-environments/pr-42" \
-H "Authorization: Bearer $USER_TOKEN" \
-H "x-organization-id: $ORGANIZATION_ID"Remover um preview
DELETE /project/:projectId/preview-environments/:previewKeySolicita a remoção imediata do preview. A cobrança de compute e de storage é encerrada no instante da remoção, e o endereço do preview é liberado. Repetir o delete enquanto a remoção está em andamento retorna a mesma operação, sem criar outra operação nem outra cobrança.
curl -sS -X DELETE \
"$BASE_URL/project/$PROJECT_ID/preview-environments/pr-42" \
-H "Authorization: Bearer $USER_TOKEN" \
-H "x-organization-id: $ORGANIZATION_ID"A API responde 202 com { "status": "accepted", "data": { "operation": {...}, "preview": {...} } }. Uma chave que não existe ou cujo preview já foi removido retorna 404 (PREVIEW_NOT_FOUND, mensagem Preview not found) e não cria operação; trate essa resposta como "já removido". Se houver um upsert em andamento para a mesma chave, a API retorna 409 (PREVIEW_OPERATION_CONFLICT). O delete aceita o projeto principal em qualquer estado. A Action não exige IMAGE para este caminho.
Remoção junto com o projeto principal
Excluir o projeto principal remove todos os seus previews na mesma hora e encerra a cobrança deles nesse instante. Previews também são removidos automaticamente quando a expiração (expires_at) é atingida, inclusive previews parados.
Polling de operações
Consultar uma operação
GET /project/:projectId/preview-environments/:previewKey/operations/:operationIdExige project.preview.read ou project.preview.deploy. Use o operation_id retornado em data.operation por PUT ou DELETE para acompanhar a operação. Consulte em intervalos limitados e pare em um estado terminal.
Estados possíveis:
acceptedreservingprovisioningupdatingavailabledeletingdeletedfailed
Upsert termina com sucesso em available; delete termina com sucesso em deleted. Em failed, leia o código público e corrija a entrada antes de tentar novamente.
curl -sS \
"$BASE_URL/project/$PROJECT_ID/preview-environments/pr-42/operations/operation-id" \
-H "Authorization: Bearer $USER_TOKEN" \
-H "x-organization-id: $ORGANIZATION_ID"A resposta de operação contém somente estado, identificadores públicos, timestamps, URL quando disponível, expires_at e erro público sanitizado. Não contém segredos ou detalhes internos.
Erros públicos
Os erros usam { "status": "failed", "code": "...", "message": "..." }. Os códigos permanecem estáveis e a mensagem é acionável:
| Status | Código | Quando ocorre |
|---|---|---|
400 | preview_disabled | Os Ambientes de Preview não estão habilitados para o projeto ou a organização. Também vale para operações com API Key da organização enquanto o opt-in estiver desligado |
400 | preview_plan_not_allowed | O plano do projeto principal não oferece previews (por exemplo, o plano free) |
400 | preview_ttl_invalid, preview_ttl_exceeds_limit, preview_limit_invalid | Duração ou limite fora do permitido |
400 | forgejo_preview_unavailable | Previews nativos do Forgejo não estão disponíveis para o repositório do projeto |
402 | ORGANIZATION_BILLING_BLOCKED | A organização tem pagamentos pendentes; regularize-os e repita a operação |
403 | PREVIEW_ENV_ACCESS_REQUIRED | A credencial não tem project.env.read nem project.image.update no projeto principal |
404 | PARENT_PROJECT_NOT_FOUND | O projeto principal não existe ou foi excluído |
404 | PREVIEW_NOT_FOUND | Delete de uma chave inexistente ou de um preview já removido |
409 | PROJECT_STATE_CONFLICT, PROJECT_OPERATION_IN_PROGRESS | O projeto principal não está em execução nem parado, ou tem outra operação em andamento |
409 | GITHUB_PREVIEW_KEY_OWNED, FORGEJO_PREVIEW_KEY_OWNED | A chave pertence a um preview nativo de pull request (mensagem The preview is managed by another workflow) |
422 | IMAGE_NOT_ALLOWED | A imagem informada não pode ser usada neste projeto |
422 | PROJECT_OPERATION_NOT_SUPPORTED | O tipo do projeto principal não oferece previews |
409 | PARENT_CONFIGURATION_UNAVAILABLE | O projeto principal não tem configuração pública suficiente para aplicar o preview |
409 | PREVIEW_OPERATION_CONFLICT | Já existe uma operação ativa e incompatível para a mesma identidade |
409 | PREVIEW_ORGANIZATION_LIMIT_REACHED, PREVIEW_PROJECT_LIMIT_REACHED | O limite efetivo da organização ou do projeto foi atingido |
502 | PREVIEW_PROVISIONING_FAILED | O preview não pôde ser provisionado |
503 | ORGANIZATION_RUNTIME_UNAVAILABLE | O ambiente da organização está temporariamente indisponível; tente novamente em instantes |
preview_wait_timeout é reportado pela Action oficial quando ela atinge o tempo máximo de espera; a API não retorna esse código. Nunca exponha a resposta completa em logs públicos quando ela fizer parte de uma automação.
Status HTTP
200: leitura bem-sucedida.202: upsert ou delete aceito para processamento, sempre com{ "status": "accepted", "data": { "operation", "preview" } }.400: body, chave, ação, duração inválidos, plano sem previews ou opt-in desligado.401: credencial ausente ou inválida.402: organização com pagamentos pendentes.403: organização, projeto ou permissão insuficiente.404: projeto principal, preview ou operação não encontrada.409: conflito com uma operação ativa, payload incompatível, chave de um preview nativo, estado do projeto principal, limite atingido ou configuração do projeto principal indisponível.422: imagem não permitida ou tipo de projeto sem previews.502,503: falha ao provisionar o preview ou ambiente da organização temporariamente indisponível.
Limites de requisições (contados por IP de origem): leitura da lista, das settings e dos planos 50 por minuto; atualização das settings 10 por minuto; leitura de um preview 100 por minuto; PUT e DELETE 10 por minuto; consulta de operação 120 por minuto; custos 30 por minuto.
Próximos passos
Última atualização em