Preview Environments — API

Use these endpoints to configure and operate Preview Environments nested under a primary HTTP project. A preview has its own identity, URL, capacity, storage, and billing lifecycle, but it can only be accessed through the primary project it belongs to.

The operation is idempotent by organization, primary project, and previewKey. PUT and DELETE may return an asynchronous operation; query the operation endpoint until it reaches a terminal state.

Authentication and security

Include a credential with the corresponding project permission and organization context:

HeaderRequiredDescription
x-api-keyAction operationsPrimary project API Key kept in a secret.
x-organization-idOrganization-token requestsActive project organization. The Action identifies the organization from the authenticated project.
Content-Type: application/jsonFor requests with a bodyIndicates a JSON body.

The owner must enable Preview Environments in the console before a project API Key can operate previews. The key stays limited to the primary project and its configured guardrails: it cannot change settings, entitlements, limits, or disallowed plans.

Public responses never include environment variables, credentials, private image URLs, resource names, or internal operational details. Treat the API Key as a secret and never log it.

Base URL and identifiers

https://api.zenifra.com/v1

In the examples, PROJECT_ID represents the primary HTTP project ID and API_KEY represents an API Key kept in a secret. Replace only these placeholders with values from your organization.

export PROJECT_ID="your-project-id"
export API_KEY="your-api-key"
export USER_TOKEN="your-user-token"
export ORGANIZATION_ID="your-organization-id"
export BASE_URL="https://api.zenifra.com/v1"

previewKey may contain only letters, numbers, periods, underscores, and hyphens within the API's size limit. Do not silently alter a rejected key.

Endpoints

GoalMethod and path
Read settingsGET /project/:projectId/preview-environments/settings
Update settingsPATCH /project/:projectId/preview-environments/settings
Read inherited planGET /project/:projectId/preview-environments/plans
Read preview costsGET /project/:projectId/preview-environments/billing
List previewsGET /project/:projectId/preview-environments
Create or update a previewPUT /project/:projectId/preview-environments/:previewKey
Read a previewGET /project/:projectId/preview-environments/:previewKey
Remove a previewDELETE /project/:projectId/preview-environments/:previewKey
Read an operationGET /project/:projectId/preview-environments/:previewKey/operations/:operationId

Project settings

Read settings

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

Returns the primary project's opt-in and effective guardrails. The public response includes enabled, the project's inherited plan in default_plan/allowed_plans, default_ttl_hours, max_ttl_hours, and max_active_environments. A Preview cannot select a different plan from the primary project.

curl -sS "$BASE_URL/project/$PROJECT_ID/preview-environments/settings" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "x-organization-id: $ORGANIZATION_ID"

Example response:

{
  "data": {
    "enabled": true,
    "default_plan": "basic",
    "allowed_plans": ["basic"],
    "default_ttl_hours": 24,
    "max_ttl_hours": 168,
    "max_active_environments": 5
  }
}

Update settings

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

Requires project configuration permission. The API does not allow changing the organization entitlement or exceeding the effective limit.

{
  "enabled": true,
  "default_ttl_hours": 24,
  "max_ttl_hours": 168,
  "max_active_environments": 5
}

default_ttl_hours and max_ttl_hours must be between 1 and 168, and the default cannot be greater than the maximum. The hourly price always comes from the primary project's plan.

Preview costs

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

Requires project.billing.read. Returns the accrued cost of active Previews, the current hourly rate, and an estimate until expiration for each environment and for the group.

Monetary values use BRL minor units, matching the existing billing contract. For example, 5.4 means R$ 0.054. accrued_amount includes hourly snapshots calculated through as_of; pending_amount identifies value awaiting settlement. estimated_until_expiration is an estimate, not a final charge, and does not include the current hour until the hourly usage job closes it.

{
  "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
    }
  }
}

Plans

List preview plans

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

Returns only active plans allowed for previews in this project. Each item provides the plan identifier, public description, hourly price, and currency (BRL). The preview default and common plans appear only when explicitly allowed for this use.

curl -sS "$BASE_URL/project/$PROJECT_ID/preview-environments/plans" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "x-organization-id: $ORGANIZATION_ID"

The catalog price is the source of truth. Do not copy commercial values into the Action or a client application.

List previews

