Zenifra API
The Zenifra API lets you automate everything you do in the console: create and remove projects, publish new versions, configure variables, domain, instances, and auto-scaling, follow builds, logs, and metrics, and operate databases, managed services, scheduled Jobs, and preview environments.
This page covers what applies to every route: authentication, permissions, response format, asynchronous operations, rate limits, and status codes. Each reference page describes the fields, permissions, and errors specific to its endpoints.
Base URL
All routes use the /v1 prefix:
https://api.zenifra.com/v1Send JSON bodies with Content-Type: application/json.
Authentication
Organization API key (recommended for automation)
Create the key in Organization → API Keys in the console. It belongs to the organization, grants only the scopes you choose, and can have an expiration date and an allowed IP list. The secret starts with znf_ and is shown only once, when the key is created. See Organization API Keys.
Send the key in one of these headers:
-H "x-api-key: znf_..."
# or
-H "Authorization: Bearer znf_..."The organization is identified by the key itself, so the x-organization-id header is not needed for these calls.
User session token
Integrations that act on behalf of a person use Authorization: Bearer <token> and must send the active organization in x-organization-id. Without this header, the API responds 400 or 403, depending on the route.
Some routes only accept a user token, such as creating and revoking Git connections (owner session required), preview environment settings, and member management. The pages for those routes state this restriction.
Legacy project keys
Older keys bound to a single project are still accepted only to update the published image (Update image) and to operate preview environments with the opt-in enabled. For any other route, use an organization API key.
Headers
| Header | When to use | Description |
|---|---|---|
x-api-key or Authorization: Bearer znf_... | Automation | Organization API key |
Authorization: Bearer <token> | User session | Login token |
x-organization-id | Required with a user token | Active organization. Ignored when the credential is an organization API key |
Content-Type: application/json | Requests with a body | Payload format |
Idempotency-Key | Routes that accept it | Avoids duplicate operations when a request is retried; see below |
Permissions
An owner session has full access. assistant and member users and API keys need the action scope on the right resource, either by specific ID or with * for every resource of that type:
| Task | Minimum scope |
|---|---|
| Create an HTTP project or scheduled Job | project.create on organization:* |
| Create a database (PostgreSQL, MariaDB, or Analytics) | database.create on organization:* |
| Create Key-Value, Cache, or Queue (Valkey) | managed_service.create on organization:* |
| View a project | project.read on project:<project-id> |
| Metrics and storage usage | project.metrics.read on project:<project-id> |
| Logs and builds | project.logs.read on project:<project-id> |
| Publish a new image | project.image.update on project:<project-id> |
| Remove a project | project.delete on project:<project-id> |
The complete scope catalog is in Organization API Keys. An API key used from an IP outside its allowed list receives 403 with the code API_KEY_IP_NOT_ALLOWED; an expired or revoked key receives 401.
Response format
Responses use a JSON envelope with status:
{
"status": "success",
"data": { }
}Errors return status: "failed" with a readable message and, when available, a stable code to handle in your code:
{
"status": "failed",
"code": "PROJECT_MUST_BE_RUNNING",
"message": "project must be running to update autoscaling"
}Use the code (when present) and the HTTP status to decide what to do; do not depend on the exact message text.
On most routes the body is validated before authentication. Because of that, an invalid payload returns 400 even when the credential is also missing.
Asynchronous operations and idempotency
Long-running operations, such as builds, previews, and managed service credential rotation, respond 202 Accepted with an operation identifier. Template deployments respond 201 with the deployment state. Check the state on the route indicated by the resource page until the operation finishes.
Routes that accept the Idempotency-Key header use it to return the same operation when the request is repeated, without creating a second resource. Generate a unique value per operation (for example, a UUID) and reuse it only when retrying exactly the same request.
Rate limits
Each route has its own limit, counted per source IP address, method, and route. Reads usually allow more calls per minute than writes, and sensitive operations, such as plan or exposure changes, have lower limits. Each resource page lists the relevant limits.
When the limit is exceeded, the API responds 429 Too Many Requests with a message that says how many seconds to wait. Implement retries with increasing backoff.
HTTP status codes
| Code | Meaning |
|---|---|
200 | Request succeeded |
201 | Resource created |
202 | Operation accepted and processed in the background |
400 | Invalid data, missing required header, or operation not supported for the project type |
401 | Missing, invalid, expired, or revoked credential; on some older routes, plan without the feature |
402 | Plan without metrics, or organization with a pending payment |
403 | Insufficient permission, resource outside the credential scope, IP not allowed, or plan without the feature (e.g. HEALTHCHECK_NOT_AVAILABLE_FOR_PLAN) |
404 | Resource not found (project, organization, build, preview, run) |
409 | Conflict with the current state, such as an operation in progress, a stopped project, or no capacity available on the plan |
429 | Rate limit exceeded or change in a cooldown period |
500 | Internal error |
502 | A dependent step of the operation failed |
503 | Feature temporarily unavailable; try again later |
Endpoints by area
Projects
| Task | Page | Main endpoints |
|---|---|---|
| Create, list, and remove projects; read plans | Projects | POST /project, GET /project, DELETE /project/:id, GET /project/plans |
| Read project, name, description, and exposure | Project Information | GET /project/:id, PATCH /project/:id/name, PATCH /project/:id/exposure |
| Stop or resume | Lifecycle | PATCH /project/:id/stop, PATCH /project/:id/resume |
| Change plan and renewal | Plan | PATCH /project/:id/plan |
| Scale instances | Instances | GET /project/:id/instances, PATCH /project/:id/instances |
| Configure auto-scaling | Auto-scaling | PATCH /project/:id/autoscaling |
| Configure health checks | Health check | GET /project/:id/healthcheck, PATCH /project/:id/healthcheck |
| Email warnings | Alerts | GET /project/:id/alerts, PATCH /project/:id/alerts |
| Environment variables | Environment Variables | GET /project/:id/envs, PATCH /project/:id/envs |
| Storage | Storage | PATCH /project/:id/storage/size, GET /project/:id/storage/usage |
| Subdomain and custom domains | Domain | PATCH /project/:id/domain, PATCH /project/:id/custom-domains |
| Restrict access by IP | Network access | PATCH /project/:id/network-access |
| Project costs | Project billing | GET /project/:id/billing/hourly-usage, GET /project/:id/billing/ledger |
| Scheduled Jobs | Scheduled Jobs | PATCH /project/:id/schedule, GET /project/:id/job-runs |
Deploy
| Task | Page | Main endpoints |
|---|---|---|
| Publish an image | Update Deploy Image | PATCH /project/:id/image |
| Publication history | Deployments | GET /project/:id/historic/deployments |
| Connect repositories and configure the source | Git connections and sources | /git/providers, /git/connections, /project/:id/source |
| Trigger and follow builds | Git repository builds | POST /project/:id/deploy, GET /project/:id/builds, GET /project/:id/builds/:buildId/logs |
| Preview environments | Preview Environments | PUT /project/:id/preview-environments/:previewKey |
| Templates | Templates | GET /templates, POST /templates/:id/deployments |
The /project/:id/github/* routes remain available for older GitHub projects. For new integrations, use the /project/:id/deploy and /project/:id/builds routes, which work with any Git provider.
Data and services
| Task | Page | Main endpoints |
|---|---|---|
| Database status, connection, password, and version | Databases | GET /project/:id/database/status, GET /project/:id/database/connection |
| Key-Value, Cache, and Queue (Valkey) | Managed services | GET /managed-services/:id/connection, PATCH /managed-services/:id/credentials |
Observability
| Task | Page | Main endpoints |
|---|---|---|
| Metrics and logs | Metrics and Logs | GET /project/:id/metrics, GET /project/:id/logs |
| HTTP traffic | Network Metrics | GET /project/:id/metrics/network/* |
Examples
cURL
curl "https://api.zenifra.com/v1/project/507f1f77bcf86cd799439011" \
-H "x-api-key: $ZENIFRA_API_KEY"Python
import os
import requests
BASE_URL = "https://api.zenifra.com/v1"
headers = {"x-api-key": os.environ["ZENIFRA_API_KEY"]}
response = requests.get(
f"{BASE_URL}/project/507f1f77bcf86cd799439011/metrics",
headers=headers,
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Node.js
const BASE_URL = 'https://api.zenifra.com/v1'
async function getMetrics(projectId) {
const response = await fetch(`${BASE_URL}/project/${projectId}/metrics`, {
headers: { 'x-api-key': process.env.ZENIFRA_API_KEY },
})
if (!response.ok) {
throw new Error(`Zenifra API ${response.status}: ${await response.text()}`)
}
return (await response.json()).data
}
getMetrics('507f1f77bcf86cd799439011').then(console.log)Store the API key in an environment variable or secret manager; never put it in source code or frontend applications.
FAQ
Where do I find the API key and the project ID?
The API key is created in Organization → API Keys; the secret is shown only once, so store it securely when you create it. If you lose the secret, revoke the key and create another one. The project ID is shown in the console, in the project page URL, and in the GET /v1/project response.
Does one API key work for every project?
It works for the resources and actions you choose when creating it. Use the specific project ID to limit the key to one project, or * for all current and future projects in the organization. Prefer keys with the fewest scopes, separated by environment or integration.
Is x-organization-id always required?
No. With an organization API key, the organization comes from the key itself. With a user token, the header is required.
Is there a rate limit?
Yes. Limits vary by route and are counted per source IP. When you receive 429, wait for the time shown in the response before retrying. For high-volume automation, contact support.
Can I use the CLI or MCP instead of the API?
Yes. The CLI and the MCP server use the same API and the same permissions, and are the fastest path for scripts and assistants.
Last updated on