A2A 1.0 · Ed25519 receipts · SOC 2 in progress
Build October 31, 2026Status

Scoped API key management for AI agents: what each key should be allowed to do

One key per agent, explicit scopes per endpoint, and a 403 that tells you which scope was missing. This walks through how to scope agent credentials so a runaway agent can't send mail or book slots it was never granted.

Scoped API key management for AI agents requires assigning exactly one bearer token to each running agent, restricting that token to the precise endpoints the agent must call, and rejecting unauthorized requests with a clean 403 before any storage write executes. Shared admin tokens create operational hazards. A single parsing error, schema drift, or prompt injection can overwrite calendars, dispatch unapproved messages, and erase operational accountability across an entire platform.

Least privilege for LLM agents is a technical contract enforced at the API gateway and database layers. Scoping every key to explicit actions, isolating mailboxes, and recording every invocation to an append-only audit trail gives platform engineers bounded blast radiuses when autonomous code touches production services.

The failure you are actually fixing

Most agent deployments break in production due to three concrete failure modes:

  • An outbound agent dispatches unreviewed email messages directly to external clients after misinterpreting a user prompt or looping on a tool call.
  • A scheduling agent writes over an existing meeting slot on an executive calendar because it holds broad calendar write permissions instead of read-only availability access.
  • An auditor asks which specific agent identity triggered an external API mutation, and engineering can only point to a shared service account token used by six background workers.

A single shared admin key causes each failure. When all workers share one environment variable, the blast radius of any prompt failure equals the union of all capabilities across your agent fleet. Compromising a triage agent gives the underlying model write access to your entire communications infrastructure.

The fix is mechanical. Issue one key per agent, attach an explicit scope list to that key, and evaluate enforcement at the server before application code touches storage. Under this model, an agent stuck in a generation loop can execute only the exact calls allowed by its grant. Every state-changing operation emits an audit record that links back to the specific key identifier, making failures traceable within seconds.

What a scope is, mechanically

A scope is an immutable string verified by the API gateway before executing a route handler. Client code cannot override it, and model instructions cannot alter it. Access delegation frameworks define scope strings as explicit permission identifiers evaluated per request, as specified in IETF RFC 6749 Section 3.3. If an incoming HTTP request lacks the necessary scope in its bearer credentials, the server terminates execution immediately with a 403 Forbidden and an explicit error payload.

Granular scopes isolate agent capabilities across scheduling, messaging, and human review operations:

  • availability:read: Queries busy and free blocks across configured calendars without disclosing event summaries or attendee rosters.
  • bookings:read: Retrieves existing calendar reservations, attendee lists, and event metadata.
  • bookings:write: Commits new bookings, holds time slots, or cancels existing reservations.
  • mailbox:read: Inspects inbound email messages, threads, and metadata inside an assigned mailbox.
  • mailbox:write: Drafts or dispatches outbound email replies.
  • rules:read: Fetches agent-specific scheduling constraints, working hours, and operational boundaries.
  • approvals:request: Submits a human approval request to an operations queue without executing the gated action.

AgentDraft agents authenticate with bearer API keys prefixed avs_live_, stored argon2id-hashed. Each key carries explicit scopes (availability:read, bookings:read, bookings:write, mailbox:read, mailbox:write, rules:read, approvals:request); new keys default to availability:read and bookings:write, and approvals:request must be granted per key. When a triage bot attempts to call a write endpoint without having received the scope, the router returns a structured error immediately.

The gateway performs this verification in linear time against the parsed key context before dispatching to internal workers. The resulting check avoids wasted compute cycles in language model wrappers or downstream execution frameworks such as LangChain, CrewAI, AutoGen, and the OpenAI Agents SDK.

Authentication models: bearer tokens, storage, and hashing

High-security agent infrastructure handles API tokens as secret material with short lifecycle expectations. Storing credentials in plain text inside database tables or embedding them in Docker container environment variables creates immediate exposure vectors. The OWASP Key Management Cheat Sheet outlines standard requirements for key generation, storage hashing, and cryptographic hygiene in automated backends.

When an agent presents a key, the receiving gateway inspects the authorization header:

Authorization: Bearer avs_live_9b2e7c41d80a42f6e9137c5e2197ba43

