Awards

Description:
  • Query award progress, listen for award events, and generate certificates. **Query Functions** (read-only): - `getContentAwards(contentId)` - Get awards for a learning path/course - `getContentAwardsByIds(contentIds)` - Get awards for multiple content items (batch optimized) - `getCompletedAwards(brand)` - Get user's earned awards - `getInProgressAwards(brand)` - Get awards user is working toward - `getAwardStatistics(brand)` - Get aggregate award stats **Event Callbacks**: - `registerAwardCallback(fn)` - Listen for new awards earned - `registerProgressCallback(fn)` - Listen for progress updates **Certificates**: - `fetchCertificate(awardId)` - Generate certificate (Web only)
Source:
Examples
Quick Start
import {
  getContentAwards,
  getCompletedAwards,
  registerAwardCallback
} from 'musora-content-services'

// Show awards for a learning path
const { hasAwards, awards } = await getContentAwards(learningPathId)

// Get user's completed awards
const completed = await getCompletedAwards('drumeo')

// Listen for new awards (show notification)
useEffect(() => {
  return registerAwardCallback((award) => {
    showCelebration(award.name, award.badge, award.completion_data.message)
  })
}, [])
How Awards Update (Collection Context)
// Award progress updates automatically when you save content progress.
// CRITICAL: Pass collection context when inside a learning path!

import { contentStatusCompleted } from 'musora-content-services'

// Correct - passes collection context
const collection = { type: 'learning-path-v2', id: learningPathId }
await contentStatusCompleted(lessonId, collection)

// Wrong - no collection context (affects wrong awards!)
await contentStatusCompleted(lessonId, null)

Methods

(static) getAwardStatistics(brandopt) → {Promise.<AwardStatistics>}

Description:
  • Returns aggregate statistics about the user's award progress. Use this for profile stats, gamification dashboards, or achievement summaries. Returns an object with: - `totalAvailable` - Total awards that can be earned - `completed` - Number of awards earned - `inProgress` - Number of awards started but not completed - `notStarted` - Number of awards not yet started - `completionPercentage` - Overall completion % (0-100, one decimal) Returns all zeros on error (never throws).
Source:
Examples
// Profile stats card
function ProfileStatsCard() {
  const [stats, setStats] = useState(null)
  const brand = useBrand()

  useEffect(() => {
    getAwardStatistics(brand).then(setStats)
  }, [brand])

  if (!stats) return <LoadingSpinner />

  return (
    <View style={styles.statsCard}>
      <StatItem label="Awards Earned" value={stats.completed} />
      <StatItem label="In Progress" value={stats.inProgress} />
      <StatItem label="Available" value={stats.totalAvailable} />
      <ProgressRing
        progress={stats.completionPercentage / 100}
        label={`${stats.completionPercentage}%`}
      />
    </View>
  )
}
// Achievement progress bar
const stats = await getAwardStatistics('drumeo')
return (
  <View>
    <Text>{stats.completed} of {stats.totalAvailable} awards earned</Text>
    <ProgressBar progress={stats.completionPercentage / 100} />
  </View>
)
Parameters:
Name Type Attributes Default Description
brand string <optional>
null Brand to filter by (drumeo, pianote, guitareo, singeo), or null for all brands
Returns:
Statistics object with award counts and completion percentage
Type
Promise.<AwardStatistics>

(static) getCompletedAwards(brandopt, optionsopt) → {Promise.<Array.<AwardInfo>>}

Description:
  • Returns all awards the user has completed. Use this for "My Achievements" or profile award gallery screens. Each award includes: - Badge and award images for display - Completion date for "Earned on X" display - `completionData.message` - Pre-generated congratulations text - `completionData.XXX` - other fields are award type dependant Returns empty array `[]` on error (never throws).
Source:
Examples
// Awards gallery screen
function AwardsGalleryScreen() {
  const [awards, setAwards] = useState([])
  const brand = useBrand() // 'drumeo', 'pianote', etc.

  useEffect(() => {
    getCompletedAwards(brand).then(setAwards)
  }, [brand])

  return (
    <FlatList
      data={awards}
      keyExtractor={item => item.awardId}
      numColumns={2}
      renderItem={({ item }) => (
        <AwardBadge
          badge={item.badge}
          title={item.awardTitle}
          earnedDate={new Date(item.completedAt).toLocaleDateString()}
          onPress={() => showAwardDetail(item)}
        />
      )}
    />
  )
}
// Show award detail with practice stats
function AwardDetailModal({ award }) {
  return (
    <Modal>
      <Image source={{ uri: award.award }} />
      <Text>{award.awardTitle}</Text>
      <Text>Instructor: {award.instructorName}</Text>
      <Text>Completed: {new Date(award.completedAt).toLocaleDateString()}</Text>
      <Text>Practice time: {award.completionData.practice_minutes} minutes</Text>
      <Text>Days practiced: {award.completionData.days_user_practiced}</Text>
      <Text>{award.completionData.message}</Text>
    </Modal>
  )
}
// Paginated loading
const PAGE_SIZE = 12
const loadMore = async (page) => {
  const newAwards = await getCompletedAwards(brand, {
    limit: PAGE_SIZE,
    offset: page * PAGE_SIZE
  })
  setAwards(prev => [...prev, ...newAwards])
}
Parameters:
Name Type Attributes Default Description
brand string <optional>
null Brand to filter by (drumeo, pianote, guitareo, singeo), or null for all brands
options AwardPaginationOptions <optional>
{} Optional pagination and filtering
Returns:
Array of completed award objects sorted by completion date (newest first)
Type
Promise.<Array.<AwardInfo>>

(static) getContentAwards(contentId) → {Promise.<ContentAwardsResponse>}

Description:
  • Returns awards associated with a content item and the user's progress toward each. Use this to display award progress on learning path or course detail pages. - Pass a **learning path ID** to get awards for that learning path - Pass a **course ID** to get awards for that course - Pass a **lesson ID** to get all awards that include that lesson in their requirements Returns `{ hasAwards: false, awards: [] }` on error (never throws).
