# Variáveis (referência)

## GET `/v1/projects/{p}/shared-variables`

Lista as variáveis compartilhadas do projeto (chegam a um serviço só por referência ${{shared.NOME}})

Ordenadas por nome. Valor de variável **sensível** vem `null` (com `sensitive=true`) a não ser que o token tenha `env:reveal`.

Escopo exigido: `env:read`. Com `env:reveal`: valores sensíveis em claro.

Escopo exigido: `env:read` · opcional (muda a resposta): `env:reveal` · operationId: `listSharedVariables`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |
| `cursor` | query | string | não | Cursor opaco devolvido em `next_cursor` da página anterior. |
| `limit` | query | integer | não | Itens por página (1 a 200). |

### Resposta 200

Página (mais novo/antigo primeiro conforme o recurso).

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `next_cursor` | string\|null | sim | Passe em `cursor` para a próxima página; `null` ao fim. |
| `has_more` | boolean | sim | Há mais páginas. |
| `items` | array<object> | sim |  |
| `items[].name` | string | sim |  |
| `items[].value` | string\|null | sim | `null` quando a variável é sensível e o token não tem `env:reveal`. |
| `items[].sensitive` | boolean | sim | Flag declarado: o valor foi marcado como sensível. Governa o mascaramento do valor e a regra de nunca rebaixar. |
| `items[].effective_sensitive` | boolean | não | `true` quando a variável é sensível **ou** o valor referencia (`${{svc.VAR}}`) uma variável sensível (p.ex. `${{db.DATABASE_URL}}`): o valor resolvido herda a sensibilidade. Sempre enviado. O flag `sensitive` não muda por herança (continua o que o usuário declarou); o texto da referência é devolvido como está, e o valor herdado só sai em `?resolved=true`, que exige `env:reveal`. Referência que não resolve ou variável compartilhada: igual a `sensitive`. |

```json
{
  "items": [
    {
      "name": "LOG_LEVEL",
      "value": "info",
      "sensitive": false
    },
    {
      "name": "SESSION_SECRET",
      "value": null,
      "sensitive": true
    }
  ],
  "next_cursor": null,
  "has_more": false
}
```

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.

## PUT `/v1/projects/{p}/shared-variables`

Grava várias variáveis compartilhadas do projeto (chegam a um serviço só por referência ${{shared.NOME}})

Cria ou substitui de 1 a 200 variáveis, **tudo ou nada**. A resposta mascara os sensíveis e lista em `needs_redeploy` os serviços que precisam de novo deploy.

Escopo exigido: `env:write`.

Escopo exigido: `env:write` · operationId: `putManySharedVariables`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |

### Corpo (application/json)

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `variables` | array<object> | sim |  |
| `variables[].name` | string | sim |  |
| `variables[].value` | string | sim |  |
| `variables[].sensitive` | boolean | não |  |

### Resposta 200

Variáveis gravadas.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `items` | array<object> | sim |  |
| `items[].name` | string | sim |  |
| `items[].value` | string\|null | sim | `null` quando a variável é sensível e o token não tem `env:reveal`. |
| `items[].sensitive` | boolean | sim | Flag declarado: o valor foi marcado como sensível. Governa o mascaramento do valor e a regra de nunca rebaixar. |
| `items[].effective_sensitive` | boolean | não | `true` quando a variável é sensível **ou** o valor referencia (`${{svc.VAR}}`) uma variável sensível (p.ex. `${{db.DATABASE_URL}}`): o valor resolvido herda a sensibilidade. Sempre enviado. O flag `sensitive` não muda por herança (continua o que o usuário declarou); o texto da referência é devolvido como está, e o valor herdado só sai em `?resolved=true`, que exige `env:reveal`. Referência que não resolve ou variável compartilhada: igual a `sensitive`. |
| `needs_redeploy` | array<string> | sim |  |