The gateway does not store the token string itself. It executes an Argon2id verification check against the stored hash in the agent identity table. As specified in the IETF RFC 9106 Argon2 specification, Argon2id provides memory-hard protection against GPU-assisted brute-force recovery if a database backup is misplaced or exposed. By comparing the calculated hash against the record, the gateway determines identity and retrieves the assigned scope set.

For autonomous agents communicating over HTTP, key isolation also requires structured key metadata:

  • Prefixes: Static prefixes such as avs_live_ or avs_test_ allow automated secret scanners like GitHub Secret Scanning or Trufflehog to detect accidental commits in source code repositories.
  • Truncated fingerprint: A non-sensitive suffix (for example, displaying only the last 4 characters) allows human operators to confirm key rotation without exposing the raw bearer secret.
  • Granular expiry: Automated key rotation windows force agent orchestration pipelines to refresh credentials on a predictable cadence.
  • Revocation state: An immediate Boolean flag in the identity store lets an engineer revoke a single compromised agent without impacting sibling agents operating in the same workspace.

Enforcing least privilege by agent role

In multi-agent architectures, agents take on distinct personas and responsibilities. Giving all agents identical credentials violates core access control principles detailed in the NIST Special Publication 800-207 Zero Trust Architecture standard. Roles must translate directly into minimal scope sets.

Inbound triage agents

An inbound triage agent reads incoming messages, categorizes intent, extracts customer entities, and routes data to downstream queues. It has no authority to email customers directly or alter calendar states. Its required scopes are strictly limited:

{
  "agent_id": "agent_triage_inbound",
  "scopes": [
    "mailbox:read",
    "rules:read"
  ]
}

If prompt injection occurs via an adversarial email body, the attacker cannot instruct the model to delete records or send phishing responses to existing threads. The underlying credentials simply lack mailbox:write and bookings:write permissions. When the agent attempts an unpermitted call, the gateway rejects the request with HTTP status code 403.

Calendar scheduling agents

Scheduling agents coordinate appointments between human operators and external users. They inspect available blocks and claim reservations. They do not require access to raw mailbox message contents or global workspace configuration:

{
  "agent_id": "agent_calendar_booking",
  "scopes": [
    "availability:read",
    "bookings:read",
    "bookings:write"
  ]
}

AgentDraft coordinates holds and commits through a priority-aware conflict engine so multiple agents can act on the same calendar without double-booking. The conflict engine is race-free at the storage layer, not in application code. A booking writes one time-bucket row per 5-minute bucket (so a 30-minute booking writes 6 rows) inside a single DynamoDB TransactWriteItems, and each write carries a ConditionExpression encoding the priority rule; two agents committing the same slot cannot both win.

AgentDraft holds expire after 30 seconds by default. A committed booking can be bumped by a higher-priority agent only within a 30-second window, after which it is frozen. A single booking is capped at 480 minutes (8 hours), buffers included, and at 99 five-minute buckets per request; a longer request is rejected with 422 booking_too_long. The cap is service-wide; no workspace or plan setting raises it. AgentDraft's conflict engine locks time in five-minute buckets: a 30-minute booking writes 6 bucket rows, and the 480-minute maximum booking spans 96. AgentDraft syncs Google Calendar today; Microsoft 365 / Outlook calendar sync is planned, not yet shipped.

Executive assistant and autonomous outreach agents

Agents that communicate with users and claim meeting slots need both scheduling and communication scopes, but they must remain bounded by approval mechanisms for sensitive side effects:

{
  "agent_id": "agent_outreach_exec",
  "scopes": [
    "availability:read",
    "bookings:read",
    "bookings:write",
    "mailbox:read",
    "mailbox:write",
    "approvals:request"
  ]
}

An AgentDraft mailbox is an addressable inbox owned by one agent; giving each agent its own mailbox isolates blast radius, so one runaway agent exhausts its own quota rather than the whole sending domain. Mailboxes are counted per workspace, so per-agent isolation only goes as far as the plan's mailbox count. AgentDraft gives AI agents per-agent email inboxes with inbound webhooks, replies, and audit evidence. The number of inboxes a workspace can run at once is set by plan: 1 on Developer and Individual, 5 on Team, 25 on Scale, unlimited on Enterprise.

