Scopes & Permissions

Control what each API key can access with fine-grained scopes.

5 min read

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#

ScopeGrants Access To
tutorAI chat, conversations, usage quota
decksDeck CRUD, cards, import/export, FSRS reviews
progressLearning stats, study insights, vocabulary analytics, activity
curriculumCourse units, lessons, vocabulary search, due vocabulary
exercisesAI-powered exercise generation
pronunciationPronunciation scoring and service health
webhooksWebhook registration, listing, deletion, and testing

Default Scopes#

When you create a key without specifying scopes, it defaults to ["tutor"]:

bash
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:

bash
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"]
  }'
TypeScript
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:

JSON
{
  "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#

MethodEndpointDescription
POST/chatSend message (SSE streaming)
GET/conversationsList conversations
GET/conversations/:idGet conversation with messages
DELETE/conversations/:idDelete a conversation
GET/usageCheck quota and rate limits

decks scope#

MethodEndpointDescription
GET/decksList decks
POST/decksCreate a deck
GET/decks/:idGet deck with cards
PATCH/decks/:idUpdate deck metadata
DELETE/decks/:idDelete a deck
GET/decks/:id/exportExport deck (JSON/CSV)
POST/decks/:id/cardsAdd a card
PATCH/cards/:cardIdUpdate a card
DELETE/cards/:cardIdDelete a card
POST/decks/:id/cards/importBulk import cards
GET/decks/:id/cards/reviewGet cards due for review
POST/reviewsSubmit FSRS review ratings

progress scope#

MethodEndpointDescription
GET/progress/summaryXP, level, streaks, hearts
GET/progress/insightsAccuracy, weak areas
GET/progress/vocabularyVocabulary overview stats
GET/progress/vocabulary/difficultList difficult words
GET/progress/vocabulary/trendingImproving and struggling words
GET/progress/activityDaily activity data

curriculum scope#

MethodEndpointDescription
GET/curriculum/unitsList all units
GET/curriculum/units/:idGet unit with lessons and grammar
GET/curriculum/vocabulary/searchSemantic vocabulary search
GET/curriculum/vocabulary/dueVocabulary cards due for review

exercises scope#

MethodEndpointDescription
POST/exercises/generateGenerate AI exercises on topic

pronunciation scope#

MethodEndpointDescription
POST/pronunciation/scoreScore pronunciation from audio
GET/pronunciation/healthCheck service availability

webhooks scope#

MethodEndpointDescription
POST/webhooksRegister a webhook
GET/webhooksList webhooks
DELETE/webhooks/:idDelete a webhook
POST/webhooks/:id/testSend a test event

Best Practices#

  1. Least privilege — Only grant the scopes your integration actually needs. A study reminder bot only needs decks and progress, not tutor.

  2. Separate keys for separate concerns — Create different keys for different integrations rather than sharing one key with all scopes.

  3. Rotate keys — If a key is compromised, revoke it immediately via DELETE /api/v1/keys/:id and create a new one.

  4. Check scopes before calling — If you know your key's scopes, avoid calling endpoints that will return 403. The GET /api/v1/keys endpoint (session auth) shows each key's scopes.

Tip

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.