---
title: "Environments and public keys API"
description: "Development and production credentials, and server API keys."
canonical: https://docs.notifiable.sh/api/environments-and-keys
source: https://docs.notifiable.sh/api/environments-and-keys.md
---

# Environments and public keys API

`{workspace}` is the workspace slug. `{app}` is the app UUID. `{environment}` is `development` or `production`. A person who is not a member receives 404.

## GET /workspaces/{workspace}/apps/{app}/keys

- **Auth:** signed-in member.
- **Name:** `app.keys`
- **Request:** none.
- **Response:** the keys page, including `development_public_key`, `production_public_key`, the active `public_key`, and the current `environment`. `delivery` describes managed push. `delivery.android` is `{ package_name, status }` when the stack needs Android, otherwise null. `status` is `pending`, `ready`, or `failed`. `delivery.ios` is `{ bundle_id, connected }` when the stack needs iOS, otherwise null. `connected` is true when the workspace has an Apple connection. `delivery.apple` is `{ team_id, key_id }` when this workspace has an Apple connection, otherwise null. The signing key is not included. `delivery.expo` is true for Expo. `delivery.web` is `{ public_key }` for Web, otherwise null. The private key is not included. `revealedKey` is the new public key only on the visit immediately after reset, otherwise null. Server API keys are on workspace settings. This visit is stored as the last app.
- **Errors:** 404 when the app is missing, deleted, or in another workspace.

## PATCH /workspaces/{workspace}/apps/{app}/environment

- **Auth:** signed-in developer, admin, or owner.
- **Name:** `app.environment`
- **Request:** `environment`, required, `development` or `production`.
- **Response:** redirect back. The active public key follows the new environment.
- **Errors:** 403 for a viewer. 404 when the app is missing or in another workspace. Validation error when `environment` is anything else. For React Native, Flutter, or Other with neither side configured, `production` is rejected with `Configure Android, iOS, or both before production.`

## POST /workspaces/{workspace}/apps/{app}/public-keys/{environment}/reset

- **Auth:** signed-in developer, admin, or owner.
- **Name:** `app.public-key.reset`
- **Request:** `environment` in the path.
- **Response:** redirect back with a new `ntf_pub_` key stored for that environment only. The next keys page includes that key once as `revealedKey`. Later visits send `revealedKey` as null.
- **Errors:** 403 for a viewer. 404 when the app is missing, in another workspace, or the environment is not `development` or `production`.

## POST /workspaces/{workspace}/apple-connection

- **Auth:** signed-in developer, admin, or owner.
- **Name:** `workspace.apple-connection.store`
- **Request:** `team_id`, `key_id`, and `signing_key`. The signing key is the key Apple issues for that team. One connection is stored for the workspace and reused by every iOS app.
- **Response:** redirect back. The keys page and later app creation show Apple Developer connected.
- **Errors:** guests are redirected to login. 403 for a viewer. 404 when the user is not a member. 422 when a value is missing or the signing key cannot be read.

## PATCH /workspaces/{workspace}/apps/{app}/delivery

- **Auth:** signed-in developer, admin, or owner.
- **Name:** `app.delivery.update`
- **Request:** `package_name` and `bundle_id` are optional reverse-DNS identifiers. Kotlin still requires a package name. React Native, Flutter, and Other can clear either side. Clearing the last configured side returns the app to development.
- **Response:** redirect back. A changed package name that is not already prepared is queued again.
- **Errors:** 403 for a viewer. 404 when the app is missing or in another workspace. 422 when an identifier is not reverse-DNS or a required package name is empty.

## POST /workspaces/{workspace}/apps/{app}/push-credentials

- **Auth:** signed-in developer, admin, or owner.
- **Name:** `app.push-credentials.store`
- **Request:** `provider` (`apns`, `fcm`, `expo`, or `web_push`). This route remains for a secret already used by an app. The keys page does not show it. APNs sends `key_id`, `team_id`, `bundle_id`, and `private_key`. FCM sends `service_account`. Expo sends `access_token`; a blank token removes a stored Expo token. Web Push sends `subject` (`mailto:` or `https://`), `public_key`, and `private_key`. The credential is stored for the app's active environment. A stored secret is used ahead of managed delivery.
- **Response:** redirect back. The private key is not returned.
- **Errors:** guests are redirected to login. 403 for a viewer. 404 when the user is not a member, or when the app is missing or in another workspace. 422 when a required value is missing, the private key cannot be read, the Firebase JSON is not a service account, the Web Push subject is not a mailto address or an https URL, or the Web Push public key is not a P-256 key.

Server API keys are created from workspace settings. See Workspaces API.
