Access Keys

Authenticate your backend’s requests to Agent Handler, and mint user-scoped keys from your production key.

An Access Key authenticates your backend’s requests to the Agent Handler API and to the MCP server. Every call carries one in the Authorization header:

Authorization: Bearer <YOUR_API_KEY>

The word Bearer is required and case-sensitive. The key never appears anywhere else.

Two kinds of key matter, and the difference is what most security reviews turn on:

  • Your production key is organization-wide. It reaches every Tool Pack and every Registered User you own. Your backend holds it; nothing else should.
  • A user-scoped key is minted from it and names specific Registered Users and Tool Packs. It cannot call tools as any Registered User it does not name, whatever ID appears in the MCP URL.

Anything an agent session, a per-tenant deployment, or a downstream service carries should be the second kind. Scoping access per user walks the exchange end to end.

Production vs test

ProductionTest
How manyAny number, alongside one master key that cannot be revokedAny number
Environment scopeProduction Registered Users, real Connector calls, production audit logSandbox: test Registered Users, test data, separate audit log
RotatableYes, and each key can be revoked or given an expiry dateCreated and deleted at will
Use caseYour live backend serving real customersDevelopment, staging, CI

Test keys give you a clean sandbox. Credentials, Registered Users, and tool calls created with a test key are isolated from production. The trade-off: you can’t use a test key to call production data, and you can’t use a production key to read test resources. If you mix them up, you’ll see 404 not found on resources that you’re sure exist.

Manage both at Settings → API Keys.

Generating keys

Master key. One production key exists when your account is provisioned. It carries a master badge, and it cannot be revoked. Click Regenerate token to rotate it. Rotation invalidates the old value immediately, so propagate the new one to every backend before clicking, or you’ll have an outage as long as it takes to redeploy.

Additional production keys. Click + Create new key and name it. You can hold as many as you need, which lets one backend service, agent surface, or environment carry its own credential. Each key’s menu offers Copy key ID, Regenerate, and Revoke. Creating and revoking keys requires the Manage organization API keys permission.

Test keys. Click + Create new key, name it (a hint about what it’s for, such as “ci”, “alice-dev”, “staging-2”), copy the value. The value is shown once; if you lose it, delete the key and create a new one.

Every key’s value is shown once at creation, production and test alike.

Expiry and restrictions

A production key can carry an optional expiry date, set when you create it. The key stops working on that date, which suits a contractor’s access or a time-boxed migration.

A production key can also be restricted to specific Tool Packs, specific Registered Users, or both. A restricted key’s row summarizes its reach, for example “2 Tool Packs, all Registered Users”. Three things to know before you rely on it:

  • Tool Pack and Registered User restrictions are enforced on every tool call. A key restricted to one Registered User cannot call tools as another; the out-of-scope call returns 404, not that user’s data.
  • The dashboard does not let you choose scopes, so a key restricted here still reaches the management endpoints and can read and write configuration. To withhold that too, mint the key through the API with ["runtime:all"] and leave management:all off. See Scoping access per user.
  • A key’s restrictions cannot be edited after creation. To change them, revoke the key and create a replacement.

Rotation

Rotate production keys at least once a year, and immediately if you suspect one leaked.

The cleanest rotation is to create a second key, move traffic to it, then revoke the first, which avoids any window where no valid key is deployed. Use it wherever you can:

  1. Create a new production key and copy its value.
  2. Roll it out to every backend that uses the old one, meaning secret manager update, release, confirm.
  3. Confirm traffic on the new key.
  4. Revoke the old key.

The master key cannot be revoked, so rotating that one specific key still means regenerating it in place:

  1. Roll the replacement value out to every backend first.
  2. Click Regenerate token. The old value stops working at the moment you click.

If a key has leaked publicly, by being committed to a repo or posted in a chat, revoke or regenerate first and roll out second. The cost of an outage is much lower than the cost of someone else having your key.

User-scoped Access Keys

Everything on the API Keys page has an API equivalent under /api/v1/access-keys/. That is how you mint a key that reaches one user, one tenant, or one service, rather than passing your organization-wide key around.

