---
title: Approve secret access by environment
description: "🎯 Auto-approve access to secret parameter values in lower environments and require manual approval for production."
sidebar_label: Secret access approvals
toc_max_heading_level: 2
doc_id: 834bb826-b4e1-4e30-bc21-a29c33099ce6
tutorial_type: tutorial
tutorial_category: governance
tutorial_time: 15 min
tutorial_featured: false
tutorial_cover: /img/tutorials/covers/secrets-approval-by-environment.svg
keywords:
  - secrets
  - parameters
  - approvals
  - policies
  - dimensions
  - slack
tags:
  - governance
  - production
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Approve secret access by environment

> 🎯 **Goal:** Let your team reveal secret parameter values in lower environments instantly, while production access requires a manual approval.

:::warning Upcoming deprecation

This tutorial uses policies, which will be deprecated in favor of [checklists](/docs/approvals/checklists). For a new guardrail, start with [Your first checklist](/docs/approvals/your-first-checklist) instead. If you want to migrate policies to checklists, [reach out to the nullplatform team](/docs/support).

:::

## Introduction

Secret parameters often hold one value per environment: a development database password, a staging token, a production key. Not all of those values deserve the same protection — blocking a developer from reading the development value slows them down, while handing out the production value without review is a risk.

Since access requests for secret values carry the [dimensions](/docs/dimensions) being revealed, you can express this rule as pure configuration: one approval action, one policy, and the environment matrix takes care of itself. Every request is audited, including the auto-approved ones.

## What you'll set up

You'll:

- Create an **approval action** for `parameter:read-secrets` that turns policy results into outcomes.
- Attach a **policy** that passes only for lower environments, so `development` and `staging` requests **auto-approve** while `production` requests wait for **manual review**.
- Review and approve requests from the **Approval requests** screen in **Platform settings**, and optionally get notified in **Slack**.

## Prerequisites

You'll need:

- The [**nullplatform CLI**](/docs/cli/) installed: `curl https://cli.nullplatform.com/install.sh | sh`
- A valid [**nullplatform API key**](/docs/authorization/api-keys) with permissions to manage approvals, and an environment variable for the CLI:
  ```bash
  export NULLPLATFORM_API_KEY=<your_api_key_here>
  ```
- An `environment` [dimension](/docs/dimensions) with values like `development`, `staging`, and `production`.
- A secret [parameter](/docs/parameters) with values set per environment.

:::note Who gets gated
Approvals apply to users who **don't** hold the direct permission to read secret values (granted through the SecOps role). Users with that permission keep revealing values directly, with no approval step. See [Accessing secret values](/docs/parameters/secret-visibility).
:::

## 1. Create the approval action

The approval action declares that revealing secret values of parameters under an NRN requires an approval, and maps policy results to outcomes: if the policy passes, the request is **approved automatically**; if it fails, it goes to **manual review**.

> Replace `<organization=XXXX:account=XXXX:namespace=XXXX>` with the NRN where the rule should apply.

<Tabs
defaultValue="env-action-cli"
values={[
{ label: 'CLI', value: 'env-action-cli' },
{ label: 'cURL', value: 'env-action-curl' },
]}>

<TabItem value="env-action-cli">

```bash
np approval action create
  --body '{
    "nrn": "<organization=XXXX:account=XXXX:namespace=XXXX>",
    "entity": "parameter",
    "action": "parameter:read-secrets",
    "dimensions": {},
    "on_policy_success": "approve",
    "on_policy_fail": "manual"
  }'
```

</TabItem>
<TabItem value="env-action-curl">

```bash
curl -L -X POST 'https://api.nullplatform.com/approval/action' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
  "nrn": "<organization=XXXX:account=XXXX:namespace=XXXX>",
  "entity": "parameter",
  "action": "parameter:read-secrets",
  "dimensions": {},
  "on_policy_success": "approve",
  "on_policy_fail": "manual"
}'
```

</TabItem>
</Tabs>

Setting `dimensions` to `{}` makes the action govern requests for any dimensions — the policy decides per request. Save the `id` from the response; you'll need it in step 3.

#### ✅ Checkpoint

Go to **Platform settings → Approvals → Settings** and confirm the new action is listed for the **Parameters** entity.

<img alt="Approval settings listing the parameter read-secrets action" src="/img/tutorials/secrets-approval-env-action-settings.png" width="80%" className="helper-image img-separator" />

## 2. Create the policy

The policy is an allowlist of the environments that may auto-approve. Requests carry the dimensions being revealed, so conditions can reference them directly:

<Tabs
defaultValue="env-policy-cli"
values={[
{ label: 'CLI', value: 'env-policy-cli' },
{ label: 'cURL', value: 'env-policy-curl' },
]}>

<TabItem value="env-policy-cli">

```bash
np approval policy create
  --body '{
    "nrn": "<organization=XXXX:account=XXXX:namespace=XXXX>",
    "name": "Auto-approve secret access in lower environments",
    "conditions": {
      "dimensions.environment": { "$in": ["development", "staging"] }
    }
  }'
```

</TabItem>
<TabItem value="env-policy-curl">

```bash
curl -L -X POST 'https://api.nullplatform.com/approval/policy' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
  "nrn": "<organization=XXXX:account=XXXX:namespace=XXXX>",
  "name": "Auto-approve secret access in lower environments",
  "conditions": {
    "dimensions.environment": { "$in": ["development", "staging"] }
  }
}'
```