GET /project/:projectId/preview-environments

Lists previews nested under the primary project. Use the page and limit parameters for pagination.

curl -sS "$BASE_URL/project/$PROJECT_ID/preview-environments?page=1&limit=20" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "x-organization-id: $ORGANIZATION_ID"

Each item may contain these public fields:

FieldDescription
idPublic preview identifier.
keyStable key identifying the environment in the project.
statusProduct state such as accepted, provisioning, available, deleting, deleted, or failed.
urlPublic URL when available.
planSelected plan.
payment_modeThe preview uses hourly billing.
exposureExposure configured for the environment.
inherit_envsAlways true: variables from the primary project are inherited. Values are never returned.
updated_at and expires_atMost recent update and current expiration.

The list does not return environment variables, data, credentials, hashes, leases, counters, or resource names.

Create or update a preview

PUT /project/:projectId/preview-environments/:previewKey

Creates or updates the preview identified by previewKey. An upsert body accepts:

FieldTypeRequiredDescription
imagestringYes for upsertReference to the ready-to-use image to publish.
inherit_envsbooleanNoLegacy compatibility field; the Preview always inherits user variables inside Zenifra.
ttl_hoursintegerNoDuration in hours; default 24, minimum 1, maximum 168.

The preview always uses the primary project's plan and synchronizes its port, exposure, and access rules. Storage is new, empty, and isolated. Data, custom domains, and custom image startup commands are not inherited.

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
  }'

The first acceptance normally returns 202 Accepted with id, key, operation_id, and the current state. An idempotent replay that already finished may return 200. If an active operation has an incompatible payload, the API returns 409 and you should query the existing operation.

Read a preview

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

Returns the current state, URL, plan, hourly BRL price when available, inherited environment state, timestamps, and current operation. It does not return environment variable values.

curl -sS \
  "$BASE_URL/project/$PROJECT_ID/preview-environments/pr-42" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "x-organization-id: $ORGANIZATION_ID"

Remove a preview

DELETE /project/:projectId/preview-environments/:previewKey

Requests immediate removal of the preview and ends its billing when removal is confirmed. The operation is idempotent: repeating the delete does not create another environment or another charge.

curl -sS -X DELETE \
  "$BASE_URL/project/$PROJECT_ID/preview-environments/pr-42" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "x-organization-id: $ORGANIZATION_ID"

The first request may return 202 Accepted with operation_id. When the preview is already removed, a repeat returns the terminal state or an equivalent success response. The Action does not require IMAGE for this path.

Operation polling

Read an operation

GET /project/:projectId/preview-environments/:previewKey/operations/:operationId

Use the operation_id returned by PUT or DELETE to follow the operation. Poll at bounded intervals and stop at a terminal state.

Possible states:

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

An upsert succeeds at available; a delete succeeds at deleted. At failed, read the public code and correct the input before trying again.

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"

The operation response contains only state, public identifiers, timestamps, the URL when available, expires_at, and a sanitized public error. It does not contain secrets or internal details.

Public errors

Domain codes stay stable and messages are actionable:

CodeMeaning
preview_not_enabledOpt-in is not enabled for the primary project.
invalid_preview_keyThe key does not meet the allowed format or size.
preview_plan_not_allowedThe plan is not in the allowed list.
preview_ttl_invalid or preview_ttl_exceeds_limitUse a TTL between 1h and 168h and within the project limit.
preview_organization_limit_reached or preview_project_limit_reachedThe effective project or organization limit was reached.
parent_configuration_unavailableThe primary project lacks enough public configuration to apply the preview.
preview_operation_in_progressAn active operation already exists for the same identity.
preview_unavailableThe preview could not become available with the provided input.
preview_wait_timeoutThe Action reached its maximum wait time.

In addition to the code, the API may return a message and a request identifier for support. Never expose the full response in public logs when it is part of an automation.

HTTP status codes

  • 200: successful read or replay of an operation that already finished.
  • 202: upsert or delete accepted for processing.
  • 400: invalid body, key, action, or duration.
  • 401: missing or invalid credential.
  • 403: insufficient organization, project, or permission; opt-in must also be active for API Key mutations.
  • 404: primary project, preview, or operation not found.
  • 409: conflict with an active operation or incompatible payload.

Next steps

On this page