Use this quickstart to run one complete Content Integration test. The flow creates a tf_content_ key, submits one small source JSON payload, polls the job, and retrieves the result manifest.
Prerequisites
- A TutorFlow admin account with access to the target organization.
- The target organization id.
- One small source JSON content group.
- A secure place to store the generated
tf_content_key.
Set the API base URL:
export TUTORFLOW_API_BASE_URL="https://api.tutorflow.io"
export TUTORFLOW_ORGANIZATION_ID="00000000-0000-4000-8000-000000000001"Step 0: Get an admin session cookie
Key management endpoints use the signed-in TutorFlow admin session. The session cookie is named jwt. If your admin account uses email and password sign-in, create a cookie jar before calling the key endpoint:
curl -c tutorflow-admin.cookies -X POST "$TUTORFLOW_API_BASE_URL/auth/login" \
-H "Content-Type: application/json" \
-H "Referer: https://tutorflow.io/sign-in" \
-d '{
"email": "admin@example.com",
"password": "your-password",
"timezone": "Asia/Seoul"
}'If your organization uses SSO or OAuth sign-in, sign in through the TutorFlow admin UI and create the key from the authenticated admin environment.
Step 1: Create a Content API key
Create the key while signed in as a TutorFlow admin. The full key is returned once.
curl -X POST "$TUTORFLOW_API_BASE_URL/v1/content/organizations/$TUTORFLOW_ORGANIZATION_ID/api-keys" \
-H "Content-Type: application/json" \
-b tutorflow-admin.cookies \
-d '{
"name": "external-content-test",
"rateLimitPerMinute": 60
}'Response:
{
"apiKey": "tf_content_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"keyId": "0b063e58-7e19-4787-90f0-d081c85f50d3",
"keyPrefix": "tf_content_abc123def456",
"name": "external-content-test",
"rateLimitPerMinute": 60
}Step 2: Save the bearer token
Store the full key in a secret manager. Do not log it.
export TUTORFLOW_CONTENT_API_KEY="tf_content_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Use it on expansion, status, and result requests:
Authorization: Bearer tf_content_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxStep 3: Submit source JSON
Save this as source-content.json:
{
"requestedOutputs": ["interactive_module", "summary_video", "expanded_quiz"],
"payload": {
"language": "en",
"categories": [
{
"id": "language-basics",
"title": "Language Basics",
"levels": [
{
"id": "level-a1",
"title": "A1 Foundations",
"lessons": [
{
"id": "lesson-vocabulary-1",
"type": "vocabulary",
"title": "Basic greetings",
"items": [
{
"term": "hello",
"meaning": "a greeting used when meeting someone",
"example": "Hello, Mina."
}
]
}
]
}
]
}
]
}
}TutorFlow also accepts the compact single-level shape:
{
"payload": {
"category": {
"id": "language-basics",
"title": "Language Basics"
},
"level": {
"id": "level-a1",
"title": "A1 Foundations",
"lessons": [
{
"id": "lesson-vocabulary-1",
"type": "vocabulary",
"title": "Basic greetings"
}
]
}
}
}Create the job:
curl -X POST "$TUTORFLOW_API_BASE_URL/v1/content/integrations/expansions" \
-H "Authorization: Bearer $TUTORFLOW_CONTENT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: language-basics-level-a1-2026-07-04" \
--data-binary @source-content.jsonStore the returned id:
export TUTORFLOW_CONTENT_JOB_ID="3f9440c4-7b15-48d7-a02f-4c1c50a8c3e1"Step 4: Poll status
curl "$TUTORFLOW_API_BASE_URL/v1/content/integrations/expansions/$TUTORFLOW_CONTENT_JOB_ID" \
-H "Authorization: Bearer $TUTORFLOW_CONTENT_API_KEY"Poll every 2 to 5 seconds until the job reaches completed or failed.
Step 5: Retrieve result
curl "$TUTORFLOW_API_BASE_URL/v1/content/integrations/expansions/$TUTORFLOW_CONTENT_JOB_ID/result" \
-H "Authorization: Bearer $TUTORFLOW_CONTENT_API_KEY"Store these fields:
| Field | Purpose |
|---|---|
job.id | Reconciliation with TutorFlow. |
job.status | Terminal state for the source content group. |
outputs[].outputType | interactive_module, summary_video, or expanded_quiz. |
outputs[].status | Per-output state. |
outputs[].resourceType | Returned content type. |
outputs[].resourceId | TutorFlow content id when available. |
outputs[].manifest | Data to reinsert or review in the external system. |
Step 6: Store the manifest
At minimum, store a record like this:
{
"sourceContentId": "language-basics:level-a1",
"tutorFlowJobId": "3f9440c4-7b15-48d7-a02f-4c1c50a8c3e1",
"status": "completed",
"outputs": [
{
"outputType": "interactive_module",
"status": "completed",
"resourceType": "module",
"resourceId": null,
"manifest": {}
}
]
}If the test fails
Send TutorFlow support:
- Content job id.
- Key prefix, never the full key.
- Request timestamp.
- Endpoint path.
- Idempotency key.
- Final error response.
- Source JSON sample, if it can be shared.