Source:
Examples
// Display award card on learning path detail page
function LearningPathAwardCard({ learningPathId }) {
  const [awardData, setAwardData] = useState({ hasAwards: false, awards: [] })

  useEffect(() => {
    getContentAwards(learningPathId).then(setAwardData)
  }, [learningPathId])

  if (!awardData.hasAwards) return null

  const award = awardData.awards[0] // Learning paths typically have one award
  return (
    <AwardCard
      badge={award.badge}
      title={award.awardTitle}
      progress={award.progressPercentage}
      isCompleted={award.isCompleted}
      completedAt={award.completedAt}
      instructorName={award.instructorName}
    />
  )
}
// Check award status before showing certificate button
const { hasAwards, awards } = await getContentAwards(learningPathId)
const completedAward = awards.find(a => a.isCompleted)
if (completedAward) {
  showCertificateButton(completedAward.awardId)
}
Parameters:
Name Type Description
contentId number Railcontent ID of the content item (lesson, course, or learning path)
Returns:
Status object with award information
Type
Promise.<ContentAwardsResponse>

(static) getContentAwardsByIds(contentIds) → {Promise.<Object.<number, ContentAwardsResponse>>}

Description:
  • Returns awards for multiple content items at once. More efficient than calling `getContentAwards()` multiple times. Returns an object where keys are content IDs and values are the same structure as `getContentAwards()`. Content IDs without awards will have `{ hasAwards: false, awards: [] }` in the result. Returns empty object `{}` on error (never throws).
Source:
Examples
const learningPathIds = [12345, 67890, 11111]
const awardsMap = await getContentAwardsByIds(learningPathIds)

learningPathIds.forEach(id => {
  const { hasAwards, awards } = awardsMap[id] || { hasAwards: false, awards: [] }
  if (hasAwards) {
    console.log(`Learning path ${id} has ${awards.length} award(s)`)
  }
})
function CourseListWithAwards({ courseIds }) {
  const [awardsMap, setAwardsMap] = useState({})

  useEffect(() => {
    getContentAwardsByIds(courseIds).then(setAwardsMap)
  }, [courseIds])

  return courseIds.map(courseId => {
    const { hasAwards, awards } = awardsMap[courseId] || { hasAwards: false, awards: [] }
    return (
      <CourseCard key={courseId} courseId={courseId}>
        {hasAwards && <AwardBadge award={awards[0]} />}
      </CourseCard>
    )
  })
}
Parameters:
Name Type Description
contentIds Array.<number> Array of Railcontent IDs to fetch awards for
Returns:
Object mapping content IDs to their award data
Type
Promise.<Object.<number, ContentAwardsResponse>>

(static) getInProgressAwards(brandopt, optionsopt) → {Promise.<Array.<AwardInfo>>}

Description:
  • Returns awards the user has started but not yet completed. Sorted by progress percentage (highest first) so awards closest to completion appear first. Use this for "Continue Learning" or dashboard widgets. Progress is calculated based on lessons completed within the correct collection context. For learning paths, only lessons completed within that specific learning path count toward its award. Returns empty array `[]` on error (never throws).
Source:
Examples
// "Almost There" widget on home screen
function AlmostThereWidget() {
  const [topAwards, setTopAwards] = useState([])
  const brand = useBrand()

  useEffect(() => {
    // Get top 3 closest to completion
    getInProgressAwards(brand, { limit: 3 }).then(setTopAwards)
  }, [brand])

  if (topAwards.length === 0) return null

  return (
    <View>
      <Text>Almost There!</Text>
      {topAwards.map(award => (
        <TouchableOpacity
          key={award.awardId}
          onPress={() => navigateToLearningPath(award)}
        >
          <Image source={{ uri: award.badge }} />
          <Text>{award.awardTitle}</Text>
          <ProgressBar progress={award.progressPercentage / 100} />
          <Text>{award.progressPercentage}% complete</Text>
        </TouchableOpacity>
      ))}
    </View>
  )
}
// Full in-progress awards list
function InProgressAwardsScreen() {
  const [awards, setAwards] = useState([])

  useEffect(() => {
    getInProgressAwards().then(setAwards)
  }, [])

  return (
    <FlatList
      data={awards}
      keyExtractor={item => item.awardId}
      renderItem={({ item }) => (
        <AwardProgressCard
          badge={item.badge}
          title={item.awardTitle}
          progress={item.progressPercentage}
          brand={item.brand}
        />
      )}
    />
  )
}
Parameters:
Name Type Attributes Default Description
brand string <optional>
null Brand to filter by (drumeo, pianote, guitareo, singeo), or null for all brands
options AwardPaginationOptions <optional>
{} Optional pagination options
Returns:
Array of in-progress award objects sorted by progress percentage (highest first)
Type
Promise.<Array.<AwardInfo>>

(static) registerAwardCallback(callback) → {UnregisterFunction}

Description:
  • Registers a callback to be notified when the user earns a new award. Only one callback can be registered at a time - registering a new one replaces the previous. Always call the returned cleanup function when your component unmounts. The callback receives an award object with: - `awardId` - Unique Sanity award ID - `name` - Display name of the award - `badge` - URL to badge image - `contentType` - Content type ('guided-course' or 'learning-path-v2') - `completed_at` - ISO timestamp - `isCompleted` - Boolean indicating the award is completed (always true for granted awards) - `completion_data.message` - Pre-generated congratulations message - `completion_data.practice_minutes` - Total practice time - `completion_data.days_user_practiced` - Days spent practicing - `completion_data.content_title` - Title of completed content
Source:
Examples
// React Native - Show award celebration modal
function useAwardNotification() {
  const [award, setAward] = useState(null)

  useEffect(() => {
    return registerAwardCallback((awardData) => {
      setAward({
        title: awardData.name,
        badge: awardData.badge,
        message: awardData.completion_data.message,
        practiceMinutes: awardData.completion_data.practice_minutes
      })
    })
  }, [])

  return { award, dismissAward: () => setAward(null) }
}
// Track award in analytics
useEffect(() => {
  return registerAwardCallback((award) => {
    analytics.track('Award Earned', {
      awardId: award.awardId,
      awardName: award.name,
      practiceMinutes: award.completion_data.practice_minutes,
      contentTitle: award.completion_data.content_title
    })
  })
}, [])
Parameters:
Name Type Description
callback AwardCallbackFunction Function called with award data when an award is earned
Returns:
Cleanup function to unregister this callback
Type
UnregisterFunction

(static) registerProgressCallback(callback) → {UnregisterFunction}

Description:
  • Registers a callback to be notified when award progress changes (but award is not yet complete). Only one callback can be registered at a time. Use this to update progress bars or show "almost there" encouragement. The callback receives: - `awardId` - Unique Sanity award ID - `progressPercentage` - Current completion percentage (0-99) Note: When an award reaches 100%, `registerAwardCallback` fires instead.
