Criar, listar e remover projetos
Esta página cobre o ciclo básico de um projeto pela API: consultar os catálogos públicos de planos, criar o projeto com POST /v1/project, listá-lo e removê-lo com DELETE /v1/project/:id. Para consultar ou alterar um projeto existente, veja Informações de projetos.
Autenticação e permissões
Criação, listagem e remoção aceitam uma API Key da organização (x-api-key: znf_... ou Authorization: Bearer znf_...) ou um token de usuário com x-organization-id. Os catálogos de planos são públicos e não exigem autenticação.
| Operação | Scope mínimo |
|---|---|
| Criar aplicação HTTP ou Job agendado | project.create em organization:* |
| Criar PostgreSQL, MariaDB ou ClickHouse | database.create em organization:* |
| Criar Valkey (Key-Value, Cache ou Queue) | managed_service.create em organization:* |
| Listar projetos | Nenhum scope extra; a lista traz apenas os projetos que a credencial pode ler |
| Remover projeto | project.delete em project:<project-id> |
owner tem acesso completo. Sem o scope correspondente ao tipo informado em config.type_project, a API responde 403 com insufficient organization permission.
Catálogos públicos
Consulte os catálogos antes de criar um projeto: eles informam os planos aceitos, os preços vigentes e o que cada plano inclui. Os valores monetários são números em centavos de BRL. Não fixe preços em automações.
| Rota | Conteúdo | Limite |
|---|---|---|
GET /v1/project/plans | Planos de aplicações HTTP | 50 por minuto |
GET /v1/project/job/plans | Planos de Jobs agendados | 60 por minuto |
GET /v1/project/storage/plans | Preço do armazenamento | 50 por minuto |
GET /v1/project/database/plans | Planos de bancos e capacidade por mecanismo | 10 por minuto |
GET /v1/project/database/catalog | Mecanismos de banco e campos de configuração | 100 por minuto |
Planos de aplicações HTTP
GET /v1/project/plans{
"status": "success",
"message": "get plans with success",
"data": [
{
"plan": "premium",
"prices": { "hourly": 0, "monthly": 0, "yearly": 0 },
"features": ["..."],
"permissions": { },
"capabilities": {
"logs": true,
"metrics": true,
"healthcheck": true,
"autoscaling": true,
"custom_subdomain": true,
"network_access": true
}
}
]
}Os preços do exemplo são ilustrativos; leia os valores da resposta. A lista vem ordenada pelo preço por hora, e os planos gratuitos retornam preços 0. O objeto capabilities indica, com valores booleanos, o que o plano inclui. logs é sempre true, porque os logs da aplicação estão disponíveis em todos os planos; os demais campos indicam se o plano inclui métricas, health check, auto-scaling, subdomínio personalizado e controle de acesso de rede. Prefira capabilities para decidir o que oferecer em integrações.
Planos de Jobs agendados
GET /v1/project/job/plans retorna os planos job-* com price_per_minute, currency, payment_mode: "per_minute", features e permissions. Quando os Jobs estão temporariamente indisponíveis, a rota responde 503 com SCHEDULED_JOBS_UNAVAILABLE. Detalhes em Jobs agendados.
Armazenamento
GET /v1/project/storage/plans retorna uma entrada por tipo de armazenamento:
{
"status": "success",
"message": "get storage prices with success",
"data": [
{
"storage": "1gb_persistente",
"prices": { "hourly": 0, "monthly": 0, "yearly": 0 },
"persistent": true
}
]
}Os preços se referem a 1 GB. O valor por hora corresponde ao valor mensal dividido por 720, e o anual a 12 vezes o mensal.
Planos de bancos de dados
GET /v1/project/database/plans retorna os planos db-* (PostgreSQL, MariaDB e Key-Value) e analytics-* (ClickHouse). Cada item traz plan, prices (hourly, monthly, yearly), features e engines, com a capacidade aceita por mecanismo:
{
"plan": "db-basic",
"prices": { "hourly": 0, "monthly": 0, "yearly": 0 },
"features": ["..."],
"engines": {
"postgresql": {
"instances": { "min": 1, "default": 1, "max": 3, "editable": true },
"storage": { "min": 1, "default": 1, "max": 250, "editable": true, "included": 0, "step": 1 },
"versions": ["15", "16", "17", "18"],
"default_version": "18",
"billing": { "instances": "per_instance", "storage": "per_instance_gb" }
}
}
}Os números do exemplo são ilustrativos. Se o catálogo estiver indisponível, a rota responde 503 com database catalog is temporarily unavailable.
Catálogo de mecanismos de banco
GET /v1/project/database/catalog organiza as mesmas informações por mecanismo. Cada item traz id, name, versions, plans (com id, payment_modes e capability), configuration_fields (versão, instâncias e capacidade em GB; os limites de cada plano ficam em plans[].capability) e connection_fields, que descreve os dados de conexão entregues pelo produto. A resposta usa o envelope { "status": "success", "data": [...] } e pode responder 503 nas mesmas condições do catálogo de planos.
Criar um projeto
POST /v1/project| Header | Obrigatório | Descrição |
|---|---|---|
x-api-key ou Authorization | Sim | API Key da organização ou token de usuário |
x-organization-id | Com token de usuário | Organização ativa |
Content-Type: application/json | Sim | Formato do corpo |
Idempotency-Key | Obrigatório para Valkey | 16 a 200 caracteres entre letras, números, ., _ e - |
Limite: 10 requisições por minuto.
Campos comuns
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | Sim | 6 a 32 caracteres, letras minúsculas, números e hífens entre eles |
description | string | Não | 1 a 256 caracteres |
plan | string | Sim | Plano compatível com o tipo do projeto (veja os catálogos) |
payment_mode | string | Sim | hourly, monthly ou yearly; Jobs usam somente per_minute |
config | object | Sim | Configuração por tipo; config.type_project é http, postgresql, mariadb, clickhouse, valkey ou job |
Os números do corpo devem ser enviados como números JSON, não como strings. Campos que não pertencem ao tipo informado são rejeitados com 400.
Aplicação HTTP a partir de imagem
Campo de config | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type_project | string | Sim | http |
exposure | string | Sim | public ou private |
image.url | string | Sim | Referência da imagem, 8 a 256 caracteres, sem http:// ou https:// |
image.is_public | boolean | Sim | false exige image.authentication |
image.authentication | object | Para imagem privada | auth_type: "username_password" com username e token, ou auth_type: "aws" com aws.access_key_id, aws.secret_access_key, aws.region e aws.account_id |
port | integer | Sim | Porta em que a aplicação escuta |
instances | number | Sim | Quantidade inicial de instâncias |
storage.persistent | boolean | Sim | Se o armazenamento é persistente |
storage.capacity | number | Sim | 1 a 250 GB; no plano free, exatamente 1 |
storage.dir_path_to_persist | string | Com persistent: true | Caminho absoluto persistido, até 80 caracteres |
envs | array | Sim | Até 50 itens { "name", "value" }; name com 1 a 120 caracteres e value com 1 a 32.760. Use [] para nenhuma |
network_access | object | Sim | ingress_white_list (1 a 10 itens) e ingress_black_list (até 10 itens), cada item com cidr e description. Veja Acesso de rede |
subdomain | string | Não | 3 a 54 caracteres; proibido com exposure: private |
custom_domains | array | Não | Até 20 domínios; deve ficar vazio com exposure: private |
healthcheck | object | Não | { "enabled": true, "path": "/health" }. Exige plano com capabilities.healthcheck: true. Veja Health check |
autoscaling | object | Não | enabled: true, max_instances maior ou igual a instances e alvos opcionais de CPU e memória de 1 a 100. Exige plano com capabilities.autoscaling: true |
curl -X POST "https://api.zenifra.com/v1/project" \
-H "x-api-key: znf_..." \
-H "Content-Type: application/json" \
-d '{
"name": "minha-api",
"plan": "basic",
"payment_mode": "hourly",
"config": {
"type_project": "http",
"exposure": "public",
"image": { "url": "ghcr.io/minha-org/minha-api:1.4.2", "is_public": true },
"port": 8080,
"instances": 1,
"storage": { "persistent": false, "capacity": 1 },
"envs": [{ "name": "NODE_ENV", "value": "production" }],
"network_access": {
"ingress_white_list": [{ "cidr": "0.0.0.0/0", "description": "Acesso público" }],
"ingress_black_list": []
}
}
}'{
"status": "success",
"message": "create a project with success",
"data": {
"project_id": "6650f1a2b3c4d5e6f7a8b9c0",
"domain": "<domínio-do-projeto>",
"type": "application"
}
}Para imagens em registros privados, veja Registro privado.
Aplicação HTTP a partir de um repositório Git
Substitua image por source e build, que devem ser enviados juntos. A conexão precisa existir antes; veja Conexões Git.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
source.connection_id | string | Sim | ID da conexão Git da organização |
source.repository_id | string | Sim | Repositório selecionado na conexão (até 1.024 caracteres) |
source.branch | string | Sim | Branch publicada (até 255 caracteres) |
source.auto_deploy | boolean | Sim | Publica a cada push na branch |
source.version_deploy | object | Não | enabled, event (tag ou release), tag_pattern e include_prereleases. Não pode ser habilitado junto com auto_deploy |
build.runtime | string | Não | Runtime do catálogo GET /v1/git/runtime-catalog |
build.version | string | Não | Versão aceita pelo runtime |
build.start_command, build.pre_build_command, build.build_command | string | Não | Até 256 caracteres |
build.dockerfile_path | string | Não | Até 256 caracteres |
build.context_path | string | Não | Diretório raiz do build, relativo à raiz do repositório. Padrão ".". Veja Diretório raiz do build |
{
"name": "minha-api",
"plan": "basic",
"payment_mode": "hourly",
"config": {
"type_project": "http",
"exposure": "public",
"source": {
"connection_id": "<connection-id>",
"repository_id": "<repository-id>",
"branch": "main",
"auto_deploy": true
},
"build": { "start_command": "npm start" },
"port": 3000,
"instances": 1,
"storage": { "persistent": false, "capacity": 1 },
"envs": [],
"network_access": {
"ingress_white_list": [{ "cidr": "0.0.0.0/0", "description": "Acesso público" }],
"ingress_black_list": []
}
}
}A resposta 201 inclui build_id do primeiro build, que você acompanha em Builds de repositórios Git. Para repositórios GitHub, a criação continua usando o campo github; source com a conexão GitHub responde 409 com GIT_SOURCE_CREATION_UNAVAILABLE. Em github, o campo opcional context_path segue as mesmas regras de build.context_path.
Diretório raiz do build
build.context_path (ou github.context_path na criação GitHub) define o diretório raiz do build: a pasta do repositório onde a instalação de dependências, o pre-build, o build e o start são executados. Use esse campo para publicar uma aplicação que está em uma subpasta de um monorepo.
- Opcional; o padrão é
".", a raiz do repositório. - O valor é normalizado: espaços nas pontas,
./no início e/no fim são removidos." ./apps/api/ "é salvo como"apps/api", e um valor vazio vira".". - Aceita somente caminhos relativos dentro do repositório, com
/como separador e até 256 caracteres. - Caminhos absolutos (
/app), segmentos..ou.(apps/../api), barras invertidas (\) e caracteres de controle são rejeitados com400. - Somente o conteúdo da pasta entra no build. Se ela não existir na branch publicada, o build falha informando que o diretório raiz configurado não existe no repositório.
- A pasta
.gitdo repositório não entra no build nem na aplicação publicada. Umcontext_pathapontando para.gité tratado como pasta inexistente.
{
"build": {
"start_command": "npm start",
"build_command": "npm run build",
"context_path": "apps/api"
}
}PostgreSQL, MariaDB e ClickHouse
Campo de config | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type_project | string | Sim | postgresql, mariadb ou clickhouse |
version | string | Sim | PostgreSQL: 15, 16, 17 ou 18. MariaDB: 10 ou 11. ClickHouse: 26.8.6.5 |
instances | number | Sim | PostgreSQL: 1 a 3 (no db-free, 1). MariaDB: exatamente 3. ClickHouse: conforme o catálogo do plano |
storage.persistent | boolean | Sim | Persistência dos dados |
storage.capacity | number | Sim | 1 a 250 GB; nos planos gratuitos, exatamente 1 |
envs | array | Sim | Mesmas regras da aplicação HTTP; use [] |
network_access | object | Sim | Mesmo formato da aplicação HTTP |
Use planos db-* para PostgreSQL e MariaDB (MariaDB não está disponível no db-free) e planos analytics-* para ClickHouse.
{
"name": "banco-pedidos",
"plan": "db-basic",
"payment_mode": "hourly",
"config": {
"type_project": "postgresql",
"version": "18",
"instances": 1,
"storage": { "persistent": true, "capacity": 10 },
"envs": [],
"network_access": {
"ingress_white_list": [{ "cidr": "203.0.113.0/24", "description": "Escritório" }],
"ingress_black_list": []
}
}
}{
"status": "success",
"message": "create a project with success",
"data": {
"project_id": "6650f1a2b3c4d5e6f7a8b9c1",
"type": "database",
"database": {
"connection": {
"host": "<host>",
"port": 5432,
"database": "<database>",
"username": "<username>",
"password": "<senha>",
"connectionString": "postgresql://<username>:<senha>@<host>:5432/<database>"
},
"version": "18",
"replicas": 1
}
}
}A senha só aparece na criação e na renovação de credenciais. Guarde-a em local seguro. Se os dados de conexão ainda não estiverem prontos, connection pode vir null; consulte depois GET /v1/project/:id/database/connection. Veja também Bancos de dados e ClickHouse.
Valkey: Key-Value, Cache e Queue
Campo de config | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type_project | string | Sim | valkey |
profile | string | Sim | key_value, cache ou queue |
version | string | Sim | 9.1.1 |
storage | object | Para key_value e queue | { "persistent": true, "capacity": 1-250 }; proibido em cache |
network_access | object | Não | Mesmo formato, com ingress_white_list de 0 a 10 itens |
O plano precisa combinar com o perfil: db-* para key_value, cache-* para cache e queue-* para queue. O header Idempotency-Key é obrigatório. Ao repetir a mesma requisição com a mesma chave, a API devolve o mesmo projeto sem criar outro; a mesma chave com outro corpo responde 409 com IDEMPOTENCY_KEY_CONFLICT.
curl -X POST "https://api.zenifra.com/v1/project" \
-H "x-api-key: znf_..." \
-H "Idempotency-Key: cache-sessoes-2026-10-08-001" \
-H "Content-Type: application/json" \
-d '{
"name": "cache-sessoes",
"plan": "cache-basic",
"payment_mode": "hourly",
"config": { "type_project": "valkey", "profile": "cache", "version": "9.1.1" }
}'A resposta 201 (message: "project created") traz project_id, status, type: "managed_service" e managed_service com engine, profile, version, username, host, port, tls e connection_string. A connection_string com a senha só aparece na primeira resposta; uma repetição com a mesma chave devolve os demais campos. Veja Serviços gerenciados, Cache e Filas.
Outros tipos
- Jobs agendados (
type_project: "job"): planosjob-*,payment_mode: "per_minute"econfig.job.cron. Veja Jobs agendados. - Aplicação a partir de template: corpo alternativo com
template_idetemplate_revision. Veja Templates.
Erros da criação
| Código | Situação |
|---|---|
400 | Corpo inválido, plano incompatível com o tipo ("plan" must match "config.type_project"), plano inexistente, versão não suportada, IDEMPOTENCY_KEY_REQUIRED em Valkey ou modo de cobrança inválido (PAYMENT_MODE_INVALID, SCHEDULED_JOB_PAYMENT_MODE_INVALID) |
402 | Organização bloqueada por pagamento pendente |
403 | Scope insuficiente para o tipo de projeto, ou plano sem health check (HEALTHCHECK_NOT_AVAILABLE_FOR_PLAN) ou sem auto-scaling |
409 | Limite de planos gratuitos atingido, capacidade indisponível (INSUFFICIENT_PLAN_CAPACITY), domínio indisponível, conflito de idempotência ou conexão Git indisponível (GIT_CONNECTION_UNAVAILABLE), ou origem Git indisponível para criação direta (GIT_SOURCE_CREATION_UNAVAILABLE) |
429 | Limite de requisições excedido |
503 | Recurso temporariamente indisponível, como PLAN_CAPACITY_TEMPORARILY_UNAVAILABLE ou ORGANIZATION_RUNTIME_UNAVAILABLE; tente novamente |
Limites do plano Free
Quando a criação esbarra em um limite gratuito, a resposta 409 traz code e limit, além de message:
code | Campos extras | Situação |
|---|---|---|
FREE_PLAN_INSTANCE_LIMIT_REACHED | limit: 2 | A organização já usa as 2 instâncias do plano free |
FREE_PLAN_INSTANCE_LIMIT_EXCEEDED | limit: 2, remaining | As instâncias pedidas passam do que ainda resta no plano free; remaining informa quantas ainda cabem |
DB_FREE_PROJECT_LIMIT_REACHED | limit: 2 | A organização já tem 2 projetos db-free ativos |
{
"status": "failed",
"code": "FREE_PLAN_INSTANCE_LIMIT_REACHED",
"limit": 2,
"message": "You have reached the limit of 2 instances on the free plan. Delete an existing project to create a new one."
}Prefira code para tratar o erro no seu código; message é um texto legível para pessoas.
Nos planos gratuitos, a organização pode ter no máximo 2 instâncias no plano free, 2 projetos no db-free (PostgreSQL e Key-Value somados) e 1 serviço por perfil nos planos cache-free e queue-free.
Listar projetos
GET /v1/projectAceita page (padrão 1), limit (1 a 50, padrão 15), type (http, postgresql, mariadb ou valkey), profile (somente com type=valkey), status, search (1 a 256 caracteres, busca por nome ou descrição), sort_by (updated_at ou created_at) e sort_order (asc ou desc). A resposta traz data.projects e data.pagination com page, limit, total e pages. Limite: 100 requisições por minuto. Os campos de cada projeto estão em Informações de projetos.
Remover um projeto
DELETE /v1/project/:idcurl -X DELETE "https://api.zenifra.com/v1/project/6650f1a2b3c4d5e6f7a8b9c0" \
-H "x-api-key: znf_..."{
"status": "success",
"message": "deleted the project with success"
}Projetos Valkey são removidos em segundo plano: a resposta é 202 com { "project_id": "...", "status": "deleting" }. A remoção é definitiva; faça backup dos dados antes. Limite: 10 requisições por minuto.
| Código | Situação |
|---|---|
404 | Projeto não encontrado na organização |
409 | Projeto já removido, execuções de Job ainda em finalização (JOB_RUNS_PENDING) ou operação do serviço gerenciado em andamento |
503 | Falha temporária ao liberar o domínio; tente novamente |
Próximos passos
- Consulte e altere o projeto em Informações de projetos.
- Opere bancos em Bancos de dados pela API.
- Ajuste o ciclo de vida e as instâncias.
- Acompanhe custos em Cobrança do projeto.
Última atualização em
API Zenifra
Referência da API REST da Zenifra para criar e operar projetos com API Key da organização, permissões por recurso, limites e códigos de resposta.
Informações do projeto
Liste e filtre projetos por tipo, status e perfil Valkey, consulte detalhes e atualize nome, descrição e exposição pela API Zenifra.