Securing agent webhooks and callback delivery

Scoped outbound requests cover only half of the network path. Autonomous systems rely heavily on webhooks to receive inbound email events and booking confirmations. If an adversary can forge incoming webhooks, they can spoof customer responses and trick an agent into executing unauthorized tasks.

Verifying signatures on every inbound webhook payload prevents these injection attacks. AgentDraft signs every webhook delivery with an X-AgentDraft-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256> header computed over <t>. followed by the raw request body, using the workspace's signing secret. The receiver verifies it; AgentDraft recommends that receivers reject any timestamp more than 300 seconds (5 minutes) from the current time to block replays, and its documented reference verifier does so. Signed webhook delivery is available on every plan, including the free Developer tier.

A reference verification handler implemented in Node.js demonstrates the required cryptographic steps:

import crypto from "node:crypto";

export function verifyAgentDraftWebhook({
  rawBody,
  signatureHeader,
  signingSecret,
  toleranceSeconds = 300,
}) {
  const parts = signatureHeader.split(",");
  const timestampPart = parts.find((p) => p.startsWith("t="));
  const signaturePart = parts.find((p) => p.startsWith("v1="));

  if (!timestampPart || !signaturePart) {
    throw new Error("Invalid signature header format");
  }

  const timestamp = parseInt(timestampPart.slice(2), 10);
  const receivedSig = signaturePart.slice(3);
  const now = Math.floor(Date.now() / 1000);

  if (Math.abs(now - timestamp) > toleranceSeconds) {
    throw new Error("Webhook delivery timestamp expired or skewed");
  }

  const payloadToSign = `${timestamp}.${rawBody}`;
  const computedSig = crypto
    .createHmac("sha256", signingSecret)
    .update(payloadToSign, "utf8")
    .digest("hex");

  const sigBufferA = Buffer.from(receivedSig, "hex");
  const sigBufferB = Buffer.from(computedSig, "hex");

  if (
    sigBufferA.length !== sigBufferB.length ||
    !crypto.timingSafeEqual(sigBufferA, sigBufferB)
  ) {
    throw new Error("Invalid webhook HMAC signature");
  }

  return true;
}

Using crypto.timingSafeEqual prevents timing analysis attacks that attempt to deduce signature bytes iteratively, following standards detailed in FIPS PUB 198-1: The Keyed-Hash Message Authentication Code (HMAC).

Human approval gates: pausing autonomous execution

Certain actions carry consequences that code cannot easily reverse. Deleting records, issuing client refunds, booking high-value sales meetings, and emailing non-standard contracts require human oversight. Permanent unattended write access introduces unnecessary risk. Systems should force the agent to request explicit human sign-off for sensitive mutations.

AgentDraft lets an agent pause any consequential action for human sign-off: it opens an approval request carrying a one-line summary and a JSON evidence payload, a person approves or denies it in the dashboard with an optional note, and the agent reads the outcome back. The gated action does not have to be one AgentDraft performs—a deploy, a migration, or a refund is gated the same way. Every transition lands in the append-only audit trail and fires an approval.* webhook.

The requesting agent decides for itself when to open an approval request. AgentDraft does not yet provide a policy engine that auto-requires approval by action class, amount threshold, or role, and there are no escalation chains or multi-approver quorums—a single workspace human resolves each request.

Approvals are decided in the AgentDraft dashboard. AgentDraft emails the workspace owner a notification linking to the queue, but the decision itself is made signed in—there are deliberately no approve-from-email links, because an unauthenticated one-click approve is an attack surface. Slack, Discord, Teams, SMS and push delivery are not available today.

Humans sign in to the dashboard with a passkey defined by the W3C Web Authentication specification (WebAuthn), with a magic link as the bootstrap and recovery path. Enterprise SSO (SAML/SCIM via WorkOS) is on the AgentDraft roadmap and not available today; agents authenticate with bearer API keys and humans with passkeys. The public changelog is at agentdraft.io/changelog and every user-visible change lands there.

Append-only audit trails for agent accountability

When an agent executes an API call, you must know what prompt, context, or key triggered the mutation. Standard application log lines disappear into high-volume aggregators and can be altered or pruned. Systems require immutable, structured event logging linked directly to identity keys.

