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:

HeaderObrigatórioDescrição
x-api-keyNas operações de automaçãoAPI Key da organização, mantida em um secret.
x-organization-idCom token de usuárioOrganização ativa do projeto. A API Key da organização já está vinculada à organização.
Content-Type: application/jsonEm requests com bodyIndica 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/v1

Nos 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

ObjetivoMétodo e rota
Consultar settingsGET /project/:projectId/preview-environments/settings
Atualizar settingsPATCH /project/:projectId/preview-environments/settings
Listar plano herdadoGET /project/:projectId/preview-environments/plans
Consultar custos dos previewsGET /project/:projectId/preview-environments/billing
Listar previewsGET /project/:projectId/preview-environments
Criar ou atualizar um previewPUT /project/:projectId/preview-environments/:previewKey
Consultar um previewGET /project/:projectId/preview-environments/:previewKey
Remover um previewDELETE /project/:projectId/preview-environments/:previewKey
Consultar uma operaçãoGET /project/:projectId/preview-environments/:previewKey/operations/:operationId

Settings do projeto

Consultar settings

GET /project/:projectId/preview-environments/settings

Retorna 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/settings

Requer 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/billing

Requer 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/plans

Retorna 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-environments

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

CampoDescrição
idIdentificador público do preview.
keyChave estável que identifica o ambiente no projeto.
statusEstado de produto, como accepted, provisioning, available, deleting ou failed. Previews já removidos não aparecem na lista.
urlURL pública quando disponível.
planPlano selecionado.
payment_modeO preview usa cobrança hourly.
exposureExposição configurada para o ambiente.
inherit_envsSempre true: os ENVs do projeto principal são herdados. Nunca contém os valores.
updated_at e expires_atAtualizaçã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/:previewKey

Cria ou atualiza o preview identificado por previewKey. O corpo de um upsert aceita:

CampoTipoObrigatórioDescrição
imagestringSim no upsertReferência da imagem pronta a publicar (até 2.048 caracteres).
inherit_envsbooleanNãoCampo de compatibilidade legada; aceita somente true, porque o Preview sempre herda os ENVs do usuário dentro da Zenifra. false é rejeitado com 400.
ttl_hoursintegerNãoDuração em horas; default 24, mínimo 1, máximo 168.
sourceobjectNãoMetadados 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.read ou project.image.update), senão 403 (PREVIEW_ENV_ACCESS_REQUIRED);
  • o projeto principal existe e está em execução ou parado, senão 404 (PARENT_PROJECT_NOT_FOUND) ou 409 (PROJECT_STATE_CONFLICT ou PROJECT_OPERATION_IN_PROGRESS); tipos de projeto sem previews recebem 422 (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 free não oferece), senão 400 (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_OWNED ou FORGEJO_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/:previewKey

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

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

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

  • accepted
  • reserving
  • provisioning
  • updating
  • available
  • deleting
  • deleted
  • failed

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:

StatusCódigoQuando ocorre
400preview_disabledOs 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
400preview_plan_not_allowedO plano do projeto principal não oferece previews (por exemplo, o plano free)
400preview_ttl_invalid, preview_ttl_exceeds_limit, preview_limit_invalidDuração ou limite fora do permitido
400forgejo_preview_unavailablePreviews nativos do Forgejo não estão disponíveis para o repositório do projeto
402ORGANIZATION_BILLING_BLOCKEDA organização tem pagamentos pendentes; regularize-os e repita a operação
403PREVIEW_ENV_ACCESS_REQUIREDA credencial não tem project.env.read nem project.image.update no projeto principal
404PARENT_PROJECT_NOT_FOUNDO projeto principal não existe ou foi excluído
404PREVIEW_NOT_FOUNDDelete de uma chave inexistente ou de um preview já removido
409PROJECT_STATE_CONFLICT, PROJECT_OPERATION_IN_PROGRESSO projeto principal não está em execução nem parado, ou tem outra operação em andamento
409GITHUB_PREVIEW_KEY_OWNED, FORGEJO_PREVIEW_KEY_OWNEDA chave pertence a um preview nativo de pull request (mensagem The preview is managed by another workflow)
422IMAGE_NOT_ALLOWEDA imagem informada não pode ser usada neste projeto
422PROJECT_OPERATION_NOT_SUPPORTEDO tipo do projeto principal não oferece previews
409PARENT_CONFIGURATION_UNAVAILABLEO projeto principal não tem configuração pública suficiente para aplicar o preview
409PREVIEW_OPERATION_CONFLICTJá existe uma operação ativa e incompatível para a mesma identidade
409PREVIEW_ORGANIZATION_LIMIT_REACHED, PREVIEW_PROJECT_LIMIT_REACHEDO limite efetivo da organização ou do projeto foi atingido
502PREVIEW_PROVISIONING_FAILEDO preview não pôde ser provisionado
503ORGANIZATION_RUNTIME_UNAVAILABLEO 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

Nessa página