```json
{
  "items": [
    {
      "name": "LOG_LEVEL",
      "value": "info",
      "sensitive": false
    },
    {
      "name": "SESSION_SECRET",
      "value": null,
      "sensitive": true
    }
  ],
  "needs_redeploy": [
    "web"
  ]
}
```

### Resposta 409

`sensitive_lowered` (sensível não volta a não-sensível) ou `duplicate_variable`.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `error` | enum(unauthorized, forbidden, not_found, payload_too_large, quota_exceeded, unsupported_type, class_not_allowed, extension_mismatch, malware_detected, svg_unsafe, archive_encrypted, archive_bomb, archive_unsupported, rate_limited, unavailable, idempotency_conflict, invalid_request, unsupported_media_type, bad_json, too_large, authz_unavailable, database_unavailable, restore_not_removable, gallery_not_empty, bad_body, internal, name_taken, slug_taken, project_quota_exceeded, project_not_empty, deploy_in_progress, nothing_to_rollback, not_cancelable, sensitive_lowered, duplicate_variable, domain_taken, volume_exists, volume_in_use, volume_attached, backup_not_ready, unresolved_reference, reference_cycle, invalid_reference, reference_too_deep, value_too_large) | sim | Código estável para tratamento programático. A tabela completa está na descrição geral. |
| `message` | string | sim | Mensagem legível em pt-BR. |
| `details` | object | não | Presente em alguns `422` (ex.: `unresolved_reference` e `reference_cycle` trazem `details.references`). |
| `details.references` | array<string> | não | Referências problemáticas (só nomes, nunca valores). |
| `details.retry_after_seconds` | integer | não | Em `422 rate_limited` (ex.: verificação manual de domínio): segundos até poder tentar de novo. |

### Resposta 422

`quota_exceeded` (limite de variáveis).

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `error` | enum(unauthorized, forbidden, not_found, payload_too_large, quota_exceeded, unsupported_type, class_not_allowed, extension_mismatch, malware_detected, svg_unsafe, archive_encrypted, archive_bomb, archive_unsupported, rate_limited, unavailable, idempotency_conflict, invalid_request, unsupported_media_type, bad_json, too_large, authz_unavailable, database_unavailable, restore_not_removable, gallery_not_empty, bad_body, internal, name_taken, slug_taken, project_quota_exceeded, project_not_empty, deploy_in_progress, nothing_to_rollback, not_cancelable, sensitive_lowered, duplicate_variable, domain_taken, volume_exists, volume_in_use, volume_attached, backup_not_ready, unresolved_reference, reference_cycle, invalid_reference, reference_too_deep, value_too_large) | sim | Código estável para tratamento programático. A tabela completa está na descrição geral. |
| `message` | string | sim | Mensagem legível em pt-BR. |
| `details` | object | não | Presente em alguns `422` (ex.: `unresolved_reference` e `reference_cycle` trazem `details.references`). |
| `details.references` | array<string> | não | Referências problemáticas (só nomes, nunca valores). |
| `details.retry_after_seconds` | integer | não | Em `422 rate_limited` (ex.: verificação manual de domínio): segundos até poder tentar de novo. |

```json
{
  "error": "quota_exceeded",
  "message": "Limite de serviços do plano atingido."
}
```

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `413` Corpo JSON acima do limite da rota (`too_large`); `415` O corpo precisa ser `application/json` (`unsupported_media_type`); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.

## GET `/v1/projects/{p}/shared-variables/{name}`

Lê uma variável

Valor sensível vem `null` sem `env:reveal`.

Escopo exigido: `env:read`. Com `env:reveal`: valor sensível em claro.

Escopo exigido: `env:read` · opcional (muda a resposta): `env:reveal` · operationId: `getSharedVariable`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |
| `name` | path | string | sim | Nome da variável (`^[A-Za-z_][A-Za-z0-9_]{0,255}$`). |

### Resposta 200