Source:
Examples
// React Native - Update progress in learning path screen
function LearningPathScreen({ learningPathId }) {
  const [awardProgress, setAwardProgress] = useState({})

  useEffect(() => {
    return registerProgressCallback(({ awardId, progressPercentage }) => {
      setAwardProgress(prev => ({
        ...prev,
        [awardId]: progressPercentage
      }))
    })
  }, [])

  // Use awardProgress to update UI
}
// Show encouragement toast at milestones
useEffect(() => {
  return registerProgressCallback(({ awardId, progressPercentage }) => {
    if (progressPercentage === 50) {
      showToast('Halfway to your award!')
    } else if (progressPercentage >= 90) {
      showToast('Almost there! Just a few more lessons.')
    }
  })
}, [])
Parameters:
Name Type Description
callback ProgressCallbackFunction Function called with progress data when award progress changes
Returns:
Cleanup function to unregister this callback
Type
UnregisterFunction

(static) resetAllAwards() → {Promise.<{deletedCount: number}>}

Source:
Returns:
Type
Promise.<{deletedCount: number}>

(async, inner) fetchCertificate(awardId) → {Promise.<Certificate>}

Description:
  • Fetch certificate data for a completed award with all images converted to base64. Returns certificate information ready for rendering in a PDF or image format. All image URLs (logos, signatures, ribbons) are converted to base64 strings for offline use.
Source:
Examples
Generate certificate PDF (Web)
const cert = await fetchCertificate('abc-123')
generatePDF({
  userName: cert.user_name,
  awardTitle: cert.title,
  completedAt: new Date(cert.completed_at).toLocaleDateString(),
  message: cert.message,
  brandLogo: `data:image/png;base64,${cert.brand_logo_64}`,
  signature: cert.instructor_signature_64
    ? `data:image/png;base64,${cert.instructor_signature_64}`
    : null
})
Display certificate preview (Web)
const cert = await fetchCertificate(awardId)
return (
  <CertificatePreview
    userName={cert.user_name}
    awardTitle={cert.title}
    message={cert.message}
    awardImage={`data:image/png;base64,${cert.award_image_64}`}
    instructorName={cert.instructor_name}
    signature={cert.instructor_signature_64}
  />
)
React Native Implementation
// This function is NOT compatible with React Native due to FileReader/Blob APIs.
// For React Native, implement certificate generation using:
//
// 1. Use react-native-blob-util for base64 image conversion:
//    import ReactNativeBlobUtil from 'react-native-blob-util'
//    const base64 = await ReactNativeBlobUtil.fetch('GET', imageUrl)
//      .then(res => res.base64())
//
// 2. Use react-native-html-to-pdf for PDF generation:
//    import RNHTMLtoPDF from 'react-native-html-to-pdf'
//    const pdf = await RNHTMLtoPDF.convert({
//      html: certificateHtmlTemplate,
//      fileName: `certificate-${awardId}`,
//      directory: 'Documents',
//    })
//
// 3. Build certificate data using getContentAwards() or getCompletedAwards()
//    which ARE React Native compatible, then handle image conversion
//    and PDF generation in your RN app layer.
Parameters:
Name Type Description
awardId string Unique Sanity award ID
Throws:
If award is not found or not completed
Type
Error
Returns:
Certificate object with base64-encoded images: - award_id {string} - Sanity award ID - user_name {string} - User's display name - user_id {number} - User's ID - completed_at {string} - ISO timestamp of completion - message {string} - Certificate message for display - type {string} - Award type (e.g., 'content-award') - title {string} - Award title/name - musora_logo {string} - URL to Musora logo - musora_logo_64 {string} - Base64-encoded Musora logo - musora_bg_logo {string} - URL to Musora background logo - musora_bg_logo_64 {string} - Base64-encoded background logo - brand_logo {string} - URL to brand logo - brand_logo_64 {string} - Base64-encoded brand logo - ribbon_image {string} - URL to ribbon decoration - ribbon_image_64 {string} - Base64-encoded ribbon image - award_image {string} - URL to award image - award_image_64 {string} - Base64-encoded award image - instructor_name {string} - Instructor's name - instructor_signature {string|undefined} - URL to signature (if available) - instructor_signature_64 {string|undefined} - Base64-encoded signature
Type
Promise.<Certificate>
Description:
  • Query award progress, listen for award events, and generate certificates. **Query Functions** (read-only): - `getContentAwards(contentId)` - Get awards for a learning path/course - `getContentAwardsByIds(contentIds)` - Get awards for multiple content items (batch optimized) - `getCompletedAwards(brand)` - Get user's earned awards - `getInProgressAwards(brand)` - Get awards user is working toward - `getAwardStatistics(brand)` - Get aggregate award stats **Event Callbacks**: - `registerAwardCallback(fn)` - Listen for new awards earned - `registerProgressCallback(fn)` - Listen for progress updates **Certificates**: - `fetchCertificate(awardId)` - Generate certificate (Web only)
Source:
Examples
Quick Start
import {
  getContentAwards,
  getCompletedAwards,
  registerAwardCallback
} from 'musora-content-services'

// Show awards for a learning path
const { hasAwards, awards } = await getContentAwards(learningPathId)

// Get user's completed awards
const completed = await getCompletedAwards('drumeo')

// Listen for new awards (show notification)
useEffect(() => {
  return registerAwardCallback((award) => {
    showCelebration(award.name, award.badge, award.completion_data.message)
  })
}, [])
How Awards Update (Collection Context)
// Award progress updates automatically when you save content progress.
// CRITICAL: Pass collection context when inside a learning path!

import { contentStatusCompleted } from 'musora-content-services'

// Correct - passes collection context
const collection = { type: 'learning-path-v2', id: learningPathId }
await contentStatusCompleted(lessonId, collection)

// Wrong - no collection context (affects wrong awards!)
await contentStatusCompleted(lessonId, null)

Methods

(static) getAwardStatistics(brandopt) → {Promise.<AwardStatistics>}

Description:
  • Returns aggregate statistics about the user's award progress. Use this for profile stats, gamification dashboards, or achievement summaries. Returns an object with: - `totalAvailable` - Total awards that can be earned - `completed` - Number of awards earned - `inProgress` - Number of awards started but not completed - `notStarted` - Number of awards not yet started - `completionPercentage` - Overall completion % (0-100, one decimal) Returns all zeros on error (never throws).
