Skip to content
Learni
View all tutorials
Outils de développement

How to Integrate the TalentLMS API in 2026

Lire en français

Introduction

TalentLMS is a powerful cloud LMS platform for deploying scalable online training, used by over 25,000 organizations. In 2026, its REST API v1 remains the standard for automated integrations: syncing users from a CRM, dynamically creating courses from a database, or sending certificates via webhooks.

This intermediate tutorial guides you through integrating the TalentLMS API into a Node.js app with TypeScript. You'll learn to set up Basic Auth (domain + API key), list users, create a course, and handle errors. Each step includes complete, functional code ready to copy-paste. By the end, you'll have a robust integration that avoids common pitfalls like rate limits (100 req/min).

Why it matters: HR teams automate 70% of enrollments via API, reducing manual tasks by 80%. Ready to supercharge your training stack?

Prerequisites

  • Active TalentLMS account (Starter plan is enough for API testing).
  • API key generated in Account & Settings > API Authentication (format: api_key:secret).
  • TalentLMS domain (e.g., mondomaine.talentlms.com).
  • Node.js 20+ and npm/yarn installed.
  • Basic knowledge of TypeScript and fetch/axios.
  • Editor like VS Code with TypeScript extension.

Initialize the Node.js Project

terminal
mkdir talentlms-integration
cd talentlms-integration
npm init -y
npm install typescript @types/node axios dotenv
npm install -D ts-node nodemon
npx tsc --init
mkdir src

These commands create a clean Node.js project, install axios for HTTP calls, dotenv for environment variables, and TypeScript for static typing. ts-node lets you run TS files directly. Stick to npm if targeting production.

Set Up Environment Variables

Create a .env file at the root to securely store your TalentLMS credentials. Never commit this file (add it to .gitignore). The API uses Basic Auth: encode api_key:secret in base64.

Configuration File and Types

src/config.ts
import dotenv from 'dotenv';
dotenv.config();

export interface TalentLMSConfig {
  domain: string;
  apiKey: string;
  apiSecret: string;
  baseUrl: string;
}

export const config: TalentLMSConfig = {
  domain: process.env.TALENTLMS_DOMAIN || 'mondomaine.talentlms.com',
  apiKey: process.env.TALENTLMS_API_KEY || '',
  apiSecret: process.env.TALENTLMS_API_SECRET || '',
  baseUrl: `https://${process.env.TALENTLMS_DOMAIN || 'mondomaine.talentlms.com'}/api/v1`,
};

if (!config.apiKey || !config.apiSecret) {
  throw new Error('TALENTLMS_API_KEY et TALENTLMS_API_SECRET manquants dans .env');
}

This module loads env vars and defines a TypeScript interface for the config. It validates credentials on startup to prevent runtime errors. Use it everywhere to centralize API access, like a single 'toolbox'.

Implement Basic Auth

TalentLMS requires an Authorization: Basic header. Let's create a reusable helper to generate this token, with retry logic for 401 Unauthorized.

Authentication Module

src/auth.ts
import axios, { AxiosInstance, AxiosError } from 'axios';
import { config } from './config';

const authToken = Buffer.from(`${config.apiKey}:${config.apiSecret}`).toString('base64');

export const apiClient: AxiosInstance = axios.create({
  baseURL: config.baseUrl,
  timeout: 10000,
  headers: {
    'Authorization': `Basic ${authToken}`,
    'Content-Type': 'application/json',
  },
});

apiClient.interceptors.response.use(
  (response) => response,
  (error: AxiosError) => {
    if (error.response?.status === 401) {
      throw new Error('Authentification échouée : vérifiez api_key et secret');
    }
    if (error.response?.status === 429) {
      throw new Error('Rate limit dépassé : attendez 1 min');
    }
    return Promise.reject(error);
  }
);

export default apiClient;

This Axios client preconfigures Basic Auth and handles common errors (401, 429) via interceptors. Reusable for all endpoints, it times out after 10s to avoid hangs. Test it with a GET /courses to verify.

List Users

Endpoint: GET /users. Optional params: page=1, per_page=50. JSON response with users[] including id, email, firstname.

Script to List Users

src/users.ts
import apiClient from './auth';

interface TalentLMSUser {
  id: number;
  email: string;
  firstname: string;
  lastname: string;
  status: string;
}

interface UsersResponse {
  users: TalentLMSUser[];
  page: number;
  per_page: number;
  total_pages: number;
}

export async function listUsers(page: number = 1, perPage: number = 20): Promise<UsersResponse> {
  try {
    const response = await apiClient.get<UsersResponse>('/users', {
      params: { page, 'per_page': perPage },
    });
    console.log(`Page ${page} : ${response.data.users.length} users trouvés.`);
    return response.data;
  } catch (error) {
    console.error('Erreur lors du listage users:', error);
    throw error;
  }
}

// Exemple d'usage
if (import.meta.url === `file://${process.argv[1]}`) {
  listUsers().then(console.log).catch(console.error);
}

This typed script fetches a paginated list of users and logs the results. Use page and per_page to scale (max 100). Run with ts-node src/users.ts after sourcing .env.