POST /api/v1/access-keys/ accepts tool_pack_ids, registered_user_ids, scopes, and either expires_at or expires_in:

curl -X POST https://ah-api.merge.dev/api/v1/access-keys/ \
-H "Authorization: Bearer $MERGE_AGENT_HANDLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "session-user_a3f9b2",
"registered_user_ids": ["f9813dd5-e70b-484c-91d8-00acd6065b07"],
"tool_pack_ids": ["1d8b6f4a-1b3e-4f9b-9d3a-3a8e8b9c0d1f"],
"scopes": ["runtime:all"],
"expires_in": 3600
}'

The key value comes back once, on the create response. After that only key_masked is readable.

Two scopes exist. runtime:all permits MCP tool calls; management:all permits the resource management endpoints. A session key wants the first and not the second.

For the full exchange, the boundaries it enforces, and why editing the Registered User ID in the MCP URL cannot reach another user, see Scoping access per user.

Narrower only, never wider

A key you create through the API can be narrower than the key that created it, never wider. Each of tool_pack_ids, registered_user_ids, scopes, and the expiry has to be a subset of the calling key’s own grant. A key limited to one Tool Pack cannot mint a key that reaches a second one, so a leaked scoped key cannot be escalated into a broader one.

Passing null for tool_pack_ids or registered_user_ids means unrestricted on that dimension, and it is accepted only when the calling key is itself unrestricted there. Omit both from an agent’s key and you get a key as wide as the one that minted it.

Which keys a key can rotate or revoke

The same subset rule governs POST /api/v1/access-keys/{key_id}/regenerate/ and POST /api/v1/access-keys/{key_id}/revoke/. A key can act on itself, or on a key whose grant it fully covers:

DimensionThe rule
EnvironmentBoth keys are production, or both are test
ScopesThe target’s scopes are a subset of the acting key’s
ExpiryThe target expires no later than the acting key
Tool PacksThe target’s Tool Packs are a subset of the acting key’s
Registered UsersThe target’s Registered Users are a subset of the acting key’s

Anything else returns 403, with the acting and target key ids in the response so you can tell which pair was refused.

Coverage rather than lineage is what makes this usable in both directions. Every key minted through the API is covered by the key that minted it, so a service can always rotate its own children. Keys created in the dashboard have no parent key, and your organization’s unrestricted production key covers them, so they stay manageable from the API too.

The expiry clause is the part worth planning around: a one-hour scoped key cannot rotate your never-expiring production key, which is what stops a short-lived key leak from turning into a permanent one. If a scoped service needs to rotate a long-lived key, give the service a long-lived key of its own rather than widening the scoped one.

Allowed callback origins

OAuth callbacks from third parties (and from Link) need to redirect somewhere on your domain. That domain has to be on Agent Handler’s allow-list - a security check to stop someone else’s domain from receiving your users’ OAuth codes.

At Settings → API Keys → Allowed callback origins, add every domain that hosts your frontend. The match is exact:

  • app.example.com covers https://app.example.com/... only.
  • staging.example.com is a separate entry.
  • localhost:3000 is its own entry - add the ports you use in development.

If you skip this, OAuth callbacks fail with “unauthorized origin” and your users see a broken auth flow. See Troubleshooting → Allowed callback origin error.

Storage

Treat keys like any other secret.

  • Don’t commit them. Gitignored .env for local development; secret manager (AWS Secrets Manager, Vault, Doppler, 1Password) in deployed environments.
  • Don’t expose them client-side. Production keys authorize calls on behalf of every customer’s Registered User. Always proxy MCP calls through your backend.
  • Don’t share them across environments, services, or users. Each environment, and ideally each service, holds its own copy. For anything that runs on behalf of one end user, mint a user-scoped key rather than handing over a copy of your organization-wide one.

The Audit Trail records every key creation, regeneration, and deletion. When one key rotates another through the API, the entry names both the acting key and the target key, so you can read a rotation chain without correlating requests by hand. For the Agent Handler for Employees setup, end users authenticate via OAuth through your IdP rather than an API key.

Next

Bring your own OAuth app per Connector with Application Credentials.