</TabItem>
</Tabs>

Use the environment values defined in your organization — yours may differ (for example, `dev` and `prod`). Save the policy `id` from the response.

:::warning
Keep the conditions an **allowlist** (`$in`) of permitted environments. An exclusion like `{ "$ne": "production" }` also passes for requests that don't specify dimensions, which would auto-approve access to every value of the parameter — including production.
:::

## 3. Associate the policy with the action

Link the policy to the action using the two IDs you saved:

<Tabs
defaultValue="link-policy-cli"
values={[
{ label: 'CLI', value: 'link-policy-cli' },
{ label: 'cURL', value: 'link-policy-curl' },
]}>

<TabItem value="link-policy-cli">

```bash
np approval action policy create
  --id <approval_action_id>
  --body '{
    "policy_id": <policy_id>
  }'
```

</TabItem>
<TabItem value="link-policy-curl">

```bash
curl -L -X POST 'https://api.nullplatform.com/approval/action/<approval_action_id>/policy' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
  "policy_id": <policy_id>
}'
```

</TabItem>
</Tabs>

From this point on, the environment decides the outcome:

| Request | Policy result | Outcome |
|---|---|---|
| Reveal `development` or `staging` values | Passes | `auto_approved` — no human step |
| Reveal `production` values | Fails | `pending` — waits for manual review |
| Reveal the whole parameter (no dimensions) | Fails | `pending` — waits for manual review |

## 4. Review requests in Platform settings

Approvers don't need to leave the platform: the **Approval requests** screen shows every request that needs attention, and lets you follow up on resolved ones.

1. Go to **Platform settings → Approvals → Requests**.
2. Filter by entity **Parameter** or status **Pending** to find requests waiting for review.
3. Open **View details** to see who requested access, the parameter, the requested **dimensions**, and the policies that were evaluated.
4. Click **Approve** or **Deny**. You can add an optional message for the requester.

<img alt="Approval requests list with a pending production request" src="/img/tutorials/secrets-approval-env-requests-list.png" width="80%" className="helper-image img-separator" />

<img alt="Approval request detail showing dimensions and evaluated policies" src="/img/tutorials/secrets-approval-env-request-detail.png" width="80%" className="helper-image img-separator" />

## 5. (Optional) Get notified in Slack

To get pinged when a request needs review, create a notification channel filtered to this action. First [connect the nullplatform Slack app](/docs/approvals/set-up-notifications#slack) and invite it to your channel, then:

<Tabs
defaultValue="slack-channel-cli"
values={[
{ label: 'CLI', value: 'slack-channel-cli' },
{ label: 'cURL', value: 'slack-channel-curl' },
]}>

<TabItem value="slack-channel-cli">

```bash
np notification channel create
  --body '{
    "nrn": "organization=1",
    "description": "Secret access approvals",
    "source": ["approval"],
    "type": "slack",
    "configuration": {
      "channels": ["secrets-approvals"]
    },
    "filters": {
      "action": "parameter:read-secrets"
    }
  }'
```

</TabItem>
<TabItem value="slack-channel-curl">

```bash
curl -L 'https://api.nullplatform.com/notification/channel' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
  "nrn": "organization=1",
  "description": "Secret access approvals",
  "source": ["approval"],
  "type": "slack",
  "configuration": {
    "channels": ["secrets-approvals"]
  },
  "filters": {
    "action": "parameter:read-secrets"
  }
}'
```

</TabItem>
</Tabs>

Reviewers get a Slack message for each request and can approve or deny from there. See [Set up approval notifications](/docs/approvals/set-up-notifications) for HTTP channels and more options.

<img alt="Slack notification for a secret access request" src="/img/tutorials/secrets-approval-env-slack.png" width="80%" className="helper-image img-separator" />

## Test that it works

Sign in as a developer who doesn't have direct access to secret values, open the secret parameter, and:

1. **Reveal the `development` value.** Access is approved automatically and the value appears — no waiting.
2. **Request access to the `production` value.** The request stays pending, and reviewers are notified.
3. **As an approver**, go to **Platform settings → Approvals → Requests** and approve it. Notice the request from step 1 shows as **Auto approved**, while this one records you as the reviewer.
4. **Back as the developer**, reveal the `production` value — it's now visible.

Each reveal only uncovers the values for the requested environment; everything else stays hidden. See [Accessing secret values](/docs/parameters/secret-visibility#reveal-values-for-specific-dimensions) for the details.

## Wrap-up 🎉

All done! Now you have:

- Secret access requests that **carry the environment** being revealed.
- **Auto-approval** for `development` and `staging`, with a full audit trail.
- **Manual review** for `production`, decided from the Approval requests screen or Slack.

## What's next

- [Require manual approval for applications in prod](/docs/tutorials/apps-production-approval): apply the same approval model to production deployments
- [Require stage deployment before production](/docs/tutorials/require-stage-before-prod): enforce progressive promotion across environments

---

## Related docs

- [Accessing secret values](/docs/parameters/secret-visibility)
- [Approvals](/docs/approvals/)
- [Policies](/docs/approvals/policies)
- [Approval request lifecycle](/docs/approvals/approval-requests)
- [Set up approval notifications](/docs/approvals/set-up-notifications)
