After this guide an agent can tell from error.code alone whether to fix the request, retry, top up, or stop. Key kinds are defined in Terms.
Two surfaces, one shape
An agent calls two surfaces, and both answer errors as an error object with a stable code. Branch on error.code, never on the message.
| Surface | Routes | Codes | Full reference |
|---|---|---|---|
| Content API | /v1/content/** | content_* | Content API Errors |
| Agent Platform account layer | /v1/platform/agent/*, /v1/platform/health, /v1/platform/lti/* | platform_* and a few others | This page |
A Content API error:
{
"error": {
"code": "content_invalid_api_key",
"message": "Invalid or missing Content Integration API key",
"status": 401,
"requestId": "8f9c2a4e-3b1d-4c6e-9a7f-2d5e8b1c0a93"
}
}Every Content API error carries requestId. Quote it when you contact support. An Agent Platform error has code, message, and status, and validation errors add details. Some Agent Platform errors also carry error (such as "Unauthorized") and statusCode, which repeat the status; ignore them.
A removed /v1/platform content route answers with a 410 and a link to the migration guide, with any key or none:
{
"error": {
"code": "platform_route_removed",
"message": "This Agent Platform content route was removed. Use the Content API with a tf_content_ key: https://tutorflow.io/agent-platform/docs/guides/content-api-migration",
"migrationGuide": "https://tutorflow.io/agent-platform/docs/guides/content-api-migration",
"status": 410
}
}Agent decision table
error.code | Status | Retry? | What to do |
|---|---|---|---|
content_invalid_request | 400 or 422 | No | Fix the request. error.details names each bad field. |
content_invalid_api_key | 401 | No | The key is wrong, revoked, or expired. Use another key. |
content_platform_key_not_accepted | 401 | No | You sent a tf_platform_ key to the Content API. Use a tf_content_ key; see Migrating to the Content API. |
platform_content_key_too_narrow | 403 | No | An account route got a test key or a key without full access. Use the live key from registration or the dashboard. |
content_insufficient_scope, content_classroom_not_allowed | 403 | No | The key may not do this, or not in this classroom. Use defaultClassroomId. |
content_not_found | 404 | No | The resource is not there, or not in this classroom. A test key sees only the sandbox. |
content_payment_required | 402 | After a top-up | Not enough credits. For an agent key the error says how to top up; see When credits run out. Retry with the same Idempotency-Key. |
content_payment_failed | 402 | After it is resolved | A payment failed. Priced calls stay blocked until it is paid. |
content_key_budget_exceeded | 402 | After error.resetsAt | The key reached its monthly credit limit. |
content_rate_limit_exceeded | 429 | Yes | Wait Retry-After seconds. See Rate Limiting. |
email_verification_required | 403 | After verification | A top-up route needs a verified email. Call POST /v1/platform/agent/verify-email/resend and have the owner open the link. |
x402_payment_failed | 402 | Yes, after a few seconds | The USDC payment has not settled yet. |
agent_channel_only | 403 | No | Agent top-ups are only for agent accounts. Buy credits in TutorFlow Billing. |
platform_invalid_api_key | 401 | No | The key is missing or invalid on an account route. |
platform_route_removed | 410 | No | You called a removed Platform content route. Move the call to the Content API; migrationGuide links the migration guide. |
platform_rate_limit_exceeded | 429 | Yes | An account route is over its limit, or this IP made more than 5 register attempts in the past hour. Wait Retry-After seconds. See Rate Limiting. |
platform_internal_error, content_internal_error | 500 | Yes | Retry with backoff, starting at 1 second, at most 3 times. |
The Content API has more codes, for idempotency conflicts, runs, uploads, and learners. See Error codes.
Common errors
Wrong key for the route
{
"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": "..."
}
}See Authentication for which key works where.
Out of credits
{
"error": {
"code": "content_payment_required",
"message": "The organization does not have enough available AI Credits for this request",
"billingSessionUrl": "POST /v1/platform/agent/billing/session",
"emailVerification": {
"isRequired": true,
"resendUrl": "POST /v1/platform/agent/verify-email/resend"
},
"status": 402,
"requestId": "..."
}
}Where USDC payment is enabled (it is on production; see x402.enabled in GET /v1/platform/.well-known/agent.json), the error also carries x402 with the deposit routes and the $5 to $5,000 limits. The full body is in When credits run out. Nothing was charged. Top up, then retry with the same Idempotency-Key.
With a metered overdraft, this 402 comes only once the overdraft reaches overdraftLimit.
Email verification required
{
"error": {
"code": "email_verification_required",
"message": "Email verification is required before adding billing or paying with USDC. Check your inbox or call POST /v1/platform/agent/verify-email/resend.",
"status": 403
}
}Only the top-up routes need a verified email. Trial credits work without it.
- Call
POST /v1/platform/agent/verify-email/resend. - Ask the person who owns the account email to open the link. This is one of the few steps an agent cannot do itself.
- Retry the top-up.
A generation run that failed
A generate route can answer 202 and the run can still fail later. Read status and error on GET /v1/content/classrooms/{classroomId}/runs/{runId}, or the *.generation.failed webhook. Steps that fail for a passing reason are retried for you. See Retries and failures.
Retry rules
- Retry
429afterRetry-After, and5xxwith exponential backoff and jitter. - Retry
402only after the balance changed. - Do not retry other
4xxanswers unchanged. Fix the request or the key first. - Send the same
Idempotency-Keyon every retry of a create, generate, or render, so a retry is never charged twice.