Variável.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `name` | string | sim |  |
| `value` | string\|null | sim | `null` quando a variável é sensível e o token não tem `env:reveal`. |
| `sensitive` | boolean | sim | Flag declarado: o valor foi marcado como sensível. Governa o mascaramento do valor e a regra de nunca rebaixar. |
| `effective_sensitive` | boolean | não | `true` quando a variável é sensível **ou** o valor referencia (`${{svc.VAR}}`) uma variável sensível (p.ex. `${{db.DATABASE_URL}}`): o valor resolvido herda a sensibilidade. Sempre enviado. O flag `sensitive` não muda por herança (continua o que o usuário declarou); o texto da referência é devolvido como está, e o valor herdado só sai em `?resolved=true`, que exige `env:reveal`. Referência que não resolve ou variável compartilhada: igual a `sensitive`. |

```json
{
  "name": "LOG_LEVEL",
  "value": "info",
  "sensitive": false
}
```

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.

## PUT `/v1/projects/{p}/shared-variables/{name}`

Define uma variável

Cria ou substitui. **Nunca devolve** o valor de uma variável sensível. Referências `${{svc.VAR}}` são validadas na sintaxe.

Escopo exigido: `env:write`.

Escopo exigido: `env:write` · operationId: `putSharedVariable`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |
| `name` | path | string | sim | Nome da variável (`^[A-Za-z_][A-Za-z0-9_]{0,255}$`). |

### Corpo (application/json)

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `value` | string | sim |  |
| `sensitive` | boolean | não | Omitido mantém o valor atual (novas: `false`). Sensível nunca volta a não-sensível (`409 sensitive_lowered`). |

### Resposta 200

Variável gravada.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `name` | string | sim |  |
| `value` | string\|null | sim | `null` quando a variável é sensível e o token não tem `env:reveal`. |
| `sensitive` | boolean | sim | Flag declarado: o valor foi marcado como sensível. Governa o mascaramento do valor e a regra de nunca rebaixar. |
| `effective_sensitive` | boolean | não | `true` quando a variável é sensível **ou** o valor referencia (`${{svc.VAR}}`) uma variável sensível (p.ex. `${{db.DATABASE_URL}}`): o valor resolvido herda a sensibilidade. Sempre enviado. O flag `sensitive` não muda por herança (continua o que o usuário declarou); o texto da referência é devolvido como está, e o valor herdado só sai em `?resolved=true`, que exige `env:reveal`. Referência que não resolve ou variável compartilhada: igual a `sensitive`. |
| `needs_redeploy` | array<string> | sim | Serviços marcados para novo deploy. |

```json
{
  "name": "LOG_LEVEL",
  "value": "info",
  "sensitive": false,
  "needs_redeploy": [
    "web"
  ]
}
```

### Resposta 409

`sensitive_lowered`.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `error` | enum(unauthorized, forbidden, not_found, payload_too_large, quota_exceeded, unsupported_type, class_not_allowed, extension_mismatch, malware_detected, svg_unsafe, archive_encrypted, archive_bomb, archive_unsupported, rate_limited, unavailable, idempotency_conflict, invalid_request, unsupported_media_type, bad_json, too_large, authz_unavailable, database_unavailable, restore_not_removable, gallery_not_empty, bad_body, internal, name_taken, slug_taken, project_quota_exceeded, project_not_empty, deploy_in_progress, nothing_to_rollback, not_cancelable, sensitive_lowered, duplicate_variable, domain_taken, volume_exists, volume_in_use, volume_attached, backup_not_ready, unresolved_reference, reference_cycle, invalid_reference, reference_too_deep, value_too_large) | sim | Código estável para tratamento programático. A tabela completa está na descrição geral. |
| `message` | string | sim | Mensagem legível em pt-BR. |
| `details` | object | não | Presente em alguns `422` (ex.: `unresolved_reference` e `reference_cycle` trazem `details.references`). |
| `details.references` | array<string> | não | Referências problemáticas (só nomes, nunca valores). |
| `details.retry_after_seconds` | integer | não | Em `422 rate_limited` (ex.: verificação manual de domínio): segundos até poder tentar de novo. |

