Resources
Content Integration Quickstart

Content Integration Quickstart

Create a Content API key, submit source JSON, poll status, and retrieve a result manifest.

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"

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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Step 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.json

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

FieldPurpose
job.idReconciliation with TutorFlow.
job.statusTerminal state for the source content group.
outputs[].outputTypeinteractive_module, summary_video, or expanded_quiz.
outputs[].statusPer-output state.
outputs[].resourceTypeReturned content type.
outputs[].resourceIdTutorFlow content id when available.
outputs[].manifestData 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.