Scopes & Permissions
Control what each API key can access with fine-grained scopes.
API keys are scoped — each key can only access the endpoints its scopes allow. This lets you follow the principle of least privilege: give each integration only the permissions it needs.
Available Scopes#
| Scope | Grants Access To |
|---|---|
tutor | AI chat, conversations, usage quota |
decks | Deck CRUD, cards, import/export, FSRS reviews |
progress | Learning stats, study insights, vocabulary analytics, activity |
curriculum | Course units, lessons, vocabulary search, due vocabulary |
exercises | AI-powered exercise generation |
pronunciation | Pronunciation scoring and service health |
webhooks | Webhook registration, listing, deletion, and testing |
Default Scopes#
When you create a key without specifying scopes, it defaults to ["tutor"]:
curl -X POST https://api.chamelingo.com/api/v1/keys \
-H "Cookie: better-auth.session_token=..." \
-H "Content-Type: application/json" \
-d '{"name": "Chat Only Agent"}'
# Key gets scopes: ["tutor"]
Requesting Specific Scopes#
Pass the scopes array when creating a key to request specific permissions:
curl -X POST https://api.chamelingo.com/api/v1/keys \
-H "Cookie: better-auth.session_token=..." \
-H "Content-Type: application/json" \
-d '{
"name": "Full Access Agent",
"scopes": ["tutor", "decks", "progress", "curriculum", "exercises", "pronunciation", "webhooks"]
}'
const response = await fetch("https://api.chamelingo.com/api/v1/keys", {
method: "POST",
headers: {
"Cookie": "better-auth.session_token=...",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Deck Manager",
scopes: ["decks", "progress"],
}),
});
const { key, scopes } = await response.json();
// key: "ck_live_..."
// scopes: ["decks", "progress"]
Scope Errors#
If a key attempts to access an endpoint it doesn't have the scope for, the API returns 403 Forbidden:
{
"success": false,
"error": {
"title": "Forbidden",
"message": "This API key does not have the \"decks\" scope.",
"retryable": false,
"statusCode": 403,
"code": "FORBIDDEN"
}
}
Scope-to-Endpoint Mapping#
tutor scope#
| Method | Endpoint | Description |
|---|---|---|
| POST | /chat | Send message (SSE streaming) |
| GET | /conversations | List conversations |
| GET | /conversations/:id | Get conversation with messages |
| DELETE | /conversations/:id | Delete a conversation |
| GET | /usage | Check quota and rate limits |
decks scope#
| Method | Endpoint | Description |
|---|---|---|
| GET | /decks | List decks |
| POST | /decks | Create a deck |
| GET | /decks/:id | Get deck with cards |
| PATCH | /decks/:id | Update deck metadata |
| DELETE | /decks/:id | Delete a deck |
| GET | /decks/:id/export | Export deck (JSON/CSV) |
| POST | /decks/:id/cards | Add a card |
| PATCH | /cards/:cardId | Update a card |
| DELETE | /cards/:cardId | Delete a card |
| POST | /decks/:id/cards/import | Bulk import cards |
| GET | /decks/:id/cards/review | Get cards due for review |
| POST | /reviews | Submit FSRS review ratings |
progress scope#
| Method | Endpoint | Description |
|---|---|---|
| GET | /progress/summary | XP, level, streaks, hearts |
| GET | /progress/insights | Accuracy, weak areas |
| GET | /progress/vocabulary | Vocabulary overview stats |
| GET | /progress/vocabulary/difficult | List difficult words |
| GET | /progress/vocabulary/trending | Improving and struggling words |
| GET | /progress/activity | Daily activity data |
curriculum scope#
| Method | Endpoint | Description |
|---|---|---|
| GET | /curriculum/units | List all units |
| GET | /curriculum/units/:id | Get unit with lessons and grammar |
| GET | /curriculum/vocabulary/search | Semantic vocabulary search |
| GET | /curriculum/vocabulary/due | Vocabulary cards due for review |
exercises scope#
| Method | Endpoint | Description |
|---|---|---|
| POST | /exercises/generate | Generate AI exercises on topic |
pronunciation scope#
| Method | Endpoint | Description |
|---|---|---|
| POST | /pronunciation/score | Score pronunciation from audio |
| GET | /pronunciation/health | Check service availability |
webhooks scope#
| Method | Endpoint | Description |
|---|---|---|
| POST | /webhooks | Register a webhook |
| GET | /webhooks | List webhooks |
| DELETE | /webhooks/:id | Delete a webhook |
| POST | /webhooks/:id/test | Send a test event |
Best Practices#
-
Least privilege — Only grant the scopes your integration actually needs. A study reminder bot only needs
decksandprogress, nottutor. -
Separate keys for separate concerns — Create different keys for different integrations rather than sharing one key with all scopes.
-
Rotate keys — If a key is compromised, revoke it immediately via
DELETE /api/v1/keys/:idand create a new one. -
Check scopes before calling — If you know your key's scopes, avoid calling endpoints that will return 403. The
GET /api/v1/keysendpoint (session auth) shows each key's scopes.
You can create up to 5 active API keys per account. Use this to create purpose-specific keys — one for chat, one for deck management, one for webhooks — rather than one key with all scopes.