### Resposta 422

`quota_exceeded` (limite de variáveis).

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `error` | enum(unauthorized, forbidden, not_found, payload_too_large, quota_exceeded, unsupported_type, class_not_allowed, extension_mismatch, malware_detected, svg_unsafe, archive_encrypted, archive_bomb, archive_unsupported, rate_limited, unavailable, idempotency_conflict, invalid_request, unsupported_media_type, bad_json, too_large, authz_unavailable, database_unavailable, restore_not_removable, gallery_not_empty, bad_body, internal, name_taken, slug_taken, project_quota_exceeded, project_not_empty, deploy_in_progress, nothing_to_rollback, not_cancelable, sensitive_lowered, duplicate_variable, domain_taken, volume_exists, volume_in_use, volume_attached, backup_not_ready, unresolved_reference, reference_cycle, invalid_reference, reference_too_deep, value_too_large) | sim | Código estável para tratamento programático. A tabela completa está na descrição geral. |
| `message` | string | sim | Mensagem legível em pt-BR. |
| `details` | object | não | Presente em alguns `422` (ex.: `unresolved_reference` e `reference_cycle` trazem `details.references`). |
| `details.references` | array<string> | não | Referências problemáticas (só nomes, nunca valores). |
| `details.retry_after_seconds` | integer | não | Em `422 rate_limited` (ex.: verificação manual de domínio): segundos até poder tentar de novo. |

```json
{
  "error": "quota_exceeded",
  "message": "Limite de serviços do plano atingido."
}
```

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `413` Corpo JSON acima do limite da rota (`too_large`); `415` O corpo precisa ser `application/json` (`unsupported_media_type`); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.

## DELETE `/v1/projects/{p}/shared-variables/{name}`

Apaga uma variável

Remove a variável e marca os serviços afetados para novo deploy.

Escopo exigido: `env:write`.

Escopo exigido: `env:write` · operationId: `deleteSharedVariable`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |
| `name` | path | string | sim | Nome da variável (`^[A-Za-z_][A-Za-z0-9_]{0,255}$`). |

### Resposta 204

Removida.

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.

## GET `/v1/projects/{p}/services/{s}/variables`

Lista as variáveis do serviço

Ordenadas por nome. Valor de variável **sensível** vem `null` (com `sensitive=true`) a não ser que o token tenha `env:reveal`. Com `resolved=true` devolve o ambiente final (referências expandidas, variáveis automáticas e de banco).

Escopo exigido: `env:read`. Com `env:reveal`: valores sensíveis em claro; `resolved=true` exige este escopo (`403` sem ele).

Escopo exigido: `env:read` · opcional (muda a resposta): `env:reveal` · operationId: `listServiceVariables`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |
| `s` | path | string | sim | Id ou nome do serviço dentro do projeto. |
| `cursor` | query | string | não | Cursor opaco devolvido em `next_cursor` da página anterior. |
| `limit` | query | integer | não | Itens por página (1 a 200). |
| `resolved` | query | boolean | não | `true` devolve o ambiente final do serviço (referências expandidas, variáveis automáticas e de banco). Exige `env:reveal` (senão `403`). Só em variáveis de serviço. |

### Resposta 200

Página (mais novo/antigo primeiro conforme o recurso).

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `next_cursor` | string\|null | sim | Passe em `cursor` para a próxima página; `null` ao fim. |
| `has_more` | boolean | sim | Há mais páginas. |
| `items` | array<object> | sim |  |
| `items[].name` | string | sim |  |
| `items[].value` | string\|null | sim | `null` quando a variável é sensível e o token não tem `env:reveal`. |
| `items[].sensitive` | boolean | sim | Flag declarado: o valor foi marcado como sensível. Governa o mascaramento do valor e a regra de nunca rebaixar. |
| `items[].effective_sensitive` | boolean | não | `true` quando a variável é sensível **ou** o valor referencia (`${{svc.VAR}}`) uma variável sensível (p.ex. `${{db.DATABASE_URL}}`): o valor resolvido herda a sensibilidade. Sempre enviado. O flag `sensitive` não muda por herança (continua o que o usuário declarou); o texto da referência é devolvido como está, e o valor herdado só sai em `?resolved=true`, que exige `env:reveal`. Referência que não resolve ou variável compartilhada: igual a `sensitive`. |

