# Access stored credentials

```yaml
capability_id: secrets:read
display_name: Access stored credentials
ui_capability_key: access_credentials
scope: secrets:read
endpoint:
  method: POST
  path: /secrets/get
operation: secrets.get
billable: true
environment: required
```

## Purpose and authorization

This capability lets an active, authenticated Gateman application retrieve
eligible credential values stored for that application.

It covers two credential categories:

- Custom credentials: customer-defined logical names with independently
  optional production and test values.
- Provider credentials: provider-specific stored names, including eligible
  managed SES and S3 runtime credentials, subject to provider retrieval
  authorization.

The application MUST have the `secrets:read` scope and authenticate with
[Contract v1](https://docs.getgateman.com/contract-v1/). Provider credentials
also require the relevant provider's application-scoped **Allow app
retrieval** setting. The setting applies to eligible names for that provider;
it is not a per-field or per-environment permission.

Provider relay scopes authorize Gateman to use a credential internally. They do
not authorize the application to receive that credential. This capability is
also separate from database-password retrieval, public configuration,
provider provisioning, and setup credentials.

## Implementation contract

### Request

```http
POST /secrets/get
Content-Type: application/json
X-GM-App: app_...
X-GM-Ts: 1710000000
X-GM-Sig: <Contract-v1-signature>
X-GM-Credential-Request-ID: 0123456789abcdef0123456789abcdef
```

The request MUST use the authenticated and signed Contract v1 transport. The
credential-request identifier is a separate required signed component for this
route.

```json
{
  "environment": "production",
  "keys": [
    "MY_API_KEY",
    "STRIPE_SECRET_KEY"
  ]
}
```

Request fields:

| Field | Required | Contract |
| --- | --- | --- |
| `environment` | Yes | String: `production`, `staging`, or `development`. |
| `keys` | Yes | Array of requested names; at most 10 submitted entries. |

Names are retrieved by exact name. There is no wildcard or list-all operation.
The route applies its normal input normalization, skips non-string, empty, and
overlong names, and does not return per-name diagnostics.

### Custom credential selection

Custom credentials use one logical name with two independently optional
values:

| Request environment | Selected custom value |
| --- | --- |
| `production` | Production value |
| `staging` | Test value |
| `development` | Test value |

There is no fallback between slots. If the selected value is not configured,
the name is omitted. The caller requests the same logical name in all three
environments.

### Provider credential selection

Provider credentials are retrieved by their configured names, independently of
the request environment. For example, `STRIPE_SECRET_KEY` retrieves the
production-named credential, while `STRIPE_SECRET_KEY_TEST` retrieves the
test-named credential. Gateman does not automatically substitute one for the
other.

Examples, when configured and authorized:

| Provider | Production-named credential | Test-named credential |
| --- | --- | --- |
| Stripe | `STRIPE_SECRET_KEY` | `STRIPE_SECRET_KEY_TEST` |
| Stripe webhook | `STRIPE_WEBHOOK_SECRET` | `STRIPE_WEBHOOK_SECRET_TEST` |
| Square | `SQUARE_ACCESS_TOKEN` | `SQUARE_ACCESS_TOKEN_TEST` |
| PayPal | `PAYPAL_CLIENT_ID`, `PAYPAL_CLIENT_SECRET` | `PAYPAL_CLIENT_ID_TEST`, `PAYPAL_CLIENT_SECRET_TEST` |

Other providers commonly use shared names across environments, including
email, SMS, storage, anti-bot, and general relay credentials. Do not infer
that every provider has production/test pairs.

Provider operations may independently map their own environment field to
provider credentials. That operation-level mapping does not change this
route's exact-name disclosure contract.

### Successful response

```http
HTTP/1.1 200 OK
Cache-Control: no-store
Content-Type: application/json; charset=utf-8
```

```json
{
  "ok": true,
  "secrets": {
    "MY_API_KEY": "example-custom-value",
    "STRIPE_SECRET_KEY": "sk_example_not_real"
  }
}
```

The `secrets` object contains only requested, configured, and authorized names.
Unknown, unavailable, invalid, and retrieval-disabled names are omitted. An
empty object is valid. Multiple requested names do not produce multiple
operations.

### Managed values and exclusions

Eligible managed SES and S3 runtime credentials use ordinary provider
credential names and may be returned when the `secrets:read` scope and the
corresponding provider retrieval setting authorize disclosure.

Temporary setup credentials, internal control credentials, derived provider
access-token caches, and provider configuration metadata are outside generic
retrieval. Database passwords use `db:read` and `POST /db/get`, one named
password at a time.

### Stripe webhook secret

Stripe webhook secrets have a separate route:

```http
POST /stripe/webhook-secret
```

That route requires `stripe:webhook:read`. An eligible Stripe webhook secret
may also be returned by `/secrets/get` when `secrets:read` and Stripe **Allow
app retrieval** authorize it. These are separate authorization paths.

### Errors

Responses use the shared JSON error envelope. Relevant route outcomes are:

| Condition | Status and error |
| --- | --- |
| Missing, invalid, stale, or unsigned authentication; invalid credential request ID | HTTP `401`; the route's authentication error, such as `Invalid credential request ID` or `Invalid signature`. |
| Missing `secrets:read` | HTTP `401`, `Scope not allowed`, under the current authentication contract. |
| Invalid JSON | HTTP `400`, `Invalid JSON`. |
| Missing environment | HTTP `400`, `Application environment is required`. |
| Unsupported environment | HTTP `400`, `Unsupported application environment`. |
| `keys` is not an array | HTTP `400`, `Invalid request`. |
| More than 10 submitted entries | HTTP `400`, `Too many keys requested`. |
| Replayed credential-request identifier | HTTP `409`, `Credential request already used`. |
| Monthly operation allowance exhausted | HTTP `403`, `monthly_operation_limit_reached`, with allowance details. |
| Application concurrency limit | HTTP `429`, `app_concurrency_limit_exceeded`, with `Retry-After`. |
| Application concurrency guard unavailable | HTTP `503`, `app_concurrency_guard_unavailable`. |
| Credential claim infrastructure unavailable | HTTP `503`, `Credential retrieval unavailable`. |
| Secret storage or decryption unavailable | HTTP `500`, `Secret store unavailable`. |

The authentication layer returns HTTP 401 for an unauthorized scope in the
current implementation; integrations MUST follow the actual response rather
than assuming all authorization failures are HTTP 403.

## Operation and replay obligations

One accepted request consumes one `secrets.get` operation, regardless of the
number of requested names. A request that returns no eligible values can still
consume an operation. Authentication, scope, malformed-request, and replay
rejections occur before operation reservation.

`X-GM-Credential-Request-ID` MUST:

- contain exactly 32 lowercase hexadecimal characters;
- be included in the Contract v1 signature;
- be fresh and single-use for the application.

Gateman claims the identifier before credential lookup. A later lookup or
decryption failure may still consume it. Claims are retained for 11 minutes;
a retry requires a new identifier.

Official PHP, Node.js, Python, and Go clients generate this identifier for
`/secrets/get` when using their general request methods. No client is
privileged, and clients do not automatically retry.

### Retry semantics

- Definitive rejection before disclosure, such as invalid authentication or
  request validation, did not disclose a credential. The response must still
  be handled according to its status and error; not every rejection is safely
  retryable.
- A transport failure or ambiguous outcome may occur after Gateman has
  processed the request. The caller MUST NOT assume disclosure did not occur.
- A claimed credential-request identifier cannot be replayed during its active
  protection window. A new attempt requires a fresh identifier.

A deliberately implemented retry policy MAY account for these known failure
semantics. A new accepted attempt can consume another `secrets.get` operation.
See [Operation Accounting](https://docs.getgateman.com/operation-accounting/),
[Rate limits and concurrency](https://docs.getgateman.com/rate-limits-and-concurrency/),
and [Idempotency](https://docs.getgateman.com/idempotency/).

## Implementation outcome

A correct integration should produce these observable outcomes:

- Requested, authorized, and available credential values are retrieved
  correctly.
- Custom credentials resolve to the appropriate environment slot without
  production/test fallback.
- Provider credentials are retrieved by their exact configured names.
- Omitted values are handled explicitly without assuming that they exist.
- Contract v1 authentication, single-use credential-request protection, and
  error handling are preserved.
- Retrieved plaintext is not inadvertently persisted or logged.

## Implementation instructions for agents

- **SHOULD** use an official Gateman client where appropriate. A direct HTTP
  implementation **MUST** conform to Contract v1.
- The integration **MUST** supply the application's environment using only
  `production`, `staging`, or `development`.
- It **MUST** request custom credentials by their logical names.
- It **MUST** request provider credentials by their exact configured names and
  **MUST NOT** transform provider names based on the request environment.
- It **MUST** handle omitted names explicitly; a requested name is not
  guaranteed to appear in `secrets`.
- It **MUST** preserve the fresh, single-use
  `X-GM-Credential-Request-ID` mechanism and include it in the signed request.
- It **SHOULD NOT** automatically retry an uncertain disclosure. A deliberate
  retry policy MAY use a fresh identifier and account for another accepted
  operation.
- It **MUST NOT** enable provider retrieval or expand application scopes
  without the customer's explicit authorization.
- Direct credential retrieval and provider relay operations are independent
  developer/customer choices; neither is implicitly required or preferred.
- It **SHOULD** keep retrieved plaintext credentials in memory only as long as
  needed and prevent them from entering source files, `.env` files, logs, or
  other persistent locations unless the developer intentionally selects a
  different credential-management approach.
- It **MUST NOT** represent Gateman as controlling a disclosed credential or
  treat disabling retrieval as revocation of credentials already disclosed or
  of provider-side permissions.

## Reusable AGENTS.md block

```markdown
<!-- gateman:begin capability=secrets:read -->
- Use the `secrets:read` Access stored credentials capability through
  authenticated Contract v1 `POST /secrets/get` with a fresh, signed,
  single-use `X-GM-Credential-Request-ID`.
- Custom credentials use logical names: `production` selects the production
  value; `staging` and `development` select the test value, with no fallback.
- Provider credentials use exact configured names; do not remap them from the
  request environment.
- Provider disclosure also requires that provider's **Allow app retrieval**
  setting; do not enable it or expand scopes without customer authorization.
- Treat omitted names as unavailable and do not automatically try alternatives.
- Keep returned plaintext out of source, `.env` files, logs, and unintended
  persistent locations.
- Disabling Gateman retrieval does not revoke credentials already disclosed or
  provider-side permissions.
- Full reference: https://docs.getgateman.com/capabilities/secrets.read/
<!-- gateman:end capability=secrets:read -->
```

## Related references

- [Contract v1](https://docs.getgateman.com/contract-v1/)
- [Authentication](https://docs.getgateman.com/authentication/)
- [Environments](https://docs.getgateman.com/environments/)
- [Operation Accounting](https://docs.getgateman.com/operation-accounting/)
- [Errors and responses](https://docs.getgateman.com/errors-and-responses/)
- [Idempotency](https://docs.getgateman.com/idempotency/)
- [Data retention](https://docs.getgateman.com/data-retention/)
- [Official PHP client](https://docs.getgateman.com/canonical-client-php/)
- [Official Node.js client](https://docs.getgateman.com/canonical-client-node/)
- [Official Python client](https://docs.getgateman.com/canonical-client-python/)
- [Official Go client](https://docs.getgateman.com/canonical-client-go/)
