REST API v1 · OpenAPI 3.1
Kernel Isolation: PostgreSQL RLSRate limits: per key, published on the developer page

Personal Academy REST API Reference

Integrate course provisioning, enrollment automations, skill competencies, and compliance reporting directly into your HRIS, custom portals, and automated CI/CD pipelines.

API Architectural Specifications & Authentication

The Personal Academy API is a RESTful interface described by an OpenAPI 3.1 document. All requests require HTTPS and Bearer token authentication via organisation API keys (Authorization: Bearer pak_live_...). Each key carries module scopes (such as module.learn) and is rate-limited per key (the published limits are on the developer page). Organisation isolation is enforced at the database kernel level, ensuring zero cross-organisation data leakage. Create keys and test endpoints in the Developer Portal, and view the raw OpenAPI schema at /api/v1/openapi.json.

Quick Start: Authenticating Requests

Create an API key in Admin > Developer Portal, then pass it as a Bearer token.

Download OpenAPI JSON
# List your organisation's courses using cURL
curl -X GET "https://<your-host>/api/v1/learn/courses" \
  -H "Authorization: Bearer pak_live_YOUR_API_KEY" \
  -H "Accept: application/json"

# A route that acts as one learner also needs the learner header
curl -X GET "https://<your-host>/api/v1/me/transcript" \
  -H "Authorization: Bearer pak_live_YOUR_API_KEY" \
  -H "X-Learner-Id: emp-00412"

Scopes, Rate Limit & the Learner Header

A key only reaches the modules it is scoped for, and only while your organisation is entitled to them. Within a module, each route also checks a permission held by the person the key acts as. Partner keys carry partner.* scopes for the Partner API instead.

module.learn