```json
{
  "items": [
    {
      "name": "LOG_LEVEL",
      "value": "info",
      "sensitive": false
    },
    {
      "name": "SESSION_SECRET",
      "value": null,
      "sensitive": true
    }
  ],
  "next_cursor": null,
  "has_more": false
}
```

### Resposta 422

`resolved=true` com referências inválidas: `unresolved_reference`, `reference_cycle`, `invalid_reference`, `reference_too_deep` ou `value_too_large` (com `details`).

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `error` | enum(unauthorized, forbidden, not_found, payload_too_large, quota_exceeded, unsupported_type, class_not_allowed, extension_mismatch, malware_detected, svg_unsafe, archive_encrypted, archive_bomb, archive_unsupported, rate_limited, unavailable, idempotency_conflict, invalid_request, unsupported_media_type, bad_json, too_large, authz_unavailable, database_unavailable, restore_not_removable, gallery_not_empty, bad_body, internal, name_taken, slug_taken, project_quota_exceeded, project_not_empty, deploy_in_progress, nothing_to_rollback, not_cancelable, sensitive_lowered, duplicate_variable, domain_taken, volume_exists, volume_in_use, volume_attached, backup_not_ready, unresolved_reference, reference_cycle, invalid_reference, reference_too_deep, value_too_large) | sim | Código estável para tratamento programático. A tabela completa está na descrição geral. |
| `message` | string | sim | Mensagem legível em pt-BR. |
| `details` | object | não | Presente em alguns `422` (ex.: `unresolved_reference` e `reference_cycle` trazem `details.references`). |
| `details.references` | array<string> | não | Referências problemáticas (só nomes, nunca valores). |
| `details.retry_after_seconds` | integer | não | Em `422 rate_limited` (ex.: verificação manual de domínio): segundos até poder tentar de novo. |

```json
{
  "error": "unresolved_reference",
  "message": "Há referências para variáveis ou serviços que não existem.",
  "details": {
    "references": [
      "db.MYSQL_HOST"
    ]
  }
}
```

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.

## PUT `/v1/projects/{p}/services/{s}/variables`

Grava várias variáveis do serviço

Cria ou substitui de 1 a 200 variáveis, **tudo ou nada**. A resposta mascara os sensíveis e lista em `needs_redeploy` os serviços que precisam de novo deploy.

Escopo exigido: `env:write`.

Escopo exigido: `env:write` · operationId: `putManyServiceVariables`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |
| `s` | path | string | sim | Id ou nome do serviço dentro do projeto. |

### Corpo (application/json)

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `variables` | array<object> | sim |  |
| `variables[].name` | string | sim |  |
| `variables[].value` | string | sim |  |
| `variables[].sensitive` | boolean | não |  |

### Resposta 200

Variáveis gravadas.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `items` | array<object> | sim |  |
| `items[].name` | string | sim |  |
| `items[].value` | string\|null | sim | `null` quando a variável é sensível e o token não tem `env:reveal`. |
| `items[].sensitive` | boolean | sim | Flag declarado: o valor foi marcado como sensível. Governa o mascaramento do valor e a regra de nunca rebaixar. |
| `items[].effective_sensitive` | boolean | não | `true` quando a variável é sensível **ou** o valor referencia (`${{svc.VAR}}`) uma variável sensível (p.ex. `${{db.DATABASE_URL}}`): o valor resolvido herda a sensibilidade. Sempre enviado. O flag `sensitive` não muda por herança (continua o que o usuário declarou); o texto da referência é devolvido como está, e o valor herdado só sai em `?resolved=true`, que exige `env:reveal`. Referência que não resolve ou variável compartilhada: igual a `sensitive`. |
| `needs_redeploy` | array<string> | sim |  |

