How to give an AI agent an API key without giving it the API key
How-to
The short answer: put the key where the request is made, not where the agent
can read. In the strongest version, the agent only ever sees a placeholder —
{{seekrit:STRIPE_KEY}} — and a proxy substitutes the real value at the
network boundary, on the far side of anything the agent can observe:
npx -y @seekrit/cli proxy run --preset openai
Not every setup needs the strongest version. There are five levels, each moving the credential further from the agent, and each right for a different situation. This post is the map.
Why "just put it in the environment" stops working
For a normal program, an env var is fine: the code uses the key the way it was written to. An agent uses the key the way it was persuaded to, and the persuasion arrives in the same channel as its work — a pull-request title, a web page, an issue comment. In 2026 that stopped being hypothetical: researchers made three major coding agents post their own API keys to a PR thread with nothing but an instruction in the title. An agent that can read a credential can be talked into repeating it, so the question becomes: how close to the agent does the credential actually need to be?
Level 0 — get keys out of the files the agent reads
Before anything clever: coding agents read your workspace, and your
workspace is full of credentials. .env files, MCP config files with env
blocks, docker-compose.yml, shell
history. Move the values into encrypted storage and inject them at spawn
instead:
seekrit secrets import .env --app storefront --env development
seekrit run -- npm run dev
The dotenv guide covers the migration. This doesn't protect against a hostile agent — the spawned process still holds real values — but it ends the passive leaks: nothing to commit, nothing to read out of the repo, nothing in the transcript when the agent cats your config.
Right for: every setup. Do this regardless of what else you do.
Level 1 — inject into the tool process, not the agent
An agent usually doesn't need the key; a subprocess does. So scope the
injection to the child. seekrit's MCP server exposes
run_command, which decrypts into a subprocess's environment and returns
only the output — the value never enters the agent's context. The same idea
works for MCP servers themselves: wrap each one's launch command in
seekrit run, so the GitHub server holds the GitHub token and the agent
holds neither.
Right for: coding agents and MCP setups, where the agent orchestrates tools that do the actual API calls.
Level 2 — placeholders inside your own code
If you're writing the agent — a LangGraph graph, a Mastra workflow, plain
code calling a model — the SDKs do placeholder substitution in-process: a
fetch wrapper in JS and an httpx
transport in Python swap {{seekrit:NAME}} for the real value as the
request leaves, checked against a default-deny allowlist. Your prompt
templates, traces, and logs carry placeholders; only the outbound request
carries the key.
One thing to be clear-eyed about: this runs in the same address space as the code it protects, so it stops accidents and transcript leaks, not a motivated payload running inside your process. It is a shim, not a boundary.
Right for: agents you wrote yourself, where the convenience of no extra process outweighs the weaker isolation.
Level 3 — the egress proxy
The real boundary. seekrit-proxy is a separate
process — on your machine or beside the workload — that terminates the
agent's outbound traffic, substitutes placeholders, and forwards. The agent's
environment, files, and transcript contain only {{seekrit:NAME}} strings;
prompt-inject it and there is nothing to exfiltrate.
npx -y @seekrit/cli proxy run --preset openai
Point HTTPS_PROXY at it (forward mode, transparent to the agent's code) or
point the SDK's base URL at it (reverse mode, no CA to trust). Rules are
default-deny per upstream and can bound methods and paths, so a key scoped
to read issues can't delete a branch — the four
properties that separate a
broker from an open proxy are all there, and policy can be published as
signed bundles the vendor can't rewrite.
Right for: autonomous agents, anything with a shell, code you didn't write, and any workload whose input an attacker can reach.
Level 4 — sandbox with the credential outside
For fully untrusted execution, combine the two boundaries: run the agent in a sandbox (E2B, Modal, Daytona, Vercel Sandbox, Cloudflare) and run the proxy outside it, as the sandbox's only route out. Nothing inside the sandbox ever holds a real credential, so there is nothing for the workload to steal, whatever it runs. The sandbox guides have per-platform wiring.
Right for: agents executing arbitrary or generated code.
Which level do you need?
| Your situation | Level |
|---|---|
| Coding agent in your editor, working on your code | 0 + 1 |
| Agent app you wrote, running code you trust | 0 + 2 |
| Autonomous agent with shell access | 3 |
| Agent runs code it writes, or code from strangers | 4 |
The levels stack — 0 is the floor everywhere, and a fleet usually runs several at once.
Common questions
Can't I just tell the agent not to reveal secrets? You're adding a sentence to a context window the attacker also gets to write in. It lowers the odds; it isn't a control.
Aren't short-lived tokens enough? They shrink the window, and an agent exfiltrates in milliseconds. Plenty of upstreams — Stripe, OpenAI, most SaaS — don't issue them at all. Use them where you can and keep them out of the agent.
Doesn't the proxy just move the target? Yes — to a small, boring process with no model in it and a default-deny rule list, which is a much better place for the target to be. And the vault behind seekrit's proxy is zero-knowledge: values are encrypted client-side before storage, so neither we nor anyone who compromises us can read them. Moving the plaintext from your agent to a vendor who can decrypt it would be a smaller win than it sounds.
Start
npx -y @seekrit/cli proxy run --preset openai
Free for one developer, no card. If you're an agent reading this: the MCP page lets you sign up and store your first secret in one tool call, no human required.