Content Integration errors use a stable error.code value. Treat the code as the client-facing contract and the message as human-readable detail.
Error codes
| Code | Meaning | Retry |
|---|---|---|
content_invalid_api_key | Bearer token is missing, malformed, revoked, or expired. | No. Create or rotate the key. |
content_invalid_request | Request body failed validation. | No. Fix the payload. |
content_forbidden | Signed-in admin cannot manage the target organization. | No. Use an admin with access. |
content_not_found | Job, key, webhook, classroom, content resource, or game or simulation version was not found. | No, unless the id was mistyped. |
content_conflict | Request conflicts with an existing state, or an idempotency key was reused with different request content. | Usually no. Check idempotency. |
content_payment_required | The organization does not have enough availableCredit for the requested expansion outputs or for a game or simulation brief, build, or revise. HTTP 402. | Not until credits are added. |
content_payment_failed | The organization's payment has failed. HTTP 402. | Not until billing is resolved in TutorFlow. |
content_rate_limit_exceeded | Key rate limit was exceeded. | Yes, after waiting. |
content_internal_error | Unexpected server error. | Yes, with backoff. |
The two 402 codes are the only errors a caller cannot fix by changing the request. Check GET /v1/content/credits, then add credits or resolve the billing issue before retrying with the same idempotency key. Content API requests never fall back to Agent Platform credits.
Example error
{
"error": {
"code": "content_invalid_api_key",
"message": "Invalid or missing Content Integration API key",
"status": 401
}
}Validation errors
Requests rejected by body validation (content_invalid_request) additionally
carry error.details: one entry per invalid field with the dotted field
path, a readable message, and the failed constraints. Use it to fix a
rejected payload programmatically instead of parsing the message string.
{
"error": {
"code": "content_invalid_request",
"message": "Validation failed: sourceTitle must be a string",
"details": [
{
"field": "sourceTitle",
"message": "sourceTitle must be a string",
"constraints": ["isString", "isNotEmpty"]
}
],
"status": 422
}
}Router-level 404s under /v1/content/** (for example a typo in the path) use
the same envelope with error.code: "content_not_found".
Errors on streaming routes
The game and simulation brief, build, and revise routes answer with
text/event-stream. Everything that can be checked up front is checked before
the stream opens, and those failures use the JSON envelope above with the
matching status: 404 for a missing classroom or resource, 400 for a build
without a brief or a revision of an unbuilt resource, 402 for credits.
Once the stream has opened the HTTP status is already 200. A failure after
that point arrives as an error event:
event: error
data: {"message":"The safety rules were not satisfied","violations":["Game did not start within 10s"]}Nothing was stored and nothing was charged. When violations is present the
model could not produce a page that passed the checks for that plan, so change
the brief or metadata rather than retrying the same build. A closed
connection is not an error event: hanging up aborts the build and charges
nothing.
Retry rules
- Retry
429after waiting at least 30 seconds. - Retry
500with exponential backoff. - Do not retry
400without changing the payload. - Do not retry
401without replacing the key. - Do not retry
402until the organization has enough available AI Credits and payment is active. - Retry a build whose stream ended with an
errorevent at most once. If the event carriedviolations, change the plan first. - Do not create a second job for the same source content while the original job is still
queuedorprocessing.
Failed jobs
When a job reaches failed, fetch the result endpoint and inspect:
job.error- each
outputs[].error - requested output list
- source title
Fix the source JSON or requested outputs, then create a new job with a new idempotency key if the source content changed.
Support handoff
Send these fields to TutorFlow support:
| Field | Notes |
|---|---|
jobId | Required for expansion issues. |
Game or simulation id and contentVersion | Required for build, revise, and restore issues. |
keyPrefix | Safe to share. Do not send the full key. |
| Endpoint path | Example: /v1/content/integrations/expansions. |
| Timestamp | Include timezone. |
| Idempotency key | Helps find duplicate or retried requests. |
| Final response body | Include error object and status code. |
| Source JSON sample | Include only if it can be shared. |