# API Keys for Agents: Read, Write, Publish, and an Audit Trail You Will Actually Check

_2026-10-01 · Giving AI agents Shager API keys safely: what read, write and publish allow, the sandbox claim, key storage, rate limits, key rotation and the audit log._

Give each agent the narrowest Shager key that covers its job: `read` for reporting, `write` for drafting, and `publish` only for the step that schedules posts. Keep keys in environment variables or the MCP client's configuration, never in prompts. Revoke and rotate from the agents dashboard, and read its audit log, which lists every call an agent makes, at least weekly while a new agent is bedding in.

That is the policy. The rest of this article is the reasoning and the mechanics, because a policy nobody understands gets bypassed the first time it is inconvenient.

## What can each scope do?

Every Shager key carries one or more of three scopes. Each operation in the API declares the scope it needs; you can see it as `x-scope` on each operation in the [OpenAPI document](https://app.shager.ai/api/v1/openapi.json). The MCP tools are the same operations, so the same rules apply over MCP.

| Scope | Operations |
|---|---|
| read | whoami, list_workspaces, list_channels, list_groups, list_posts, get_post, get_analytics |
| write | create_workspace, create_connect_link, create_group, upload_media_from_url, create_post, request_approval, cancel_post |
| publish | schedule_post |

Two things in that table deserve attention.

First, `publish` contains a single operation, and it is the one that makes content go live on client accounts. Isolating it is the point. An agent can do almost all of the work, from uploading media to drafting per-account captions to sending approval links, without ever being able to put a post on a Page.

Second, `cancel_post` sits under `write`. A drafting agent can pull a scheduled post back to draft. That is deliberate: stopping a mistake should be easier than making one.

A key used without the required scope gets an error in the standard shape, `{ "error": { "code", "message" } }`, with HTTP 403. Agents should report that error rather than look for another way around it, and your standing instructions should say so.

## Where keys come from

There are two paths.

**A human creates the key** at `app.shager.ai/dashboard/agents`, choosing the scopes. This is the normal path for an agency adding an agent to an existing account.

**The agent signs itself up.** An agent with no account calls `POST https://app.shager.ai/api/v1/agent/signup` with an `agent_name` and, ideally, a `contact_email` for its human. It receives an `api_key`, shown once, and a `claim_url`. That key works immediately in a sandbox, which can create up to five workspaces, upload media, create drafts and read. It cannot schedule or publish, create client connect links, or send approval requests.

```bash
curl -s https://app.shager.ai/api/v1/agent/signup \
  -H "Content-Type: application/json" \
  -d '{"agent_name":"Northwind content agent (Claude Code)",
       "contact_email":"ops@northwind-agency.example",
       "purpose":"Draft weekly posts for 3 retail clients"}'
```

Those restrictions lift only when a human opens `claim_url`, signs in and claims the organization. At that moment the human decides whether the agent's key also gets `publish`. If nobody claims it, the sandbox expires after seven days. The full recipe is published at [/auth.md](/auth.md), and the walkthrough is in [agent self-signup and claim](/en/blog/agent-self-signup-and-claim).

The design choice behind this is simple: an agent can explore and prepare on its own, but connecting real accounts and publishing to them always passes through a person.

## How should agents store and send keys?

Shager keys are bearer tokens sent as `Authorization: Bearer shgr_live_...`. The defining property of a bearer token, as [RFC 6750](https://www.rfc-editor.org/rfc/rfc6750) puts it, is that any party in possession of it can use it. Nothing else proves identity. So the whole job is not leaking it.

In practice:

1. **Environment variables, referenced by config.** Claude Code supports `${VAR}` expansion in the headers of `.mcp.json`, per its [MCP documentation](https://code.claude.com/docs/en/mcp). The file can be committed; the key stays in the environment.
2. **Never in a prompt.** A key pasted into chat ends up in transcripts and logs, and sometimes in shared conversations.
3. **Never in a client's folder.** If you keep per-client context folders, keys do not belong in them. Those folders get shared and zipped.
4. **One key per agent role.** A reporting job, a drafting agent and a scheduling step should not share a key. When one leaks, you revoke one.

```json
{
  "mcpServers": {
    "shager": {
      "type": "http",
      "url": "https://app.shager.ai/api/mcp",
      "headers": { "Authorization": "Bearer ${SHAGER_DRAFT_KEY}" }
    }
  }
}
```

Today Shager's MCP server authenticates with this API key header. OAuth sign-in for MCP is coming; until then, clients that only support OAuth connectors need to wait or call the REST API from code. [Shager and ChatGPT](/en/blog/shager-and-chatgpt) explains what that means for ChatGPT specifically.

## Rotation without downtime

Keys should be rotated when a person with access leaves, when a key may have been exposed, and on a schedule you can stick to. The sequence that avoids a gap:

1. Create a new key with the same scopes in the dashboard.
2. Update the environment variable where the agent runs.
3. Run `whoami` with the new key to confirm it works and shows the expected scopes.
4. Revoke the old key.

Revocation takes effect for every call made with that key, MCP and REST alike.

## Rate limits are part of safety

Each key is limited to 120 requests per minute. A well-behaved agent never gets near it: a full weekly draft for a client is a few dozen calls. When an agent does hit HTTP 429, it usually means a loop, an agent retrying the same failing call over and over. Your instructions should treat 429 as a stop signal, not a reason to retry faster. Back off, report, and let a human look.

## Reading the audit log

The agents dashboard shows an audit log of every agent call. It is only useful if someone reads it. What to look for in the first weeks with a new agent:

1. **Calls you did not expect.** A reporting agent calling `create_post` means its instructions or its key are wrong.
2. **Workspace drift.** Calls touching a client the task was not about. This is the early warning for the cross-client mistake discussed in [one agent, many clients](/en/blog/one-agent-many-clients).
3. **Error bursts.** Repeated 403s mean the agent keeps attempting something outside its scope. Fix the instructions, not the scope.

After a few clean weeks, a quick weekly skim is enough.

## FAQ

### Should the agent ever hold the publish scope?

It can, once you trust its drafting and you have a review step, either client approval links or an internal reviewer. A pattern that works well is to keep `publish` on a separate key used only by a short, scripted scheduling step, so the agent that writes is not the agent that ships.

### What happens to an agent's key when the sandbox is claimed?

The organization becomes the human's, and the key keeps working inside it. The human chooses at claim time whether that key may also schedule and publish. They can revoke it or create new ones from the dashboard at any time.

### Can a key be limited to one client workspace?

Scopes limit what a key can do, not which workspace it acts on. Separate clients through workspaces, per-client context and a habit of resolving the workspace by name before every write, and use separate keys per role so revocation stays surgical.

## Where to go next

If you are setting up your first agent, the [agent entry point](/en/agents) shows both paths to a key. Start with a `write` key and add `publish` only when the drafts have earned it.

---
Source: https://shager.ai/en/blog/api-keys-scopes-and-audit
