# set_secret

Source: https://modulify.ai/docs/api/secrets/set-secret

Creates or replaces one environment variable on a site.

- Title: Set an environment variable
- Scope: `config:write`
- Access: Makes changes
- Endpoint: `POST /v1/set_secret`

By default the value is stored encrypted and is server-only, and reading it back needs `get_secret` and the separate `credentials:reveal` scope. Set `isPublic` to store it unencrypted instead, so it can be read back in the product without a reveal. Leave it off for anything secret.

That flag controls storage only. Every variable reaches the build the same way, and browser visibility comes from the `NEXT_PUBLIC_` prefix on the name, not from the flag. Replacing an existing variable applies the flag again, so leaving `isPublic` out stores the new value encrypted even if the old one was public.

The new value reaches the running preview straight away, but the published site keeps the old one until the site is published again. A site holds up to 200 of your own variables on top of the managed ones, and a new one past that is refused.

`DATABASE_URL`, `DATABASE_TOKEN`, `CDN_URL`, `CDN_PRIVATE_KEY`, `EMAIL_URL` and `EMAIL_PRIVATE_KEY` are refused as locked, and a further list Modulify reserves for itself, among them `GITHUB_TOKEN`, `PROJECT_ID` and `WS_TOKEN`, is refused as reserved. See [Secrets](https://modulify.ai/docs/data/secrets#reserved-names).

## Request

Call it with a `POST` to `https://api.modulify.ai/v1/set_secret`, sending the inputs below as a JSON object. The token needs the `config:write` scope.

It makes changes, so send an [Idempotency-Key](https://modulify.ai/docs/api/idempotency) header whenever you might retry it. A retry with the same key gets the first answer back instead of running again.

```bash
curl -X POST https://api.modulify.ai/v1/set_secret \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"PROJECT_ID","key":"KEY","value":"VALUE"}'
```

Over MCP, the same method is the [set_secret tool](https://modulify.ai/docs/mcp/secrets/set-secret).

## Inputs

| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string | Yes | The site id. |
| `key` | string | Yes | The variable name, for example `STRIPE_SECRET_KEY`. Use letters, digits and underscores only, starting with a letter or an underscore. A name the platform reserves is refused. |
| `value` | string | Yes | The value to store. Anything past 10,000 characters is cut off. |
| `isPublic` | boolean | No | True to store the value unencrypted, so it can be read back without a reveal. Defaults to false. This is about storage, not browser exposure, so never set it for a credential. |

## Response

Every call answers with the [JSON envelope](https://modulify.ai/docs/api/requests-and-responses#the-response) of `success`, `message`, `data`, `code` and `version`. `data` holds the result described above, and on a method that returns a total, `count` carries it. The [response headers](https://modulify.ai/docs/api/requests-and-responses#headers-on-every-method-call) carry the call's `X-Request-Id` and what is left of your per-minute budget in `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`. [Errors](https://modulify.ai/docs/api/errors) explains every status code a call can answer with.