Use Cases
Pre-approved permission tokens
When you know exactly which APIs your agent needs, skip the back-and-forth. Create a scoped permission with predefined scopes and get instant access after one user approval.
When to use scoped permissions
Scoped permissions are ideal when your agent's API needs are known upfront and won't change:
Predictable workflows
Your agent always reads GitHub repos and sends Slack messages — nothing more.
Security-sensitive contexts
You want to guarantee an agent can never request additional scopes beyond what was approved.
Production deployments
Locked-down access for production bots where scope creep is unacceptable.
Compliance requirements
Audit-friendly: the approved scopes are exactly what the permission allows, forever.
Scoped vs. Wildcard
| Scoped | Wildcard | |
|---|---|---|
| Scopes defined | Upfront, at creation | Dynamically, as needed |
| Scope changes | Locked after approval | Can grow over time |
| User interaction | One approval, then done | Approve each new scope |
| Missing scope | Hard 403, no recourse | 403 with approval URL |
| Best for | Production, compliance | Exploration, dev agents |
Create a scoped permission
terminal
$ keychains permissions create \--name "github-bot" \--scopes "github::repo,github::read:user" \--allow-delegation> Permission created: pr_scoped_f4e5> Mode: scoped> Requested scopes: github::repo, github::read:user> Status: pending — waiting for user approval> Approval URL: https://keychains.dev/approve/pr_scoped_f4e5Note: With scoped permissions, the user sees all requested scopes at once and can approve a subset. After approval, the set is locked — your agent cannot request additional scopes.
Using from code
agent.ts
import { createPermissionRequest, proxyFetch } from '@keychains.dev/machine-sdk';// Create with explicit scopesconst { permission, approvalUrl } = await createPermissionRequest({name: 'github-bot',requestedScopes: ['github::repo', 'github::read:user'],});// Send approvalUrl to the user// After approval, all calls within scope just work:const repos = await proxyFetch('https://api.github.com/user/repos', {permissionRequestId: permission.id});Tip: If your agent tries to call an API that requires a scope not in its approved set, it gets a hard 403 — no approval URL, no retry. This is by design for security.