Create a New Course

Endpoint: POST /courses. JSON body: {name, description, price}. Response: {success: true, id: 123}. Check quotas (500 courses max/plan).

Script to Create a Course

src/courses.ts
import apiClient from './auth';

export interface CreateCoursePayload {
  name: string;
  description?: string;
  price: number;
  catalog_visibility?: 'public' | 'private';
}

export interface CourseResponse {
  success: boolean;
  id: number;
}

export async function createCourse(payload: CreateCoursePayload): Promise<CourseResponse> {
  try {
    const response = await apiClient.post<CourseResponse>('/courses', payload);
    console.log(`Cours créé avec ID: ${response.data.id}`);
    return response.data;
  } catch (error) {
    console.error('Erreur création cours:', error);
    throw error;
  }
}

// Exemple
if (import.meta.url === `file://${process.argv[1]}`) {
  createCourse({
    name: 'Introduction à TypeScript 2026',
    description: 'Cours avancé sur les generics et async.',
    price: 49.99,
    catalog_visibility: 'public',
  }).then(console.log).catch(console.error);
}

This code POSTs a course with TS interface validation. catalog_visibility controls access. Handle duplicates (400 error if name exists). Perfect for automating from a CMS.

Enroll a User in a Course

Endpoint: POST /users/{user_id}/courses. Body: {course_id: 123}. Great for automated onboarding.

User-Course Enrollment Script

src/enrollments.ts
import apiClient from './auth';

export interface EnrollmentPayload {
  course_id: number;
}

export async function enrollUser(userId: number, payload: EnrollmentPayload): Promise<{success: boolean}> {
  try {
    const response = await apiClient.post(`users/${userId}/courses`, payload);
    console.log(`User ${userId} inscrit au cours ${payload.course_id}`);
    return { success: true };
  } catch (error) {
    console.error('Erreur inscription:', error);
    return { success: false };
  }
}

// Exemple : user 42 au cours 123
if (import.meta.url === `file://${process.argv[1]}`) {
  enrollUser(42, { course_id: 123 }).then(console.log);
}

POST to enroll a specific user. Verify user_id and course_id via listUsers/createCourse. Returns boolean for chaining. Avoids duplicate enrollments (API ignores silently).

Main Integration Script

src/index.ts
#!/usr/bin/env node
import { listUsers } from './users';
import { createCourse } from './courses';
import { enrollUser } from './enrollments';

async function main() {
  console.log('🚀 Début intégration TalentLMS');
  
  // 1. Lister users
  const users = await listUsers(1, 5);
  console.table(users.users.slice(0, 3));
  
  // 2. Créer cours
  const newCourse = await createCourse({
    name: 'API Mastery 2026',
    price: 99.99,
  });
  
  // 3. Inscrire premier user
  if (users.users[0]) {
    await enrollUser(users.users[0].id, { course_id: newCourse.id });
  }

  console.log('✅ Intégration terminée');
}

main().catch(console.error);

Orchestrator script combining all functions. Run with ts-node src/index.ts. Chains operations for a full workflow: list > create > enroll. Add try/catch for production.

Add package.json Scripts

Update package.json for easy commands.

npm Scripts in package.json

package.json
{
  "name": "talentlms-integration",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "nodemon --exec ts-node src/index.ts",
    "start": "ts-node src/index.ts",
    "users": "ts-node src/users.ts",
    "courses": "ts-node src/courses.ts"
  },
  "dependencies": {
    "axios": "^1.7.2",
    "dotenv": "^16.4.5",
    "typescript": "^5.5.3"
  },
  "devDependencies": {
    "@types/node": "^22.3.0",
    "ts-node": "^10.9.2",
    "nodemon": "^3.1.4"
  }
}

Adds npm scripts for hot-reload (dev) and one-shot runs. type: module enables modern ES modules. Run npm run start after setting up .env.

Best Practices

  • Rate limiting: Add a 600ms delay between requests (100/min max). Use p-limit for concurrency.
  • Security: Never hardcode credentials; use AWS Secrets or Vault in production.
  • Pagination: Loop over total_pages for >100 users, with offset to avoid duplicates.
  • Logging: Integrate Winston or Pino for tracing API calls in production.
  • Full typing: Extend interfaces from the TalentLMS API docs for VS Code autocomplete.

Common Errors to Avoid

  • 401 Unauthorized: Check exact base64 encoding (api_key:secret, no spaces). Regenerate key if expired.
  • 404 Not Found: Misspelled domain (e.g., mondomaine without .talentlms.com).
  • 422 Validation: Invalid JSON body (e.g., negative price). Validate payload client-side.
  • CORS in frontend: TalentLMS API blocks browsers; use Next.js proxy or server-side only.

Next Steps

  • Explore TalentLMS webhooks for real-time notifications (e.g., user_completed_course).
  • Integrate with Zapier or n8n for no-code workflows.
  • Migrate to GraphQL for >10k users (2026 beta).
  • Official API Docs.
Check out our Learni courses on LMS and APIs to master H5P, SCORM, and more.