secrets block in the definition. Secrets are not declared — they are named, and read back by name through ctx.secrets. The names come from exactly three places:
Activation form secrets
Every string field in your activation form becomes readable as a secret:Keep names in a constant
Renaming a form field renames the secret and breaks every handler reading it. Put the name in one module and import it on both sides:Credential tiers
The host resolves each name across its credential tiers — account, then source, then global — plus the values from your activation, and hands your handler the resolved set.
Where two tiers hold the same name, a value the user entered at activation wins over an admin-issued one; a value your own code wrote with
secrets.set() does not, so a partner cannot shadow a credential an admin issues later. ctx.secrets.set() writes only to the account tier for this {account, product}.
Reading secrets
get returns null when no tier holds the name. names() returns names only, never values.
Writing secrets
set() throws SecretAccessError if the name resolves only from source or global tiers. This is intentional: only account-tier values are writable, so a partner cannot shadow admin-issued credentials.
Common patterns
Storing OAuth tokens
Persist tokens fromonTokens with secrets.set() — they land in the account tier:
Webhook signature validation
Name the secret in your webhook declaration and the transport handles verification:Missing API key
Handle the case where the key is not yet configured:Troubleshooting
SecretAccessError on set()
The message begins Secret "<name>" resolves only from an admin-issued tier (source or global) and cannot be written from integration code. The error carries code: 'SECRET_ACCESS_DENIED' and secretName.
An admin-issued credential can only be updated where it was issued. If your code needs to store its own value, write it under a name of its own.
Missing secret at tool runtime
A tool reads a secret that was never configured at activation. This is expected — useget() and handle null:
Next steps
- Activation form — declare form fields that become secrets
- Webhooks — signature verification via secret names
- OAuth — token storage via
onTokens