Install
openclaw skills install keychainsCall any API without leaking credentials. Keychains proxies requests and injects real tokens server-side — your agent never sees them.
openclaw skills install keychainskeychains.dev is a credential proxy for AI agents. Instead of real API keys and OAuth tokens, you use placeholders like {{OAUTH2_ACCESS_TOKEN}}. Keychains injects the real credentials server-side — your agent never sees them.
keychains curl https://api.github.com/user/repos \
-H "Authorization: Bearer {{OAUTH2_ACCESS_TOKEN}}"
keychains curl https://api.github.com/user/repos -H 'Authorization: Bearer {{OAUTH2_ACCESS_TOKEN}}'"keychains curl https://slack.com/api/chat.postMessage -X POST -H 'Authorization: Bearer {{OAUTH2_ACCESS_TOKEN}}' -H 'Content-Type: application/json' -d '{\"channel\":\"#general\",\"text\":\"Hello!\"}'"keychains curl https://api.stripe.com/v1/customers?limit=5 -H 'Authorization: Bearer {{STRIPE_SECRET_KEY}}'"keychains curl 'https://gmail.googleapis.com/gmail/v1/users/me/messages?maxResults=10' -H 'Authorization: Bearer {{OAUTH2_ACCESS_TOKEN}}'"keychains curl instead of curl. Put {{PLACEHOLDER}} where the credential goes.No credentials ever pass through the agent. The user controls everything from keychains.dev/dashboard.
Put {{VARIABLE_NAME}} where you'd normally put the real credential — in headers, body, or query params.
| Prefix | Type | Examples |
|---|---|---|
OAUTH2_ | OAuth 2.0 | {{OAUTH2_ACCESS_TOKEN}}, {{OAUTH2_REFRESH_TOKEN}} |
OAUTH1_ | OAuth 1.0 | {{OAUTH1_ACCESS_TOKEN}}, {{OAUTH1_REFRESH_TOKEN}} |
| Anything else | API key | {{STRIPE_SECRET_KEY}}, {{OPENAI_API_KEY}} |
Keychains auto-detects the provider from the URL.
When keychains returns an approval link, show it to the user and poll:
keychains curl https://api.github.com/user/repos \
-H "Authorization: Bearer {{OAUTH2_ACCESS_TOKEN}}"
# → "Authorize at: https://keychains.dev/approve/abc123xyz"
keychains wait https://keychains.dev/approve/abc123xyz --timeout 800
keychains curl https://api.github.com/user/repos \
-H "Authorization: Bearer {{OAUTH2_ACCESS_TOKEN}}"
# → works now
For TypeScript/Node.js agents, @keychains/machine-sdk provides keychainsFetch() — a drop-in fetch() replacement with the same automatic registration and credential handling as the CLI.
npm install @keychains/machine-sdk
import { keychainsFetch, KeychainsError } from '@keychains/machine-sdk';
try {
const res = await keychainsFetch('https://api.github.com/user/repos', {
headers: { Authorization: 'Bearer {{OAUTH2_ACCESS_TOKEN}}' },
});
console.log(await res.json());
} catch (err) {
if (err instanceof KeychainsError && err.approvalUrl) {
console.log('Please approve:', err.approvalUrl);
}
}
| SDK | Install | Description |
|---|---|---|
| Python SDK | pip install keychains | Drop-in requests replacement. keychains.get(), keychains.post(), keychains.Session(). |
| Client SDK | npm install @keychains/client-sdk | TypeScript SDK for delegated environments (VMs, cloud functions). |
Your request goes through keychains.dev, which replaces {{PLACEHOLDER}} variables with real credentials from the user's vault, forwards to the upstream API, and returns the response as-is. Real credentials never reach the agent. The user controls access from keychains.dev/dashboard.
More details: security whitepaper · privacy policy · terms of service
Got an approval link? Normal. Show it to the user, wait for approval, retry.
Template variable not replaced? You're using regular curl/fetch instead of keychains curl / keychainsFetch().
Tested with the following providers so far: