Security

API keys for AI agents: how to give access with a scoped key

API keys for AI agents, done safely: scope each key to one resource, store only a hash, keep it out of the chat and the repo, and revoke it in one step.

6 min read

To give an AI agent API access safely, create a key for that agent alone, scoped to the one resource its task needs, stored on the server only as a hash, kept out of the repository and the chat, and revocable at once. Never hand an agent your personal token or an admin key: the agent reads untrusted content, and whatever its key can reach, a bad instruction can reach too.

Why an agent should not use your credentials

A coding agent is a program that follows text. Most of that text comes from you, but some comes from files, web pages, issues and tool results, and any of it can contain instructions (see prompt injection in agent tools). The agent also leaves traces: commands in a transcript, files in a temporary folder, logs.

Your own token was not designed for that. It usually spans every project you belong to, rarely expires, and cannot be revoked without breaking your own tools. A dedicated key changes the question from “what can the agent do?” to “what can this key do?”, which you can answer precisely.

What “scoped” should mean

A scoped key is more than a random string. Each property below limits one kind of damage:

Property What it limits
One resource (a project, a repository, a bucket) What a leaked key can reach
Only the operations the task needs What a misled agent can do
Stored as a hash on the server What a database leak reveals
A short visible prefix Which key to revoke, without showing the key
An expiry for temporary access How long a forgotten key stays useful
Revocation that works at once How long a known leak stays open
A last-use date Whether a key is still needed

Issue the key: generate, hash, show once

Issuing a key the right way takes a few lines. Use a cryptographic random source, store only the hash and a short prefix, and return the key once:

import { randomBytes, createHash } from "node:crypto";

const key = "myapp_" + randomBytes(32).toString("hex");      // shown to the user once
const hash = createHash("sha256").update(key).digest("hex"); // stored
const prefix = key.slice(0, 12);                             // stored, to tell keys apart

await db.insert("api_keys", { project_id, hash, prefix, expires_at, created_at: new Date() });
return { key }; // never logged, never stored in clear

To check a request, hash the key from the Authorization header and look up that hash. A long random key does not need a slow password hash: SHA-256 is enough, because nobody can guess 32 random bytes.

Enforce the scope on the server, not in the prompt

A sentence in the agent’s instructions (“only touch project A”) is a request, not a boundary. The scope has to hold even if the agent is told the opposite:

  • Resolve the key to its resource first, then run every call inside that scope. A key for project A that names an item of project B should get “not found”.
  • Let the database enforce it where you can: row level security, a dedicated role, one transaction per call.
  • Grant the agent’s role write access column by column rather than on whole tables.
  • Test isolation with real queries: a key of one project reading another project must fail.

Keep the key out of the chat, the repository and the logs

Most leaks are not attacks. They are a key printed in a terminal, pasted in a chat or committed in a config file. With a coding agent, three habits cover most of it:

  1. Save the key to a file outside the repository that only you can read, and read it inside each command, without printing it:
AAB_KEY=$(node -p 'require("/tmp/agentappbuilder/my-app.json").key') && curl -s -X POST https://app.agentappbuilder.com/api/tools/get_project_context -H "authorization: Bearer $AAB_KEY"
  1. When the key goes into Claude Code’s MCP configuration, use local scope (claude mcp add --scope local ...), stored in ~/.claude.json in your home folder, or reference a variable in .mcp.json instead of the value. Claude Code MCP server setup covers the scopes.
  2. Run commands that contain a key yourself, in your own terminal, so the key does not pass through the conversation.

Claude Code’s auto mode helps too: printing a live credential or token into the transcript or a file is blocked by default, as the permission modes documentation lists.

Plan for the leaked key

Assume one will leak, and decide in advance:

  • Who can revoke, and where. Revocation should be one action and take effect at once.
  • What the key reaches. If the answer is “one project’s data”, the incident is small.
  • How a temporary key ends. An unclaimed or unused key should expire on its own.
  • What the key cannot do. Write it down: no access to source code, no deployment rights, nothing outside its resource.

How agentappbuilder scopes an agent’s key

agentappbuilder, a prototype canvas that Claude Code writes to, follows this model. Its security page has the details:

  • A key is aab_ followed by 64 hexadecimal characters, 32 random bytes. The platform stores only its SHA-256 hash and its first 12 characters.
  • A key opens one project. Every tool call runs in one transaction under a dedicated database role, whose row level security policies only match rows of that project.
  • A key that the agent receives with a new project is temporary: it expires with the 7-day claim link, and becomes permanent once you claim the project.
  • An owner or admin revokes a key in Settings, Keys, and it stops working at once. The key of a project that is still waiting to be claimed cannot be revoked from the app, so you claim the project first.
  • The platform holds no Git, database or hosting credential. A leaked project key reaches the prototypes and captures of that project, and nothing else.

The agent saves its key in /tmp/agentappbuilder with umask 077, reads it in each command and never prints it, as described in keep the key out of your repository.

A checklist before you hand a key to an agent

  • The key is new and used by this agent only.
  • It opens one resource, with the operations the task needs.
  • The server stores a hash and a prefix, not the key.
  • It has an expiry, or you know when you will revoke it.
  • It lives outside the repository, in a file only you can read or in local configuration.
  • No command prints it, and you ran any command that contains it yourself.
  • You know where to revoke it, and you have tried it once.
  • You know what it cannot reach.

A scoped key does not make an agent trustworthy. It makes the worst case small, visible and easy to end, which is what you need from any credential you hand to software that reads the internet.

Frequently asked questions

Should an AI coding agent use my personal access token?

No. A personal token can do everything you can, in every project. Give the agent its own key, limited to the one resource the task needs, so a mistake or a leak stays small and can be revoked without locking you out.

Is it safe to put an API key in .mcp.json?

Not as a literal value: a project .mcp.json is meant to be committed. Keep keyed servers in local scope, or reference an environment variable such as ${API_KEY} in .mcp.json.

What should I do if an agent's key leaks?

Revoke it, create a new one, and check what the key could reach and when it was last used. A key scoped to one project limits the damage to that project.

Try it on your own app

Tell Claude Code one sentence. It creates the project and gives you a link to see your screens.

Tell Claude Code:

Connect this project to agentappbuilder.com and show me its screens.