Agents reach APIs through tools, and credential handling is where most incidents start. Here are the patterns that hold up.
Prefer short-lived tokens over static keys
A static API key in an agent's environment is a standing liability — it leaks into logs, traces, and sometimes model output. Where the API supports it, use OAuth client-credentials or a token-exchange flow and refresh out-of-band:
curl -s -X POST https://auth.example.com/oauth/token \
-d grant_type=client_credentials \
-d client_id="$CLIENT_ID" -d client_secret="$CLIENT_SECRET" \
-d scope="reports:read"
Scope the token to exactly what the tool needs (reports:read, not *).
Never put secrets where the model can see them
- Not in tool
descriptions, not in system prompts, not in retrieved context. - Resolve credentials inside the tool implementation, after the model decides to call it — the model should never handle the secret itself.
Azure / Entra
Prefer managed identities over client secrets for workloads in Azure — no secret to leak or rotate:
from azure.identity import DefaultAzureCredential
cred = DefaultAzureCredential() # uses managed identity when available
token = cred.get_token("https://graph.microsoft.com/.default")
Separate read from write
Give read-only tools read-only credentials. If a tool can mutate state, gate it behind confirmation and use a distinct, narrowly-scoped credential so a compromised read path can't write.