Source:
Examples
// Profile stats card
function ProfileStatsCard() {
  const [stats, setStats] = useState(null)
  const brand = useBrand()

  useEffect(() => {
    getAwardStatistics(brand).then(setStats)
  }, [brand])

  if (!stats) return <LoadingSpinner />

  return (
    <View style={styles.statsCard}>
      <StatItem label="Awards Earned" value={stats.completed} />
      <StatItem label="In Progress" value={stats.inProgress} />
      <StatItem label="Available" value={stats.totalAvailable} />
      <ProgressRing
        progress={stats.completionPercentage / 100}
        label={`${stats.completionPercentage}%`}
      />
    </View>
  )
}
// Achievement progress bar
const stats = await getAwardStatistics('drumeo')
return (
  <View>
    <Text>{stats.completed} of {stats.totalAvailable} awards earned</Text>
    <ProgressBar progress={stats.completionPercentage / 100} />
  </View>
)
Parameters:
Name Type Attributes Default Description
brand string <optional>
null Brand to filter by (drumeo, pianote, guitareo, singeo), or null for all brands
Returns:
Statistics object with award counts and completion percentage
Type
Promise.<AwardStatistics>

(static) getCompletedAwards(brandopt, optionsopt) → {Promise.<Array.<AwardInfo>>}

Description:
  • Returns all awards the user has completed. Use this for "My Achievements" or profile award gallery screens. Each award includes: - Badge and award images for display - Completion date for "Earned on X" display - `completionData.message` - Pre-generated congratulations text - `completionData.XXX` - other fields are award type dependant Returns empty array `[]` on error (never throws).
Source:
Examples
// Awards gallery screen
function AwardsGalleryScreen() {
  const [awards, setAwards] = useState([])
  const brand = useBrand() // 'drumeo', 'pianote', etc.

  useEffect(() => {
    getCompletedAwards(brand).then(setAwards)
  }, [brand])

  return (
    <FlatList
      data={awards}
      keyExtractor={item => item.awardId}
      numColumns={2}
      renderItem={({ item }) => (
        <AwardBadge
          badge={item.badge}
          title={item.awardTitle}
          earnedDate={new Date(item.completedAt).toLocaleDateString()}
          onPress={() => showAwardDetail(item)}
        />
      )}
    />
  )
}
// Show award detail with practice stats
function AwardDetailModal({ award }) {
  return (
    <Modal>
      <Image source={{ uri: award.award }} />
      <Text>{award.awardTitle}</Text>
      <Text>Instructor: {award.instructorName}</Text>
      <Text>Completed: {new Date(award.completedAt).toLocaleDateString()}</Text>
      <Text>Practice time: {award.completionData.practice_minutes} minutes</Text>
      <Text>Days practiced: {award.completionData.days_user_practiced}</Text>
      <Text>{award.completionData.message}</Text>
    </Modal>
  )
}
// Paginated loading
const PAGE_SIZE = 12
const loadMore = async (page) => {
  const newAwards = await getCompletedAwards(brand, {
    limit: PAGE_SIZE,
    offset: page * PAGE_SIZE
  })
  setAwards(prev => [...prev, ...newAwards])
}
Parameters:
Name Type Attributes Default Description
brand string <optional>
null Brand to filter by (drumeo, pianote, guitareo, singeo), or null for all brands
options AwardPaginationOptions <optional>
{} Optional pagination and filtering
Returns:
Array of completed award objects sorted by completion date (newest first)
Type
Promise.<Array.<AwardInfo>>

(static) getContentAwards(contentId) → {Promise.<ContentAwardsResponse>}

Description:
  • Returns awards associated with a content item and the user's progress toward each. Use this to display award progress on learning path or course detail pages. - Pass a **learning path ID** to get awards for that learning path - Pass a **course ID** to get awards for that course - Pass a **lesson ID** to get all awards that include that lesson in their requirements Returns `{ hasAwards: false, awards: [] }` on error (never throws).
Source:
Examples
// Display award card on learning path detail page
function LearningPathAwardCard({ learningPathId }) {
  const [awardData, setAwardData] = useState({ hasAwards: false, awards: [] })

  useEffect(() => {
    getContentAwards(learningPathId).then(setAwardData)
  }, [learningPathId])

  if (!awardData.hasAwards) return null

  const award = awardData.awards[0] // Learning paths typically have one award
  return (
    <AwardCard
      badge={award.badge}
      title={award.awardTitle}
      progress={award.progressPercentage}
      isCompleted={award.isCompleted}
      completedAt={award.completedAt}
      instructorName={award.instructorName}
    />
  )
}
// Check award status before showing certificate button
const { hasAwards, awards } = await getContentAwards(learningPathId)
const completedAward = awards.find(a => a.isCompleted)
if (completedAward) {
  showCertificateButton(completedAward.awardId)
}
Parameters:
Name Type Description
contentId number Railcontent ID of the content item (lesson, course, or learning path)
Returns:
Status object with award information
Type
Promise.<ContentAwardsResponse>

(static) getContentAwardsByIds(contentIds) → {Promise.<Object.<number, ContentAwardsResponse>>}

Description:
  • Returns awards for multiple content items at once. More efficient than calling `getContentAwards()` multiple times. Returns an object where keys are content IDs and values are the same structure as `getContentAwards()`. Content IDs without awards will have `{ hasAwards: false, awards: [] }` in the result. Returns empty object `{}` on error (never throws).
Source:
Examples
const learningPathIds = [12345, 67890, 11111]
const awardsMap = await getContentAwardsByIds(learningPathIds)

learningPathIds.forEach(id => {
  const { hasAwards, awards } = awardsMap[id] || { hasAwards: false, awards: [] }
  if (hasAwards) {
    console.log(`Learning path ${id} has ${awards.length} award(s)`)
  }
})
function CourseListWithAwards({ courseIds }) {
  const [awardsMap, setAwardsMap] = useState({})

  useEffect(() => {
    getContentAwardsByIds(courseIds).then(setAwardsMap)
  }, [courseIds])

  return courseIds.map(courseId => {
    const { hasAwards, awards } = awardsMap[courseId] || { hasAwards: false, awards: [] }
    return (
      <CourseCard key={courseId} courseId={courseId}>
        {hasAwards && <AwardBadge award={awards[0]} />}
      </CourseCard>
    )
  })
}
Parameters:
Name Type Description
contentIds Array.<number> Array of Railcontent IDs to fetch awards for
Returns:
Object mapping content IDs to their award data
Type
Promise.<Object.<number, ContentAwardsResponse>>

(static) getInProgressAwards(brandopt, optionsopt) → {Promise.<Array.<AwardInfo>>}