```json
{
  "items": [
    {
      "name": "LOG_LEVEL",
      "value": "info",
      "sensitive": false
    },
    {
      "name": "SESSION_SECRET",
      "value": null,
      "sensitive": true
    }
  ],
  "needs_redeploy": [
    "web"
  ]
}
```

### Resposta 409

`sensitive_lowered` (sensível não volta a não-sensível) ou `duplicate_variable`.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `error` | enum(unauthorized, forbidden, not_found, payload_too_large, quota_exceeded, unsupported_type, class_not_allowed, extension_mismatch, malware_detected, svg_unsafe, archive_encrypted, archive_bomb, archive_unsupported, rate_limited, unavailable, idempotency_conflict, invalid_request, unsupported_media_type, bad_json, too_large, authz_unavailable, database_unavailable, restore_not_removable, gallery_not_empty, bad_body, internal, name_taken, slug_taken, project_quota_exceeded, project_not_empty, deploy_in_progress, nothing_to_rollback, not_cancelable, sensitive_lowered, duplicate_variable, domain_taken, volume_exists, volume_in_use, volume_attached, backup_not_ready, unresolved_reference, reference_cycle, invalid_reference, reference_too_deep, value_too_large) | sim | Código estável para tratamento programático. A tabela completa está na descrição geral. |
| `message` | string | sim | Mensagem legível em pt-BR. |
| `details` | object | não | Presente em alguns `422` (ex.: `unresolved_reference` e `reference_cycle` trazem `details.references`). |
| `details.references` | array<string> | não | Referências problemáticas (só nomes, nunca valores). |
| `details.retry_after_seconds` | integer | não | Em `422 rate_limited` (ex.: verificação manual de domínio): segundos até poder tentar de novo. |

### Resposta 422

`quota_exceeded` (limite de variáveis).

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `error` | enum(unauthorized, forbidden, not_found, payload_too_large, quota_exceeded, unsupported_type, class_not_allowed, extension_mismatch, malware_detected, svg_unsafe, archive_encrypted, archive_bomb, archive_unsupported, rate_limited, unavailable, idempotency_conflict, invalid_request, unsupported_media_type, bad_json, too_large, authz_unavailable, database_unavailable, restore_not_removable, gallery_not_empty, bad_body, internal, name_taken, slug_taken, project_quota_exceeded, project_not_empty, deploy_in_progress, nothing_to_rollback, not_cancelable, sensitive_lowered, duplicate_variable, domain_taken, volume_exists, volume_in_use, volume_attached, backup_not_ready, unresolved_reference, reference_cycle, invalid_reference, reference_too_deep, value_too_large) | sim | Código estável para tratamento programático. A tabela completa está na descrição geral. |
| `message` | string | sim | Mensagem legível em pt-BR. |
| `details` | object | não | Presente em alguns `422` (ex.: `unresolved_reference` e `reference_cycle` trazem `details.references`). |
| `details.references` | array<string> | não | Referências problemáticas (só nomes, nunca valores). |
| `details.retry_after_seconds` | integer | não | Em `422 rate_limited` (ex.: verificação manual de domínio): segundos até poder tentar de novo. |

```json
{
  "error": "quota_exceeded",
  "message": "Limite de serviços do plano atingido."
}
```

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `413` Corpo JSON acima do limite da rota (`too_large`); `415` O corpo precisa ser `application/json` (`unsupported_media_type`); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.

## GET `/v1/projects/{p}/services/{s}/variables/{name}`

Lê uma variável

Valor sensível vem `null` sem `env:reveal`.

Escopo exigido: `env:read`. Com `env:reveal`: valor sensível em claro.

