After this page an agent knows which key to send to which route, how to keep test work free, and what each authentication error means.
Three kinds of key exist. A Content key starts with tf_content_ and is either a live key (spends credits, works in defaultClassroomId) or a test key (tf_content_test_, spends nothing, works only in sandboxClassroomId). A legacy key starts with tf_platform_ and signs only the account routes. Register no longer issues legacy keys; an older account may still hold one.
Send the key
Every authenticated request sends a Content API key in the Authorization header with the Bearer scheme:
curl "https://api.tutorflow.io/v1/content/classrooms/$CLASSROOM_ID/courses" \
-H "Authorization: Bearer tf_content_..."The live key that register returns as contentApiKey works on the Content API (/v1/content/**) and on the Agent Platform account routes (/v1/platform/agent/*).
Key kinds
| Prefix | Kind | Use |
|---|---|---|
tf_content_ | Live Content API key | All content work in your default classroom, and the account routes. Spends credits. |
tf_content_test_ | Test Content API key | Content work in the sandbox classroom only. Spends nothing. |
tf_platform_ | Legacy Agent Platform key | Only the account routes. It reaches no content route. Do not build on it. |
Live and test keys
A test key works only in your account's sandbox classroom. Priced routes answer with canned content and charge nothing, webhooks go only to test endpoints, and the learner routes are not available. Use it while you build; switch to a live key and defaultClassroomId when you go live. See Test Mode.
| Live key | Test key | |
|---|---|---|
| Classroom | defaultClassroomId | sandboxClassroomId |
| Credits | Spent | Never spent |
/v1/platform/agent/* | Yes | No, 403 platform_content_key_too_narrow |
GET /v1/platform/agent/account, called with a live key, returns both classroom ids. sandboxClassroomId is null until the account has a test key. A test key finds its sandbox with GET /v1/content/classrooms.
Where each key works
| Route | tf_content_ | tf_content_test_ | tf_platform_ |
|---|---|---|---|
/v1/content/** | Yes | Sandbox classroom only | No, 401 content_platform_key_not_accepted |
/v1/platform/agent/* (account, billing, email verification) | Yes, see below | No | Yes |
/v1/platform/health, /v1/platform/lti/registrations | Yes, see below | No | Yes |
The old /v1/platform content routes (/videos, /slides, /courses, /tests, /modules, /games, /evaluations, /learners) and /v1/platform/webhooks were removed on 2026-10-16, so no key reaches them. See Migrating to the Content API.
The account routes move money and manage the account, so they take only a tf_content_ key that can do everything: a live key of an agent account, with every classroom and the content:read, content:write, content:generate, and webhooks:manage scopes. The keys that registration and the dashboard issue are like that. A narrower key gets 403 platform_content_key_too_narrow.
Get a key
- From the API:
POST /v1/platform/agent/registerreturnscontentApiKey, a livetf_content_key. See Quick Start. No API route creates a second key. - From the dashboard: the developer signs in at tutorflow.io, opens the Agent Platform dashboard, and creates a key. Choose Live or Test as the mode. The dashboard creates only
tf_content_keys, and it is the only place that creates test keys.
The raw key is shown once, at creation. TutorFlow stores only a hash.
Rotate and revoke
Rotate or revoke a key in the Agent Platform dashboard. Rotation returns a new raw key once. Put it in your secret store, restart the clients that use the old one, then revoke the old one if it is still active.
Keep keys safe
- Keep keys in environment variables or a secrets manager, never in source code or in a browser.
- Use test keys in development and CI.
- Rotate a key at once if it may have leaked.
Errors
The Content API answers in its own envelope with a content_* code. See Errors.
Missing or invalid key on the Content API:
{
"error": {
"code": "content_invalid_api_key",
"message": "Invalid or missing Content Integration API key",
"status": 401,
"requestId": "..."
}
}A tf_platform_ key on the Content API:
{
"error": {
"code": "content_platform_key_not_accepted",
"message": "Agent Platform keys (tf_platform_) do not work on the Content API. Use a tf_content_ key: agent register returns one as contentApiKey, and the agent dashboard issues new ones.",
"status": 401,
"requestId": "..."
}
}A test key or a narrow key on an account route:
{
"error": {
"code": "platform_content_key_too_narrow",
"message": "Account routes take a live tf_content_ key with every classroom and the content:read, content:write, content:generate and webhooks:manage scopes, like the key register issues.",
"status": 403
}
}A missing or unknown key on an account route:
{
"error": {
"code": "platform_invalid_api_key",
"message": "Invalid or missing Platform API key",
"error": "Unauthorized",
"statusCode": 401,
"status": 401
}
}Do not retry any of these with the same key: fix the key first.
A call to a removed /v1/platform content route answers 410 platform_route_removed with any key, or none. See What happens on errors.
For every other error, see Error Handling.