Description:
  • Returns awards the user has started but not yet completed. Sorted by progress percentage (highest first) so awards closest to completion appear first. Use this for "Continue Learning" or dashboard widgets. Progress is calculated based on lessons completed within the correct collection context. For learning paths, only lessons completed within that specific learning path count toward its award. Returns empty array `[]` on error (never throws).
Source:
Examples
// "Almost There" widget on home screen
function AlmostThereWidget() {
  const [topAwards, setTopAwards] = useState([])
  const brand = useBrand()

  useEffect(() => {
    // Get top 3 closest to completion
    getInProgressAwards(brand, { limit: 3 }).then(setTopAwards)
  }, [brand])

  if (topAwards.length === 0) return null

  return (
    <View>
      <Text>Almost There!</Text>
      {topAwards.map(award => (
        <TouchableOpacity
          key={award.awardId}
          onPress={() => navigateToLearningPath(award)}
        >
          <Image source={{ uri: award.badge }} />
          <Text>{award.awardTitle}</Text>
          <ProgressBar progress={award.progressPercentage / 100} />
          <Text>{award.progressPercentage}% complete</Text>
        </TouchableOpacity>
      ))}
    </View>
  )
}
// Full in-progress awards list
function InProgressAwardsScreen() {
  const [awards, setAwards] = useState([])

  useEffect(() => {
    getInProgressAwards().then(setAwards)
  }, [])

  return (
    <FlatList
      data={awards}
      keyExtractor={item => item.awardId}
      renderItem={({ item }) => (
        <AwardProgressCard
          badge={item.badge}
          title={item.awardTitle}
          progress={item.progressPercentage}
          brand={item.brand}
        />
      )}
    />
  )
}
Parameters:
Name Type Attributes Default Description
brand string <optional>
null Brand to filter by (drumeo, pianote, guitareo, singeo), or null for all brands
options AwardPaginationOptions <optional>
{} Optional pagination options
Returns:
Array of in-progress award objects sorted by progress percentage (highest first)
Type
Promise.<Array.<AwardInfo>>

(static) registerAwardCallback(callback) → {UnregisterFunction}

Description:
  • Registers a callback to be notified when the user earns a new award. Only one callback can be registered at a time - registering a new one replaces the previous. Always call the returned cleanup function when your component unmounts. The callback receives an award object with: - `awardId` - Unique Sanity award ID - `name` - Display name of the award - `badge` - URL to badge image - `contentType` - Content type ('guided-course' or 'learning-path-v2') - `completed_at` - ISO timestamp - `isCompleted` - Boolean indicating the award is completed (always true for granted awards) - `completion_data.message` - Pre-generated congratulations message - `completion_data.practice_minutes` - Total practice time - `completion_data.days_user_practiced` - Days spent practicing - `completion_data.content_title` - Title of completed content
Source:
Examples
// React Native - Show award celebration modal
function useAwardNotification() {
  const [award, setAward] = useState(null)

  useEffect(() => {
    return registerAwardCallback((awardData) => {
      setAward({
        title: awardData.name,
        badge: awardData.badge,
        message: awardData.completion_data.message,
        practiceMinutes: awardData.completion_data.practice_minutes
      })
    })
  }, [])

  return { award, dismissAward: () => setAward(null) }
}
// Track award in analytics
useEffect(() => {
  return registerAwardCallback((award) => {
    analytics.track('Award Earned', {
      awardId: award.awardId,
      awardName: award.name,
      practiceMinutes: award.completion_data.practice_minutes,
      contentTitle: award.completion_data.content_title
    })
  })
}, [])
Parameters:
Name Type Description
callback AwardCallbackFunction Function called with award data when an award is earned
Returns:
Cleanup function to unregister this callback
Type
UnregisterFunction

(static) registerProgressCallback(callback) → {UnregisterFunction}

Description:
  • Registers a callback to be notified when award progress changes (but award is not yet complete). Only one callback can be registered at a time. Use this to update progress bars or show "almost there" encouragement. The callback receives: - `awardId` - Unique Sanity award ID - `progressPercentage` - Current completion percentage (0-99) Note: When an award reaches 100%, `registerAwardCallback` fires instead.
Source:
Examples
// React Native - Update progress in learning path screen
function LearningPathScreen({ learningPathId }) {
  const [awardProgress, setAwardProgress] = useState({})

  useEffect(() => {
    return registerProgressCallback(({ awardId, progressPercentage }) => {
      setAwardProgress(prev => ({
        ...prev,
        [awardId]: progressPercentage
      }))
    })
  }, [])

  // Use awardProgress to update UI
}
// Show encouragement toast at milestones
useEffect(() => {
  return registerProgressCallback(({ awardId, progressPercentage }) => {
    if (progressPercentage === 50) {
      showToast('Halfway to your award!')
    } else if (progressPercentage >= 90) {
      showToast('Almost there! Just a few more lessons.')
    }
  })
}, [])
Parameters:
Name Type Description
callback ProgressCallbackFunction Function called with progress data when award progress changes
Returns:
Cleanup function to unregister this callback
Type
UnregisterFunction

(static) resetAllAwards() → {Promise.<{deletedCount: number}>}

Source:
Returns:
Type
Promise.<{deletedCount: number}>

(async, inner) fetchCertificate(awardId) → {Promise.<Certificate>}

Description:
  • Fetch certificate data for a completed award with all images converted to base64. Returns certificate information ready for rendering in a PDF or image format. All image URLs (logos, signatures, ribbons) are converted to base64 strings for offline use.
