Skip to Content
📧 Join the Teacharium waitlist to get access. 

Embed Player Data

Retrieve lesson data for embedded players using JWT token authentication. This endpoint is designed for displaying lessons in embedded iframes on external websites.

For a complete guide on embedding lessons, see Embedding Lessons (Hosted).

Endpoint

GET /api/public/lessons/{lessonId}/player-data

Authentication

This endpoint requires JWT token authentication. The token must be obtained from the Sign Token endpoint and passed as a query parameter.

Unlike other API endpoints that use Bearer token authentication, this endpoint uses a JWT token in the query string for compatibility with iframe embedding.

URL Parameters

ParameterTypeRequiredDescription
lessonIdstring (UUID)YesThe lesson ID to retrieve

Query Parameters

ParameterTypeRequiredDescription
tokenstringYesJWT token from /api/public/sign-token
include_metadatabooleanNoInclude lesson metadata (default: false)
validate_playabilitybooleanNoValidate lesson is playable (default: true)

Request Example

curl 'https://your-domain.com/api/public/lessons/550e8400-e29b-41d4-a716-446655440000/player-data?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...&include_metadata=true'

Response

Success Response (200 OK)

Returns the lesson data optimized for the TeachariumPlayer, along with user attributes from the token.

{ "lesson": { "lesson": { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "Introduction to JavaScript", "status": "published", "variable_definitions": [ { "name": "userName", "type": "string", "initialValue": "Student" } ], "widget_settings": { "overrides": {} } }, "sections": [ { "id": "section-1", "title": "Getting Started", "order_index": 0, "steps": [ { "id": "step-1", "title": "Welcome", "order_index": 0, "content": { "content": [], "root": {} } } ] } ], "totalSteps": 10, "totalSections": 3 }, "userAttributes": { "userId": "user_12345", "sessionId": "session_abc", "accountType": "premium" }, "metadata": { "title": "Introduction to JavaScript", "status": "published", "totalSections": 3, "totalSteps": 10, "estimatedDuration": 600 }, "playability": { "valid": true, "errors": [] } }

Response Fields

FieldTypeDescription
lessonobjectComplete lesson data formatted for the player
lesson.lessonobjectLesson metadata and settings
lesson.sectionsarrayArray of sections with steps
lesson.totalStepsnumberTotal number of steps across all sections
lesson.totalSectionsnumberTotal number of sections
userAttributesobjectUser attributes from the JWT token
metadataobjectAdditional lesson metadata (if include_metadata=true)
playabilityobjectPlayability validation results (if validate_playability=true)

Error Responses

400 Bad Request

Invalid request parameters.

{ "error": "Invalid lesson ID" }

Possible causes:

  • Missing or invalid lesson ID
  • Missing token parameter
  • Invalid query parameters

401 Unauthorized

Token authentication failed.

{ "error": "Token verification failed: Token expired" }

Possible causes:

  • Token has expired
  • Token signature is invalid
  • Token was not signed with the correct secret
  • Missing JWT_SECRET environment variable

403 Forbidden

Token does not grant access to the requested lesson.

{ "error": "Token does not grant access to this lesson" }

Possible causes:

  • The lesson ID in the URL doesn’t match the lesson ID in the token
  • Attempting to use a token for a different lesson

404 Not Found

Lesson not found or doesn’t belong to the organization.

{ "error": "Lesson not found or access denied" }

Possible causes:

  • Lesson ID doesn’t exist
  • Lesson belongs to a different organization than the one in the token
  • Lesson has been deleted

422 Unprocessable Entity

Lesson has validation errors that prevent playback (only if validate_playability=true).

{ "error": "Lesson has validation errors that prevent playback" }

Possible causes:

  • Lesson has no sections
  • Lesson has sections with no steps
  • Lesson structure is incomplete

500 Internal Server Error

An unexpected error occurred.

{ "error": "An unexpected error occurred while fetching lesson data" }

Usage Example

JavaScript/TypeScript

async function loadEmbeddedLesson(lessonId, token) { const url = new URL( `/api/public/lessons/${lessonId}/player-data`, 'https://your-domain.com' ); url.searchParams.set('token', token); url.searchParams.set('include_metadata', 'true'); url.searchParams.set('validate_playability', 'true'); const response = await fetch(url.toString()); if (!response.ok) { const error = await response.json(); throw new Error(error.error); } return await response.json(); } // Usage try { const lessonData = await loadEmbeddedLesson( '550e8400-e29b-41d4-a716-446655440000', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' ); console.log('Lesson loaded:', lessonData.lesson.lesson.title); console.log('User ID:', lessonData.userAttributes.userId); console.log('Total steps:', lessonData.lesson.totalSteps); } catch (error) { console.error('Failed to load lesson:', error.message); }

React Hook

import { useState, useEffect } from 'react'; function useEmbeddedLesson(lessonId, token) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { if (!lessonId || !token) { setError('Missing lesson ID or token'); setLoading(false); return; } async function fetchLesson() { try { const url = new URL( `/api/public/lessons/${lessonId}/player-data`, 'https://your-domain.com' ); url.searchParams.set('token', token); url.searchParams.set('include_metadata', 'true'); const response = await fetch(url.toString()); if (!response.ok) { const error = await response.json(); throw new Error(error.error); } const result = await response.json(); setData(result); } catch (err) { setError(err.message); } finally { setLoading(false); } } fetchLesson(); }, [lessonId, token]); return { data, loading, error }; } // Usage in component function EmbeddedLessonPlayer({ lessonId, token }) { const { data, loading, error } = useEmbeddedLesson(lessonId, token); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; if (!data) return null; return ( <div> <h1>{data.lesson.lesson.title}</h1> <LessonPlayer lessonData={data.lesson} /> </div> ); }

CORS Support

This endpoint includes CORS headers to allow embedding from any origin:

Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, OPTIONS Access-Control-Allow-Headers: Content-Type

Security Considerations

  • Token expiration: Tokens expire based on the timeout set when signing (default 2 hours, max 24 hours)
  • Lesson access control: Tokens are tied to specific lessons and cannot be used for other lessons
  • Organization isolation: Lessons can only be accessed if they belong to the organization that signed the token
  • User attributes: User attributes in the token are read-only and cannot be modified without re-signing
  • Service role access: This endpoint uses service role access to bypass RLS, as authentication is handled via JWT

Differences from Authenticated Player Data

This endpoint differs from /api/lessons/{id}/player-data in several ways:

FeaturePublic Embed EndpointAuthenticated Endpoint
AuthenticationJWT token in query stringSession-based (cookies)
Access ControlToken-basedRLS + user session
CORSEnabled for all originsRestricted
User ContextFrom token attributesFrom authenticated user
Use CaseExternal embeddingInternal application

Best Practices

  1. Generate tokens server-side: Never expose API credentials in client code
  2. Set appropriate timeouts: Match token expiration to expected lesson duration
  3. Include user identification: Use userAttributes in tokens for tracking
  4. Handle token expiration: Implement token refresh logic if needed
  5. Validate responses: Always check response status and handle errors gracefully
  6. Cache conservatively: Response includes Cache-Control: no-store header

Next Steps

Last updated on