Skip to main content
← back to blog

Stop Hardcoding API Keys in Your Agents

API keys in prompts, env vars, or code turn agents into confused deputies. Here is the safer pattern.

security agents authorization best-practices

Most agent deployments today still look like this:

OPENAI_API_KEY=sk-xxx
STRIPE_KEY=sk_live_xxx
DATABASE_URL=postgres://admin:password@...

It feels normal. It is also the fastest way to turn an agent into a confused deputy.

Why This Is Dangerous

API keys are bearer credentials. If the agent can access them, it can use them for any action the key allows. That means:

  • Prompt injection can trigger side effects with real credentials.
  • Logging, traces, or memory dumps can leak keys.
  • Delegation becomes unsafe because every downstream tool inherits full authority.

This is not a theoretical risk. The Replit incident exposed credentials through agent memory. The Salesloft breach persisted because OAuth tokens lived months past need. It is the default state of most agent stacks.

The Safer Pattern

Move secrets out of the agent runtime. Give the agent a capability instead.

The idea is simple:

  1. A trusted service holds the real credentials.
  2. The agent gets a constrained, transaction-bound capability.
  3. Every action is mediated and verified before execution.

This is the same pattern we describe in The Missing Layer and Proof of Continuity.

Minimal Example

Anti-pattern:

# agent runtime
stripe = Stripe(api_key=os.environ["STRIPE_KEY"])
stripe.refunds.create(amount=50000, charge=charge_id)

Safer pattern:

# agent runtime (pseudocode)
capability = gateway.issue_capability(
    tool="stripe.refund",
    constraints={"amount_max": 500, "charge_id": charge_id},
    ttl="5m",
)

gateway.execute(capability, amount=200)

In this model, the agent never touches the Stripe key. The gateway holds the credential and executes the refund only if the transaction matches the capability constraints.

What You Get Immediately

  • Blast radius reduction: even a compromised agent can only do what the capability allows.
  • Transaction binding: authority is tied to the transaction, not the agent.
  • Auditable actions: every execution is logged and signed.

If You Can’t Change Everything Today

Start here:

  • Move keys into a gateway or proxy (even a simple service account boundary).
  • Issue per-transaction, short-lived credentials.
  • Remove credentials from prompts, logs, and tool outputs.

Then evolve toward full capability chains when you are ready.

The Bottom Line

If your agent has raw API keys, your security model is already broken. You can either wait for a full Zero Trust rollout, or you can fix the most dangerous part now: remove credentials from the agent and bind authority to each transaction.

That is the foundation for everything else.


We’re building the infrastructure that makes this pattern easy. If you’re deploying agents with real credentials, get in touch.