Source:
Examples
Generate certificate PDF (Web)
const cert = await fetchCertificate('abc-123')
generatePDF({
  userName: cert.user_name,
  awardTitle: cert.title,
  completedAt: new Date(cert.completed_at).toLocaleDateString(),
  message: cert.message,
  brandLogo: `data:image/png;base64,${cert.brand_logo_64}`,
  signature: cert.instructor_signature_64
    ? `data:image/png;base64,${cert.instructor_signature_64}`
    : null
})
Display certificate preview (Web)
const cert = await fetchCertificate(awardId)
return (
  <CertificatePreview
    userName={cert.user_name}
    awardTitle={cert.title}
    message={cert.message}
    awardImage={`data:image/png;base64,${cert.award_image_64}`}
    instructorName={cert.instructor_name}
    signature={cert.instructor_signature_64}
  />
)
React Native Implementation
// This function is NOT compatible with React Native due to FileReader/Blob APIs.
// For React Native, implement certificate generation using:
//
// 1. Use react-native-blob-util for base64 image conversion:
//    import ReactNativeBlobUtil from 'react-native-blob-util'
//    const base64 = await ReactNativeBlobUtil.fetch('GET', imageUrl)
//      .then(res => res.base64())
//
// 2. Use react-native-html-to-pdf for PDF generation:
//    import RNHTMLtoPDF from 'react-native-html-to-pdf'
//    const pdf = await RNHTMLtoPDF.convert({
//      html: certificateHtmlTemplate,
//      fileName: `certificate-${awardId}`,
//      directory: 'Documents',
//    })
//
// 3. Build certificate data using getContentAwards() or getCompletedAwards()
//    which ARE React Native compatible, then handle image conversion
//    and PDF generation in your RN app layer.
Parameters:
Name Type Description
awardId string Unique Sanity award ID
Throws:
If award is not found or not completed
Type
Error
Returns:
Certificate object with base64-encoded images: - award_id {string} - Sanity award ID - user_name {string} - User's display name - user_id {number} - User's ID - completed_at {string} - ISO timestamp of completion - message {string} - Certificate message for display - type {string} - Award type (e.g., 'content-award') - title {string} - Award title/name - musora_logo {string} - URL to Musora logo - musora_logo_64 {string} - Base64-encoded Musora logo - musora_bg_logo {string} - URL to Musora background logo - musora_bg_logo_64 {string} - Base64-encoded background logo - brand_logo {string} - URL to brand logo - brand_logo_64 {string} - Base64-encoded brand logo - ribbon_image {string} - URL to ribbon decoration - ribbon_image_64 {string} - Base64-encoded ribbon image - award_image {string} - URL to award image - award_image_64 {string} - Base64-encoded award image - instructor_name {string} - Instructor's name - instructor_signature {string|undefined} - URL to signature (if available) - instructor_signature_64 {string|undefined} - Base64-encoded signature
Type
Promise.<Certificate>
Query award progress, listen for award events, and generate certificates. **Query Functions** (read-only): - `getContentAwards(contentId)` - Get awards for a learning path/course - `getContentAwardsByIds(contentIds)` - Get awards for multiple content items (batch optimized) - `getCompletedAwards(brand)` - Get user's earned awards - `getInProgressAwards(brand)` - Get awards user is working toward - `getAwardStatistics(brand)` - Get aggregate award stats **Event Callbacks**: - `registerAwardCallback(fn)` - Listen for new awards earned - `registerProgressCallback(fn)` - Listen for progress updates **Certificates**: - `fetchCertificate(awardId)` - Generate certificate (Web only)
Description:
  • Query award progress, listen for award events, and generate certificates. **Query Functions** (read-only): - `getContentAwards(contentId)` - Get awards for a learning path/course - `getContentAwardsByIds(contentIds)` - Get awards for multiple content items (batch optimized) - `getCompletedAwards(brand)` - Get user's earned awards - `getInProgressAwards(brand)` - Get awards user is working toward - `getAwardStatistics(brand)` - Get aggregate award stats **Event Callbacks**: - `registerAwardCallback(fn)` - Listen for new awards earned - `registerProgressCallback(fn)` - Listen for progress updates **Certificates**: - `fetchCertificate(awardId)` - Generate certificate (Web only)
Source:
Examples
Quick Start
import {
  getContentAwards,
  getCompletedAwards,
  registerAwardCallback
} from 'musora-content-services'

// Show awards for a learning path
const { hasAwards, awards } = await getContentAwards(learningPathId)

// Get user's completed awards
const completed = await getCompletedAwards('drumeo')

// Listen for new awards (show notification)
useEffect(() => {
  return registerAwardCallback((award) => {
    showCelebration(award.name, award.badge, award.completion_data.message)
  })
}, [])
How Awards Update (Collection Context)
// Award progress updates automatically when you save content progress.
// CRITICAL: Pass collection context when inside a learning path!

import { contentStatusCompleted } from 'musora-content-services'

// Correct - passes collection context
const collection = { type: 'learning-path-v2', id: learningPathId }
await contentStatusCompleted(lessonId, collection)

// Wrong - no collection context (affects wrong awards!)
await contentStatusCompleted(lessonId, null)

Methods

(static) getAwardStatistics(brandopt) → {Promise.<AwardStatistics>}

Description:
  • Returns aggregate statistics about the user's award progress. Use this for profile stats, gamification dashboards, or achievement summaries. Returns an object with: - `totalAvailable` - Total awards that can be earned - `completed` - Number of awards earned - `inProgress` - Number of awards started but not completed - `notStarted` - Number of awards not yet started - `completionPercentage` - Overall completion % (0-100, one decimal) Returns all zeros on error (never throws).
Source:
Examples
// Profile stats card
function ProfileStatsCard() {
  const [stats, setStats] = useState(null)
  const brand = useBrand()

  useEffect(() => {
    getAwardStatistics(brand).then(setStats)
  }, [brand])

  if (!stats) return <LoadingSpinner />

  return (
    <View style={styles.statsCard}>
      <StatItem label="Awards Earned" value={stats.completed} />
      <StatItem label="In Progress" value={stats.inProgress} />
      <StatItem label="Available" value={stats.totalAvailable} />
      <ProgressRing
        progress={stats.completionPercentage / 100}
        label={`${stats.completionPercentage}%`}
      />
    </View>
  )
}
// Achievement progress bar
const stats = await getAwardStatistics('drumeo')
return (
  <View>
    <Text>{stats.completed} of {stats.totalAvailable} awards earned</Text>
    <ProgressBar progress={stats.completionPercentage / 100} />
  </View>
)
Parameters:
Name Type Attributes Default Description
brand string <optional>
null Brand to filter by (drumeo, pianote, guitareo, singeo), or null for all brands
Returns:
Statistics object with award counts and completion percentage
Type
Promise.<AwardStatistics>

(static) getCompletedAwards(brandopt, optionsopt) → {Promise.<Array.<AwardInfo>>}

Description:
  • Returns all awards the user has completed. Use this for "My Achievements" or profile award gallery screens. Each award includes: - Badge and award images for display - Completion date for "Earned on X" display - `completionData.message` - Pre-generated congratulations text - `completionData.XXX` - other fields are award type dependant Returns empty array `[]` on error (never throws).
