Migrate importExternalCoursesStats

This guide details how to migrate from the v1 importExternalCoursesStats endpoint to the new v2 architecture.

💡

Key changes

  • From synchronous to asynchronous processing: API v2 processes statistics imports asynchronously, allowing bulk operations of up to 10,000 records per request.
  • Simplified field structure: v2 consolidates activity tracking with clearer semantics and removes complex auto-calculation scenarios.
  • Enhanced control: v2 introduces the forceNew parameter to explicitly control whether statistics create new attempts or update existing ones.
  • Flexible user and course identification: v2 introduces identifier objects that allow you to reference courses and users by either external or internal IDs

 Documentation links

Endpoint mapping

  • v1: /api/v1/externalContents/groups/:groupId/externalPlatforms/:externalPlatform/courses/stats
  • v2: /api/v2/bulk/integrations/{integrationId}/stats

Prerequisites

Before migrating to v2, you must have an integration configuration created. If you haven't done this yet, see step 2 in Create an API-based integration for instructions on creating an integration using the POST /api/v2/integrations endpoint.

You'll need your integrationId (the _id returned when creating the integration).

Main input changes

Change type

API v1

API v2

Modified
Platform context

Path variables: groupId and externalPlatform

Path variable: integrationId (contains both platform and group context)

Modified

statistics (max 20 objects)

input (max 10,000 objects): Renamed.

Modified

externalId

courseIdentifier.value: Now nested in object with type field

Modified

mail

userIdentifier.value: Now nested in object with type field

Modified

globalTime

timeSpent: Renamed and now required; still in milliseconds

Modified

completedAt

lastActivityAt: Renamed and now required; when progress=100, this represents completion date

Modified

firstActivity

firstActivityAt: Renamed and now required

Modified

lastActivity

lastActivityAt: Renamed and now required

Added

courseIdentifier.type: New field: "externalId" or "internalId"

Added

userIdentifier.type: New field: "mail" or "internalId"

Added

forceNew: New field: controls attempt creation vs. update behavior

Added

lang: New field: language in which learner played the course

Behavioral changes

v1: Complex auto-calculation logic

v1 had four scenarios for calculating activity timestamps and globalTime.

v2 removes all auto-calculation logic. You must provide all required fields explicitly:

  • progress (0-100)
  • timeSpent (in milliseconds)
  • firstActivityAt (ISO 8601 datetime)
  • lastActivityAt (ISO 8601 datetime)

Attempt creation/update logic

v2 uses the forceNew parameter to control behavior:

  • forceNew: true → Always creates a new attempt
  • forceNew: false (default) → Updates existing non-completed attempts OR creates new attempt if:
    • No attempt exists for this course/user combination
    • firstActivityAt is later than all existing attempts' lastActivityAt or completedAt

Main output changes

API v2 shifted from synchronous to asynchronous operations. Instead of an immediate result (200 OK), it returns a 202 Accepted status code indicating the bulk request has been queued. The full result must be tracked via the Location header.