Escopo exigido: `env:read` · opcional (muda a resposta): `env:reveal` · operationId: `getServiceVariable`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |
| `s` | path | string | sim | Id ou nome do serviço dentro do projeto. |
| `name` | path | string | sim | Nome da variável (`^[A-Za-z_][A-Za-z0-9_]{0,255}$`). |

### Resposta 200

Variável.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `name` | string | sim |  |
| `value` | string\|null | sim | `null` quando a variável é sensível e o token não tem `env:reveal`. |
| `sensitive` | boolean | sim | Flag declarado: o valor foi marcado como sensível. Governa o mascaramento do valor e a regra de nunca rebaixar. |
| `effective_sensitive` | boolean | não | `true` quando a variável é sensível **ou** o valor referencia (`${{svc.VAR}}`) uma variável sensível (p.ex. `${{db.DATABASE_URL}}`): o valor resolvido herda a sensibilidade. Sempre enviado. O flag `sensitive` não muda por herança (continua o que o usuário declarou); o texto da referência é devolvido como está, e o valor herdado só sai em `?resolved=true`, que exige `env:reveal`. Referência que não resolve ou variável compartilhada: igual a `sensitive`. |

```json
{
  "name": "LOG_LEVEL",
  "value": "info",
  "sensitive": false
}
```

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.

## PUT `/v1/projects/{p}/services/{s}/variables/{name}`

Define uma variável

Cria ou substitui. **Nunca devolve** o valor de uma variável sensível. Referências `${{svc.VAR}}` são validadas na sintaxe.

Escopo exigido: `env:write`.

Escopo exigido: `env:write` · operationId: `putServiceVariable`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |
| `s` | path | string | sim | Id ou nome do serviço dentro do projeto. |
| `name` | path | string | sim | Nome da variável (`^[A-Za-z_][A-Za-z0-9_]{0,255}$`). |

### Corpo (application/json)

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `value` | string | sim |  |
| `sensitive` | boolean | não | Omitido mantém o valor atual (novas: `false`). Sensível nunca volta a não-sensível (`409 sensitive_lowered`). |

### Resposta 200

Variável gravada.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `name` | string | sim |  |
| `value` | string\|null | sim | `null` quando a variável é sensível e o token não tem `env:reveal`. |
| `sensitive` | boolean | sim | Flag declarado: o valor foi marcado como sensível. Governa o mascaramento do valor e a regra de nunca rebaixar. |
| `effective_sensitive` | boolean | não | `true` quando a variável é sensível **ou** o valor referencia (`${{svc.VAR}}`) uma variável sensível (p.ex. `${{db.DATABASE_URL}}`): o valor resolvido herda a sensibilidade. Sempre enviado. O flag `sensitive` não muda por herança (continua o que o usuário declarou); o texto da referência é devolvido como está, e o valor herdado só sai em `?resolved=true`, que exige `env:reveal`. Referência que não resolve ou variável compartilhada: igual a `sensitive`. |
| `needs_redeploy` | array<string> | sim | Serviços marcados para novo deploy. |

```json
{
  "name": "LOG_LEVEL",
  "value": "info",
  "sensitive": false,
  "needs_redeploy": [
    "web"
  ]
}
```

### Resposta 409

`sensitive_lowered`.

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `error` | enum(unauthorized, forbidden, not_found, payload_too_large, quota_exceeded, unsupported_type, class_not_allowed, extension_mismatch, malware_detected, svg_unsafe, archive_encrypted, archive_bomb, archive_unsupported, rate_limited, unavailable, idempotency_conflict, invalid_request, unsupported_media_type, bad_json, too_large, authz_unavailable, database_unavailable, restore_not_removable, gallery_not_empty, bad_body, internal, name_taken, slug_taken, project_quota_exceeded, project_not_empty, deploy_in_progress, nothing_to_rollback, not_cancelable, sensitive_lowered, duplicate_variable, domain_taken, volume_exists, volume_in_use, volume_attached, backup_not_ready, unresolved_reference, reference_cycle, invalid_reference, reference_too_deep, value_too_large) | sim | Código estável para tratamento programático. A tabela completa está na descrição geral. |
| `message` | string | sim | Mensagem legível em pt-BR. |
| `details` | object | não | Presente em alguns `422` (ex.: `unresolved_reference` e `reference_cycle` trazem `details.references`). |
| `details.references` | array<string> | não | Referências problemáticas (só nomes, nunca valores). |
| `details.retry_after_seconds` | integer | não | Em `422 rate_limited` (ex.: verificação manual de domínio): segundos até poder tentar de novo. |