Source:
Examples
// Awards gallery screen
function AwardsGalleryScreen() {
  const [awards, setAwards] = useState([])
  const brand = useBrand() // 'drumeo', 'pianote', etc.

  useEffect(() => {
    getCompletedAwards(brand).then(setAwards)
  }, [brand])

  return (
    <FlatList
      data={awards}
      keyExtractor={item => item.awardId}
      numColumns={2}
      renderItem={({ item }) => (
        <AwardBadge
          badge={item.badge}
          title={item.awardTitle}
          earnedDate={new Date(item.completedAt).toLocaleDateString()}
          onPress={() => showAwardDetail(item)}
        />
      )}
    />
  )
}
// Show award detail with practice stats
function AwardDetailModal({ award }) {
  return (
    <Modal>
      <Image source={{ uri: award.award }} />
      <Text>{award.awardTitle}</Text>
      <Text>Instructor: {award.instructorName}</Text>
      <Text>Completed: {new Date(award.completedAt).toLocaleDateString()}</Text>
      <Text>Practice time: {award.completionData.practice_minutes} minutes</Text>
      <Text>Days practiced: {award.completionData.days_user_practiced}</Text>
      <Text>{award.completionData.message}</Text>
    </Modal>
  )
}
// Paginated loading
const PAGE_SIZE = 12
const loadMore = async (page) => {
  const newAwards = await getCompletedAwards(brand, {
    limit: PAGE_SIZE,
    offset: page * PAGE_SIZE
  })
  setAwards(prev => [...prev, ...newAwards])
}
Parameters:
Name Type Attributes Default Description
brand string <optional>
null Brand to filter by (drumeo, pianote, guitareo, singeo), or null for all brands
options AwardPaginationOptions <optional>
{} Optional pagination and filtering
Returns:
Array of completed award objects sorted by completion date (newest first)
Type
Promise.<Array.<AwardInfo>>

(static) getContentAwards(contentId) → {Promise.<ContentAwardsResponse>}

Description:
  • Returns awards associated with a content item and the user's progress toward each. Use this to display award progress on learning path or course detail pages. - Pass a **learning path ID** to get awards for that learning path - Pass a **course ID** to get awards for that course - Pass a **lesson ID** to get all awards that include that lesson in their requirements Returns `{ hasAwards: false, awards: [] }` on error (never throws).
Source:
Examples
// Display award card on learning path detail page
function LearningPathAwardCard({ learningPathId }) {
  const [awardData, setAwardData] = useState({ hasAwards: false, awards: [] })

  useEffect(() => {
    getContentAwards(learningPathId).then(setAwardData)
  }, [learningPathId])

  if (!awardData.hasAwards) return null

  const award = awardData.awards[0] // Learning paths typically have one award
  return (
    <AwardCard
      badge={award.badge}
      title={award.awardTitle}
      progress={award.progressPercentage}
      isCompleted={award.isCompleted}
      completedAt={award.completedAt}
      instructorName={award.instructorName}
    />
  )
}
// Check award status before showing certificate button
const { hasAwards, awards } = await getContentAwards(learningPathId)
const completedAward = awards.find(a => a.isCompleted)
if (completedAward) {
  showCertificateButton(completedAward.awardId)
}
Parameters:
Name Type Description
contentId number Railcontent ID of the content item (lesson, course, or learning path)
Returns:
Status object with award information
Type
Promise.<ContentAwardsResponse>

(static) getContentAwardsByIds(contentIds) → {Promise.<Object.<number, ContentAwardsResponse>>}

Description:
  • Returns awards for multiple content items at once. More efficient than calling `getContentAwards()` multiple times. Returns an object where keys are content IDs and values are the same structure as `getContentAwards()`. Content IDs without awards will have `{ hasAwards: false, awards: [] }` in the result. Returns empty object `{}` on error (never throws).
Source:
Examples
const learningPathIds = [12345, 67890, 11111]
const awardsMap = await getContentAwardsByIds(learningPathIds)

learningPathIds.forEach(id => {
  const { hasAwards, awards } = awardsMap[id] || { hasAwards: false, awards: [] }
  if (hasAwards) {
    console.log(`Learning path ${id} has ${awards.length} award(s)`)
  }
})
function CourseListWithAwards({ courseIds }) {
  const [awardsMap, setAwardsMap] = useState({})

  useEffect(() => {
    getContentAwardsByIds(courseIds).then(setAwardsMap)
  }, [courseIds])

  return courseIds.map(courseId => {
    const { hasAwards, awards } = awardsMap[courseId] || { hasAwards: false, awards: [] }
    return (
      <CourseCard key={courseId} courseId={courseId}>
        {hasAwards && <AwardBadge award={awards[0]} />}
      </CourseCard>
    )
  })
}
Parameters:
Name Type Description
contentIds Array.<number> Array of Railcontent IDs to fetch awards for
Returns:
Object mapping content IDs to their award data
Type
Promise.<Object.<number, ContentAwardsResponse>>

(static) getInProgressAwards(brandopt, optionsopt) → {Promise.<Array.<AwardInfo>>}

Description:
  • Returns awards the user has started but not yet completed. Sorted by progress percentage (highest first) so awards closest to completion appear first. Use this for "Continue Learning" or dashboard widgets. Progress is calculated based on lessons completed within the correct collection context. For learning paths, only lessons completed within that specific learning path count toward its award. Returns empty array `[]` on error (never throws).
Source:
Examples
// "Almost There" widget on home screen
function AlmostThereWidget() {
  const [topAwards, setTopAwards] = useState([])
  const brand = useBrand()

  useEffect(() => {
    // Get top 3 closest to completion
    getInProgressAwards(brand, { limit: 3 }).then(setTopAwards)
  }, [brand])

  if (topAwards.length === 0) return null

  return (
    <View>
      <Text>Almost There!</Text>
      {topAwards.map(award => (
        <TouchableOpacity
          key={award.awardId}
          onPress={() => navigateToLearningPath(award)}
        >
          <Image source={{ uri: award.badge }} />
          <Text>{award.awardTitle}</Text>
          <ProgressBar progress={award.progressPercentage / 100} />
          <Text>{award.progressPercentage}% complete</Text>
        </TouchableOpacity>
      ))}
    </View>
  )
}
// Full in-progress awards list
function InProgressAwardsScreen() {
  const [awards, setAwards] = useState([])

  useEffect(() => {
    getInProgressAwards().then(setAwards)
  }, [])

  return (
    <FlatList
      data={awards}
      keyExtractor={item => item.awardId}
      renderItem={({ item }) => (
        <AwardProgressCard
          badge={item.badge}
          title={item.awardTitle}
          progress={item.progressPercentage}
          brand={item.brand}
        />
      )}
    />
  )
}
Parameters:
Name Type Attributes Default Description
brand string <optional>
null Brand to filter by (drumeo, pianote, guitareo, singeo), or null for all brands
options AwardPaginationOptions <optional>
{} Optional pagination options
Returns:
Array of in-progress award objects sorted by progress percentage (highest first)
Type
Promise.<Array.<AwardInfo>>

