Resources
Content API Key Management

Content API Key Management

Create, list, rotate, revoke, and store dedicated Content Integration API keys.

Content Integration uses dedicated API keys that start with tf_content_. These keys are separate from TutorFlow admin sessions. Admin sessions create and manage keys. External systems use the created key as a bearer token.

Content API keys are also separate from Agent Platform credentials. Use tf_content_ keys only with /v1/content/** endpoints. Do not use Platform keys or Agent Platform ownership identifiers for Content Integration.

Who can create keys

A signed-in TutorFlow admin for the organization can create, list, rotate, and revoke Content API keys.

Key management calls use the TutorFlow admin session cookie named jwt. For email and password admin accounts, create a cookie jar first:

export TUTORFLOW_API_BASE_URL="https://api.tutorflow.io"
 
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"
  }'

Use the cookie jar on key management requests with -b tutorflow-admin.cookies. The login response does not return a Content API key. It only establishes the admin session that is allowed to create the dedicated tf_content_ key.

Find the organization ID

Do not guess the organization ID. After login, list the organizations your admin account can manage for Content Integration:

curl "$TUTORFLOW_API_BASE_URL/v1/content/organizations" \
  -b tutorflow-admin.cookies

Response:

[
  {
    "id": "00000000-0000-4000-8000-000000000001",
    "name": "Customer Academy",
    "slug": "customer-academy",
    "role": "ADMIN"
  }
]

Select an organization from the response. For a single-organization account, extract the first id with jq:

export TUTORFLOW_CONTENT_ORG_ID="$(curl -s "$TUTORFLOW_API_BASE_URL/v1/content/organizations" \
  -b tutorflow-admin.cookies | jq -r '.[0].id')"

Create a key

KEY_RESPONSE="$(curl -sS -X POST "$TUTORFLOW_API_BASE_URL/v1/content/organizations/$TUTORFLOW_CONTENT_ORG_ID/api-keys" \
  -H "Content-Type: application/json" \
  -b tutorflow-admin.cookies \
  -d '{
    "name": "external-content-test",
    "rateLimitPerMinute": 60
  }')"
 
printf '%s\n' "$KEY_RESPONSE" | jq

Response:

{
  "apiKey": "tf_content_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "keyId": "0b063e58-7e19-4787-90f0-d081c85f50d3",
  "keyPrefix": "tf_content_abc123def456",
  "name": "external-content-test",
  "rateLimitPerMinute": 60
}

The apiKey value is shown once. Store it immediately.

Set the key variables from the response:

export TUTORFLOW_CONTENT_API_KEY="$(printf '%s' "$KEY_RESPONSE" | jq -r '.apiKey')"
export TUTORFLOW_CONTENT_KEY_ID="$(printf '%s' "$KEY_RESPONSE" | jq -r '.keyId')"
export TUTORFLOW_CONTENT_KEY_PREFIX="$(printf '%s' "$KEY_RESPONSE" | jq -r '.keyPrefix')"

If any value is empty or null, inspect KEY_RESPONSE before continuing. Do not continue with a copied example value.

List keys

curl "$TUTORFLOW_API_BASE_URL/v1/content/organizations/$TUTORFLOW_CONTENT_ORG_ID/api-keys" \
  -b tutorflow-admin.cookies

Response:

[
  {
    "id": "0b063e58-7e19-4787-90f0-d081c85f50d3",
    "name": "external-content-test",
    "keyPrefix": "tf_content_abc123def456",
    "status": "ACTIVE",
    "organizationId": "00000000-0000-4000-8000-000000000001",
    "rateLimitPerMinute": 60,
    "lastUsedAt": null,
    "expiresAt": null
  }
]

List responses never include the full key or hash.

Rotate a key

Rotate a key when a secret owner changes or a key may have been exposed.

ROTATE_RESPONSE="$(curl -sS -X POST "$TUTORFLOW_API_BASE_URL/v1/content/organizations/$TUTORFLOW_CONTENT_ORG_ID/api-keys/$TUTORFLOW_CONTENT_KEY_ID/rotate" \
  -b tutorflow-admin.cookies)"
 
printf '%s\n' "$ROTATE_RESPONSE" | jq

Response:

{
  "apiKey": "tf_content_newxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "keyId": "3af3ff3d-5486-4a3e-932e-dbb68e6a7d65",
  "keyPrefix": "tf_content_newabc123",
  "name": "external-content-test",
  "rateLimitPerMinute": 60
}

Update the external system with the new key before sending new jobs.

export TUTORFLOW_CONTENT_API_KEY="$(printf '%s' "$ROTATE_RESPONSE" | jq -r '.apiKey')"
export TUTORFLOW_CONTENT_KEY_ID="$(printf '%s' "$ROTATE_RESPONSE" | jq -r '.keyId')"
export TUTORFLOW_CONTENT_KEY_PREFIX="$(printf '%s' "$ROTATE_RESPONSE" | jq -r '.keyPrefix')"

Revoke a key

curl -X POST "$TUTORFLOW_API_BASE_URL/v1/content/organizations/$TUTORFLOW_CONTENT_ORG_ID/api-keys/$TUTORFLOW_CONTENT_KEY_ID/revoke" \
  -b tutorflow-admin.cookies

Revoked keys immediately stop authorizing Content Integration requests.

Storage rules

  • Store the full key in a secret manager.
  • Never log the full tf_content_ value.
  • Include only keyPrefix in support tickets.
  • Use separate keys for test and production.
  • Rotate a key after staff changes, vendor changes, or suspected exposure.

Leaked key response

  1. Revoke the exposed key.
  2. Create or rotate to a new key.
  3. Update the external system secret.
  4. Retry one small request.
  5. Share the exposed key prefix and timestamp with TutorFlow support.