### Resposta 422

`quota_exceeded` (limite de variáveis).

| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
| `error` | enum(unauthorized, forbidden, not_found, payload_too_large, quota_exceeded, unsupported_type, class_not_allowed, extension_mismatch, malware_detected, svg_unsafe, archive_encrypted, archive_bomb, archive_unsupported, rate_limited, unavailable, idempotency_conflict, invalid_request, unsupported_media_type, bad_json, too_large, authz_unavailable, database_unavailable, restore_not_removable, gallery_not_empty, bad_body, internal, name_taken, slug_taken, project_quota_exceeded, project_not_empty, deploy_in_progress, nothing_to_rollback, not_cancelable, sensitive_lowered, duplicate_variable, domain_taken, volume_exists, volume_in_use, volume_attached, backup_not_ready, unresolved_reference, reference_cycle, invalid_reference, reference_too_deep, value_too_large) | sim | Código estável para tratamento programático. A tabela completa está na descrição geral. |
| `message` | string | sim | Mensagem legível em pt-BR. |
| `details` | object | não | Presente em alguns `422` (ex.: `unresolved_reference` e `reference_cycle` trazem `details.references`). |
| `details.references` | array<string> | não | Referências problemáticas (só nomes, nunca valores). |
| `details.retry_after_seconds` | integer | não | Em `422 rate_limited` (ex.: verificação manual de domínio): segundos até poder tentar de novo. |

```json
{
  "error": "quota_exceeded",
  "message": "Limite de serviços do plano atingido."
}
```

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `413` Corpo JSON acima do limite da rota (`too_large`); `415` O corpo precisa ser `application/json` (`unsupported_media_type`); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.

## DELETE `/v1/projects/{p}/services/{s}/variables/{name}`

Apaga uma variável

Remove a variável e marca os serviços afetados para novo deploy.

Escopo exigido: `env:write`.

Escopo exigido: `env:write` · operationId: `deleteServiceVariable`

| Parâmetro | Em | Tipo | Obrig. | Descrição |
|---|---|---|---|---|
| `p` | path | string | sim | Id (26 caracteres) ou slug do projeto. Projeto de outro time ou fora dos `project_ids` do token ⇒ `404`. |
| `s` | path | string | sim | Id ou nome do serviço dentro do projeto. |
| `name` | path | string | sim | Nome da variável (`^[A-Za-z_][A-Za-z0-9_]{0,255}$`). |

### Resposta 204

Removida.

### Erros comuns

Corpo `{"error", "message", "details"?}` (veja [Autenticação e visão geral](/referencia/autenticacao/) e a [tabela de erros](/erros/tabela/)): `400` Entrada inválida (nome de arquivo fora das regras, cursor malformado, formulário sem `file`, nome de galeria vazio ou longo); `401` Token ausente, inválido, expirado ou revogado. A resposta é genérica de propósito; `403` O token não tem o escopo exigido pela operação; `404` Recurso inexistente ou pertencente a outro time (indistinguíveis de propósito); `429` Limite de requisições excedido. Espere `Retry-After` segundos; `500` Falha interna (`internal`). O detalhe fica só no log do servidor; repita depois, e informe o `Idempotency-Key` ao suporte se persistir; `503` Serviço (ex.: scanner ou borda) temporariamente indisponível. Tente de novo após `Retry-After`.
