Skip to main content
Every secrets endpoint checks one IAM action before it does anything. This is the whole list — there are no others, and no endpoint skips the check.
The API reference shows the action on each endpoint’s own page, so you do not have to come back here to look one up. Both come from the same place: the authorization call in the service, read at build time.

The actions

Operations on a specific secret are authorized against that secret’s CRN, with the secret’s tags available as condition context. Only the two collection-level operations are not.

Describing a secret does not read it

secrets:GetSecretValue is a different action from secrets:DescribeSecret, and the split runs all the way through the API: the describe response has no value field at all, so there is no shape in which metadata and plaintext travel together. Grant the read action on its own, to the few principals that need it, on the few secrets they need. The same split appears on KMS with kms:Decrypt and on certificates with certificate:GetCertificateMaterial.

Writing does not imply reading

secrets:PutSecretValue adds a version without returning anything of the old one, so a rotation job can hold it alone. That is worth doing: a component that only ever writes new material has no reason to be able to read what is already there.

Listing cannot be narrowed

secrets:ListSecrets is authorized against the collection rather than against individual secrets, so restricting it by CRN or tag has no effect. Scope secrets:GetSecretValue — that is the grant that matters.

Resources

Secrets actions are checked against one resource shape:
The region slot is populated: a secret lives in one region and is read from there. A secret’s name may contain /, and that is what makes the CRN worth scoping by. Naming secrets prod/payments/stripe-key rather than prod-payments-stripe-key lets one statement cover a whole service’s secrets and nothing else. Names match ^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,255}$.

Conditions

Create carries the tags from the request; every secret-scoped action carries the tags already on the secret:
  • basalt:RequestTag/<key> — what a caller may label a secret as, at create.
  • basalt:ResourceTag/<key> — which existing secrets an action may touch.

Writing a policy

One service reading exactly the secrets under its own path prefix:
A rotation job that may write but never read:
secrets:* includes reading every value. A wildcard on the action grants secrets:GetSecretValue along with everything else, which is rarely what is meant by “let this team manage secrets”. List the actions when the credential belongs to a person or a CI job.
See writing policies for the document format, the tag condition keys, and how a deny guardrail survives a broad allow.

What a denial looks like

A failed check answers 403:
It does not tell you which action was missing, deliberately — the message is the same for every denial, so it cannot be used to map out what a credential can and cannot reach. Look up the call you made in the table above, and the action it needs is the one to add.
A 404 is not a disguised 403. Ownership is resolved before authorization: a secret belonging to another account answers 404 because it is not yours to see, and one you own but lack the action for answers 403. A secret inside its recovery window is a third case again — it answers 409 SECRET_DELETED, which means the secret is there and the credential is fine.