Authentication
How to authenticate with the Chirpie API using API keys, and how to narrow a key with scopes.
All API requests require a valid API key passed in the Authorization header.
API Keys
API keys are prefixed with chirpie_sk_ followed by a 40-character random string. They are hashed with SHA-256 before storage, so we never store your raw key.
Authorization: Bearer chirpie_sk_aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789abGetting Your Key
From the Dashboard
- Go to chirpie.ai/dashboard/keys
- Click Create Key
- Give it a name (e.g., "Production", "My Bot")
- Copy the key immediately, because it won't be shown again
From the CLI
chirpie login # Opens browser, creates key automatically
chirpie keys create # Create additional keysFrom the API
# Requires an existing key to create a new one
curl -X POST https://chirpie.ai/api/v1/keys \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "My New Key" }'Key Management
| Action | Endpoint |
|---|---|
| Create | POST /api/v1/keys |
| List | GET /api/v1/keys |
| Revoke | DELETE /api/v1/keys?id=KEY_ID |
You can have up to 25 active API keys per account, and each key expires 90 days after it is created. Revoked keys stop working immediately.
Scopes
A key created without scopes can do everything the caller can. Name scopes and it can do less. That is the whole feature: hand an agent a key that may post but may not disconnect your accounts or mint more keys.
scopes is null for full access, and every key you already hold is one of those, so
nothing you are using today is narrowed and nothing needs changing until you want a narrower
key. Creating a key without naming any gives it whatever the caller has: null again from
the dashboard or from a full-access key, and a copy of the caller's own list from a key that
is already narrowed.
What each scope allows
| Scope | What it allows |
|---|---|
posts:read | Read posts, threads and drafts |
posts:write | Create, edit, schedule, delete, hide and unhide posts and threads |
accounts:read | List connected social accounts |
accounts:write | Connect, reconnect, deactivate and disconnect social accounts |
analytics:read | Read post metrics |
comments:read | Read comments on posts |
comments:write | Reply to, hide and delete comments |
media:write | Upload media |
keys:write | Create, list and revoke API keys |
Which scope each route needs
| Route | Method | Scope |
|---|---|---|
/api/v1/posts | POST | posts:write |
/api/v1/posts | GET | posts:read |
/api/v1/posts/:id | GET | posts:read |
/api/v1/posts/:id | PATCH | posts:write |
/api/v1/posts/:id | DELETE | posts:write |
/api/v1/posts/:id/hide | POST | posts:write |
/api/v1/posts/:id/unhide | POST | posts:write |
/api/v1/posts/:id/first-comment | POST | posts:write |
/api/v1/posts/:id/comments | GET | comments:read |
/api/v1/posts/:id/comments/:commentId | DELETE | comments:write |
/api/v1/posts/:id/comments/:commentId/hide | POST | comments:write |
/api/v1/posts/:id/comments/:commentId/reply | POST | comments:write |
/api/v1/threads | POST | posts:write |
/api/v1/media | POST | media:write |
/api/v1/accounts | GET | accounts:read |
/api/v1/accounts | POST | accounts:write |
/api/v1/accounts/:id | PATCH | accounts:write |
/api/v1/accounts/:id | DELETE | accounts:write |
/api/v1/accounts/x/keys | GET | accounts:read |
/api/v1/accounts/x/keys | PUT | accounts:write |
/api/v1/accounts/x/keys | DELETE | accounts:write |
/api/v1/analytics/posts/:id | GET | analytics:read |
/api/v1/keys | POST | keys:write |
/api/v1/keys | GET | keys:write |
/api/v1/keys | DELETE | keys:write |
There is no keys:read
Listing your keys needs keys:write, not a read scope. The key list is the inventory of
your account's credentials, and a key meant only to post has no business enumerating its
siblings. All three key methods sit behind the same scope.
A key can never grant a scope it does not hold
POST /api/v1/keys refuses to mint a key wider than the one making the request, with
403 insufficient_scope naming the scope it lacks. A key holding posts:write and
keys:write can create another posts:write key, and cannot create an accounts:write
one. Omitting scopes is not a way around it either: the new key then inherits exactly the
scopes of the key that made it. Escalation is not available even to a key that may create
keys.
What a refusal looks like
{
"error": {
"code": "insufficient_scope",
"message": "This API key does not have the posts:write scope. Create a key with posts:write and use that instead."
}
}It is a 403. The message always names the missing scope, because the next move is to mint
a key that has it.
A revoked or expired key hears that first. Chirpie checks the key's format, revocation,
expiry and burst limit before it checks scopes, so a 403 insufficient_scope really does
mean the key is good and the scope is missing.
Minting a scoped key
From the dashboard: on chirpie.ai/dashboard/keys, Create Key offers Full access or a checkbox list of permissions. The key list shows each key's scopes, so you can see at a glance which key can do what.
From the API:
curl -X POST https://chirpie.ai/api/v1/keys \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Publishing bot",
"scopes": ["posts:write", "media:write"]
}'From the SDK:
await chirpie.createKey({
name: "Publishing bot",
scopes: ["posts:write", "media:write"],
});From the CLI:
chirpie keys create --name "Publishing bot" \
--scope posts:write --scope media:writechirpie keys prints each key's scopes, or all for a key with full access.
From MCP: chirpie_create_key takes a scopes array. Leave it out for a key that can do
what the caller can.
Omitting scopes everywhere gives a key that can do exactly what the key or session making
the request can do: full access from the dashboard or from a key that has it, and a copy of
the caller's own list from a scoped key. An empty list is refused, because a key that can do
nothing is never what anyone meant.
Scopes on an AI app connected by signing in
An AI app connected to the hosted MCP server by signing in (Claude, ChatGPT,
Muse) holds no API key. Instead, Chirpie asks you on its own screen what the app may do:
Full access, which is every scope, or Read only, which is posts:read,
accounts:read, analytics:read and comments:read. The same checks apply to that
connection as to a key, so a read-only app asking to publish is refused with
403 insufficient_scope naming posts:write, and the app is only offered the tools its
scopes cover. See Read only or full access.
Security Best Practices
- Never commit keys to version control
- Use environment variables:
CHIRPIE_API_KEYfor CLI/MCP - Rotate keys regularly: create a new key, update your app, revoke the old one
- Don't share keys: create separate keys for each service or team member
- Give each key only the scopes it needs: a publishing bot rarely needs
accounts:writeorkeys:write. See Scopes - Never use keys in client-side code: API keys in browser JavaScript are visible to anyone