/api/v1/learn/*, /api/v1/me/*

module.assessment

/api/v1/assess/*

module.org

/api/v1/org/*, /api/v1/admin/people, departments, groups

module.events

/api/v1/events/*

module.paths

/api/v1/paths/*

module.skills

/api/v1/skills/*

module.insights

/api/v1/insights/*

module.standards

/api/v1/learn/scorm/*

module.ai

/api/v1/ai/*

module.authoring

/api/v1/authoring/*

module.community

/api/v1/social/*

module.knowledge

/api/v1/knowledge/*

module.engage

/api/v1/engage/*

module.commerce

/api/v1/commerce/*

module.governance

/api/v1/admin/audit-events, retention-policies, legal-holds

module.platform

/api/v1/admin/automation-rules, email-templates

module.integrations

/api/v1/admin/channels, /api/v1/catalogue/*

Rate-limited per key

Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. A higher tier can raise the limit; GET /api/v1/limits returns the figure in force for your key.

X-Learner-Id

Routes that act as one learner (such as /api/v1/me/*) need this header, set to the learner’s external_id or user_id. The learner must belong to the key’s organisation, and the header can never act as an administrator.

Core Platform Endpoints

A selection of v1 endpoints. Every route, with its parameters and errors, is in the OpenAPI document at /api/v1/openapi.json; the full reference is on the developer page. Every key is rate-limited (the published limits are on the developer page); GET /api/v1/limits returns the figure in force for your key.

GET/api/v1/learn/courses
Scope: module.learn
Permission: course.view

List courses

Courses visible to your organisation, newest first by default. Cursor-paginated; filter with status and search.

Response (200 OK)
{
  "data": [
    {
      "id": "6f1c2a0e-1b7d-4c55-9d0a-3e2f8b7c1a90",
      "title": "HIPAA Security & Clinical Privacy 2026",
      "description": "Annual privacy refresher for clinical staff.",
      "status": "published",
      "difficulty_level": "Beginner",
      "duration_minutes": 45,
      "created_at": "2026-08-12T09:00:00Z",
      "updated_at": "2026-09-01T14:30:00Z"
    }
  ],
  "meta": {
    "cursor": {
      "next": "eyJ2IjoiMjAyNi0wOC0xMlQwOTowMDowMFoifQ"
    }
  }
}
POST/api/v1/learn/enrolments
Scope: module.learn
Permission: course.assign

Enrol a person in a course

Enrols an existing active member in one of your courses, by user_id or email. Idempotent: enrolling again updates the due date.

Request Payload
{
  "user_id": "2b9e4c71-8d3a-4f60-a1e5-7c0d9b8a6f12",
  "course_id": "6f1c2a0e-1b7d-4c55-9d0a-3e2f8b7c1a90",
  "due_at": "2026-10-15T23:59:59Z"
}
Response (201 Created)
{
  "data": {
    "id": "c4a7e2d9-5f13-4b8e-9a60-1d2c3b4a5e6f",
    "user_id": "2b9e4c71-8d3a-4f60-a1e5-7c0d9b8a6f12",
    "course_id": "6f1c2a0e-1b7d-4c55-9d0a-3e2f8b7c1a90",
    "enrolled_at": "2026-09-20T12:00:00Z",
    "due_at": "2026-10-15T23:59:59Z"
  }
}
GET/api/v1/insights/completions
Scope: module.insights
Permission: report.view.tenant

List completion records

Immutable, snapshotted course-completion records for your organisation. Filter by course_id or user_id.

Response (200 OK)
{
  "data": [
    {
      "id": "9d8c7b6a-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
      "user_id": "2b9e4c71-8d3a-4f60-a1e5-7c0d9b8a6f12",
      "course_id": "6f1c2a0e-1b7d-4c55-9d0a-3e2f8b7c1a90",
      "course_title": "HIPAA Security & Clinical Privacy 2026",
      "score": 96,
      "completed_at": "2026-09-18T09:15:22Z",
      "expires_at": null,
      "recert_status": "not_applicable"
    }
  ],
  "meta": {
    "cursor": {
      "next": null
    }
  }
}
POST/api/v1/org/users
Scope: module.org
Permission: user.invite

Invite a person

Invites someone into your organisation by email with one of your roles, exactly as the admin People page does.

Request Payload
{
  "email": "developer@acme.corp",
  "roleKey": "learner",
  "departmentId": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
}
Response (201 Created)
{
  "data": {
    "id": "7e6d5c4b-3a29-4180-9f7e-6d5c4b3a2918",
    "email": "developer@acme.corp",
    "role_key": "learner",
    "expires_at": "2026-09-27T12:05:00Z",
    "accept_url": "https://<your-host>/invite/...",
    "email_sent": true
  }
}
GET/api/v1/skills/scores
Scope: module.skills
Permission: report.view.tenant

List learner skill scores

Per-learner, per-skill scores for your organisation. Filter by user_id or skill_id.

Response (200 OK)
{
  "data": [
    {
      "id": "3c2b1a09-8f7e-4d6c-9b5a-4e3d2c1b0a9f",
      "user_id": "2b9e4c71-8d3a-4f60-a1e5-7c0d9b8a6f12",
      "skill_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
      "score": 88,
      "updated_at": "2026-09-20T00:00:00Z"
    }
  ],
  "meta": {
    "cursor": {
      "next": null
    }
  }
}
GET/api/v1/me/transcript
Scope: module.learn
Permission: course.view
Header: X-Learner-Id

The learner's transcript

Acts as one learner, named in the X-Learner-Id header (an external_id or a user_id). Returns completed items with verification codes, plus credit awards and totals.

Response (200 OK)
{
  "data": {
    "completed": [
      {
        "type": "course",
        "title": "HIPAA Security & Clinical Privacy 2026",
        "status": "completed",
        "date": "2026-09-18",
        "verifyCode": "sample-cybersecurity"
      }
    ],
    "credits": {
      "awards": [],
      "totals": []
    }
  }
}

Real-Time Webhooks & Signed Event Delivery

Configure automated HTTPS callbacks when learners are enrolled, complete courses or earn certificates. Every webhook payload is signed with an HMAC-SHA256 signature in the X-Webhook-Signature header so your application can verify authenticity.

course.completed

Fired when a learner finishes all lessons and passes the final assessment.

certificate.issued

Emitted with the certificate’s public verification code.

enrollment.created

Fired when a learner is enrolled in a course.

Full Feature Free Trial

Ready for learning governance without the enterprise tax?

Start with a free trial. No card required.

No credit card required. Instant account setup in under 60 seconds.