AgentDraft records state-changing agent actions in an append-only audit trail. Every state-changing operation emits an audit record. Audit retention is per-tier and enforced on read as well as on write, so the retention claim holds even though deletion is lazy. AgentDraft does not hold formal compliance certifications (SOC 2, HIPAA, ISO 27001, etc.). It does keep an append-only audit trail.

A typical audit record includes the key prefix, agent identity, route called, parameters committed, and outcome:

{
  "event_id": "aud_01jb87v6x9e2a4q1z8m5",
  "timestamp": "2026-11-01T14:22:18.412Z",
  "agent_id": "agent_calendar_booking",
  "key_prefix": "avs_live_9b2e...",
  "action": "bookings:write",
  "status": "success",
  "resource_id": "bk_77192a8e",
  "metadata": {
    "buckets": 6,
    "duration_minutes": 30,
    "slot_start": "2026-11-02T10:00:00Z"
  }
}

Because the record captures the key prefix alongside the mutation payload, operators can inspect exactly which deployment version made an error. If an agent misbehaves, tracing the event ID isolates whether the model hallucinated or received invalid input from an external caller.

Workspace isolation and plan boundaries

Resource isolation must be enforced across workspace boundaries. Multi-tenant agent architectures require strict quotas to prevent runaway cost loops. AgentDraft is a proprietary hosted API; it is not open source and is not offered as a self-hosted or on-premise product.

AgentDraft plans are per workspace. Developer (free): 3 agents, 1 mailbox, 50 bookings a month. Individual: 3 agents, 1 mailbox, 500 bookings a month. Team: unlimited agents, 5 mailboxes, 5 seats, 10,000 bookings a month. Scale: unlimited agents, 25 mailboxes, 25 seats, 100,000 bookings a month. Enterprise: custom, with no mailbox cap. Freeform outbound email (not tied to a booking) starts at Team.

AgentDraft's free Developer tier includes one mailbox, which the workspace owner can move between its 3 agents; giving several agents their own inbox at the same time needs Team (5 mailboxes) or above. AgentDraft's Developer tier is free with no card required: 1 seat, 3 agents, 1 mailbox, 1 connected calendar, 50 bookings a month, and 7-day audit retention. Signed webhook delivery is included on the Developer tier, so a free workspace is a working sandbox for testing webhook handlers.

To configure scoped keys for your agents, visit AgentDraft at https://agentdraft.io/pricing. The free Developer tier requires no card and includes 3 agents and one mailbox per workspace.

Frequently asked questions

What happens when an agent makes an API call without the required scope?

The gateway rejects the request with HTTP status 403 Forbidden. The response contains an error string indicating the missing scope, such as missing_scope: bookings:write. The request does not reach internal application logic or database storage layers, preventing unauthorized data modification.

How are agent bearer tokens stored securely?

AgentDraft stores bearer keys hashed with Argon2id. The raw token string prefixed with avs_live_ is returned only once at creation time and cannot be retrieved from the database or dashboard later. If a key is lost, it must be revoked and regenerated.

Can an agent request its own human approval gate?

Yes. When an agent holds the approvals:request scope, it can create an approval request containing a one-line summary and an arbitrary JSON evidence payload. The agent then polls for the outcome or waits for an approval.* webhook event before continuing its task.

Why are approval decisions restricted to the dashboard?

Approvals must be made inside the authenticated AgentDraft dashboard using WebAuthn passkeys. External approval links sent via email or chat webhooks present significant security risks, as intercepted tokens or forwarded messages could allow unauthorized parties to confirm destructive agent actions.

How does AgentDraft prevent race conditions during calendar bookings?

The calendar conflict engine uses transactional condition expressions at the database layer. Time is locked in five-minute buckets. When an agent attempts to commit a booking, DynamoDB TransactWriteItems writes one row per bucket and verifies priority rules directly in storage. Two agents attempting to claim the same slot cannot both succeed.

Can multiple agents share a single mailbox on the Developer tier?

The free Developer tier provides one mailbox per workspace. The workspace owner can reassign this mailbox between agents, but running multiple dedicated, concurrent inboxes requires upgrading to the Team plan or higher.