(static) registerAwardCallback(callback) → {UnregisterFunction}

Description:
  • Registers a callback to be notified when the user earns a new award. Only one callback can be registered at a time - registering a new one replaces the previous. Always call the returned cleanup function when your component unmounts. The callback receives an award object with: - `awardId` - Unique Sanity award ID - `name` - Display name of the award - `badge` - URL to badge image - `contentType` - Content type ('guided-course' or 'learning-path-v2') - `completed_at` - ISO timestamp - `isCompleted` - Boolean indicating the award is completed (always true for granted awards) - `completion_data.message` - Pre-generated congratulations message - `completion_data.practice_minutes` - Total practice time - `completion_data.days_user_practiced` - Days spent practicing - `completion_data.content_title` - Title of completed content
Source:
Examples
// React Native - Show award celebration modal
function useAwardNotification() {
  const [award, setAward] = useState(null)

  useEffect(() => {
    return registerAwardCallback((awardData) => {
      setAward({
        title: awardData.name,
        badge: awardData.badge,
        message: awardData.completion_data.message,
        practiceMinutes: awardData.completion_data.practice_minutes
      })
    })
  }, [])

  return { award, dismissAward: () => setAward(null) }
}
// Track award in analytics
useEffect(() => {
  return registerAwardCallback((award) => {
    analytics.track('Award Earned', {
      awardId: award.awardId,
      awardName: award.name,
      practiceMinutes: award.completion_data.practice_minutes,
      contentTitle: award.completion_data.content_title
    })
  })
}, [])
Parameters:
Name Type Description
callback AwardCallbackFunction Function called with award data when an award is earned
Returns:
Cleanup function to unregister this callback
Type
UnregisterFunction

(static) registerProgressCallback(callback) → {UnregisterFunction}

Description:
  • Registers a callback to be notified when award progress changes (but award is not yet complete). Only one callback can be registered at a time. Use this to update progress bars or show "almost there" encouragement. The callback receives: - `awardId` - Unique Sanity award ID - `progressPercentage` - Current completion percentage (0-99) Note: When an award reaches 100%, `registerAwardCallback` fires instead.
Source:
Examples
// React Native - Update progress in learning path screen
function LearningPathScreen({ learningPathId }) {
  const [awardProgress, setAwardProgress] = useState({})

  useEffect(() => {
    return registerProgressCallback(({ awardId, progressPercentage }) => {
      setAwardProgress(prev => ({
        ...prev,
        [awardId]: progressPercentage
      }))
    })
  }, [])

  // Use awardProgress to update UI
}
// Show encouragement toast at milestones
useEffect(() => {
  return registerProgressCallback(({ awardId, progressPercentage }) => {
    if (progressPercentage === 50) {
      showToast('Halfway to your award!')
    } else if (progressPercentage >= 90) {
      showToast('Almost there! Just a few more lessons.')
    }
  })
}, [])
Parameters:
Name Type Description
callback ProgressCallbackFunction Function called with progress data when award progress changes
Returns:
Cleanup function to unregister this callback
Type
UnregisterFunction

(static) resetAllAwards() → {Promise.<{deletedCount: number}>}

Source:
Returns:
Type
Promise.<{deletedCount: number}>

(async, inner) fetchCertificate(awardId) → {Promise.<Certificate>}

Description:
  • Fetch certificate data for a completed award with all images converted to base64. Returns certificate information ready for rendering in a PDF or image format. All image URLs (logos, signatures, ribbons) are converted to base64 strings for offline use.
Source:
Examples
Generate certificate PDF (Web)
const cert = await fetchCertificate('abc-123')
generatePDF({
  userName: cert.user_name,
  awardTitle: cert.title,
  completedAt: new Date(cert.completed_at).toLocaleDateString(),
  message: cert.message,
  brandLogo: `data:image/png;base64,${cert.brand_logo_64}`,
  signature: cert.instructor_signature_64
    ? `data:image/png;base64,${cert.instructor_signature_64}`
    : null
})
Display certificate preview (Web)
const cert = await fetchCertificate(awardId)
return (
  <CertificatePreview
    userName={cert.user_name}
    awardTitle={cert.title}
    message={cert.message}
    awardImage={`data:image/png;base64,${cert.award_image_64}`}
    instructorName={cert.instructor_name}
    signature={cert.instructor_signature_64}
  />
)
React Native Implementation
// This function is NOT compatible with React Native due to FileReader/Blob APIs.
// For React Native, implement certificate generation using:
//
// 1. Use react-native-blob-util for base64 image conversion:
//    import ReactNativeBlobUtil from 'react-native-blob-util'
//    const base64 = await ReactNativeBlobUtil.fetch('GET', imageUrl)
//      .then(res => res.base64())
//
// 2. Use react-native-html-to-pdf for PDF generation:
//    import RNHTMLtoPDF from 'react-native-html-to-pdf'
//    const pdf = await RNHTMLtoPDF.convert({
//      html: certificateHtmlTemplate,
//      fileName: `certificate-${awardId}`,
//      directory: 'Documents',
//    })
//
// 3. Build certificate data using getContentAwards() or getCompletedAwards()
//    which ARE React Native compatible, then handle image conversion
//    and PDF generation in your RN app layer.
Parameters:
Name Type Description
awardId string Unique Sanity award ID
Throws:
If award is not found or not completed
Type
Error
Returns:
Certificate object with base64-encoded images: - award_id {string} - Sanity award ID - user_name {string} - User's display name - user_id {number} - User's ID - completed_at {string} - ISO timestamp of completion - message {string} - Certificate message for display - type {string} - Award type (e.g., 'content-award') - title {string} - Award title/name - musora_logo {string} - URL to Musora logo - musora_logo_64 {string} - Base64-encoded Musora logo - musora_bg_logo {string} - URL to Musora background logo - musora_bg_logo_64 {string} - Base64-encoded background logo - brand_logo {string} - URL to brand logo - brand_logo_64 {string} - Base64-encoded brand logo - ribbon_image {string} - URL to ribbon decoration - ribbon_image_64 {string} - Base64-encoded ribbon image - award_image {string} - URL to award image - award_image_64 {string} - Base64-encoded award image - instructor_name {string} - Instructor's name - instructor_signature {string|undefined} - URL to signature (if available) - instructor_signature_64 {string|undefined} - Base64-encoded signature
Type
Promise.<Certificate>