API Documentation

Authorization

AcademyOcean has an API and webhooks. Webhooks are particularly useful for asynchronous events, such as when a learner starts or finishes a course.

Authorization occurs using the OAUTH v2 protocol with the client_credentials grant. For authorization, you will need:

  • Link for authorization requests: https://app.academyocean.com/oauth/token
  • URL for accessing the API: https://app.academyocean.com/api/v1

API Requests

To begin using our API:

  1. You must obtain an access key. To do so, go to the Settings section of your Academy and click API tab
  2. Next, you must set up access. To do this, click "Register a new OAuth application" and fill in all fields in the window that appears. You can change these fields at any time. Next, click "Register Application."
  3. You will be redirected to a page with the newly created access keys. Here, copy the Client ID and Client Secret and transfer them to your application.
  4. To authorize using the keys you have obtained, send a POST request using the following URL: https://app.academyocean.com/oauth/token with the following parameters:
{
  "client_id": "CLIENT_ID",
  "client_secret": "CLIENT_SECRET",
  "scope": "",
  "grant_type": "client_credentials"
}

The response will be in the following format:

{
  "token_type": "Bearer",
  "expires_in": 604800,
  "access_token": "eyJ0eX..."
}
  1. After receiving an access_token, you can send requests to the API: https://app.academyocean.com/api/v1

The request body is a JSON object that contains the action name and an options object with parameters. It looks like this:

{
  "action": "learnersRegisteredAt",
  "options": {
    "dates": ["2026-01-25 00:00:00", "2026-01-25 23:59:59"]
  }
}

In this example, we are requesting a list of learners who registered between 2026-01-25 00:00:00 and 2026-01-25 23:59:59.

Here is how a sample request looks in different languages:

Sample code
# Step 1: Get access token
curl -X POST https://app.academyocean.com/oauth/token \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=CLIENT_ID" \
  --data-urlencode "client_secret=CLIENT_SECRET" \
  --data-urlencode "scope="

# Step 2: Use the access_token from the response
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learnersRegisteredAt" \
  --data-urlencode "options[dates][0]=2026-01-25 00:00:00" \
  --data-urlencode "options[dates][1]=2026-01-25 23:59:59"
$client = new GuzzleHttp\Client();

// Step 1: Get access token
$response = $client->post('https://app.academyocean.com/oauth/token', [
    'form_params' => [
        'grant_type'    => 'client_credentials',
        'client_id'     => 'CLIENT_ID',
        'client_secret' => 'CLIENT_SECRET',
        'scope'         => '',
    ],
]);

$auth = json_decode((string)$response->getBody());

// Step 2: Make API request
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => $auth->token_type . ' ' . $auth->access_token,
    ],
    'form_params' => [
        'action' => 'learnersRegisteredAt',
        'options' => [
            'dates' => [
                '2026-01-25 00:00:00',
                '2026-01-25 23:59:59',
            ],
        ],
    ],
]);

$result = json_decode((string)$response->getBody());
var_dump($result);
const axios = require('axios');

// Step 1: Get access token
const authParams = new URLSearchParams();
authParams.append('grant_type', 'client_credentials');
authParams.append('client_id', 'CLIENT_ID');
authParams.append('client_secret', 'CLIENT_SECRET');
authParams.append('scope', '');

const authResponse = await axios.post(
    'https://app.academyocean.com/oauth/token', authParams);

const token = authResponse.data.token_type + ' ' + authResponse.data.access_token;

// Step 2: Make API request
const params = new URLSearchParams();
params.append('action', 'learnersRegisteredAt');
params.append('options[dates][0]', '2026-01-25 00:00:00');
params.append('options[dates][1]', '2026-01-25 23:59:59');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: token } });

console.log(response.data);
using var client = new HttpClient();

// Step 1: Get access token
var authContent = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"grant_type", "client_credentials"},
    {"client_id", "CLIENT_ID"},
    {"client_secret", "CLIENT_SECRET"},
    {"scope", ""}
});
var authResponse = await client.PostAsync(
    "https://app.academyocean.com/oauth/token", authContent);

var authJson = await authResponse.Content.ReadAsStringAsync();
var auth = JsonSerializer.Deserialize<JsonElement>(authJson);
var token = auth.GetProperty("token_type").GetString()
    + " " + auth.GetProperty("access_token").GetString();

// Step 2: Make API request
client.DefaultRequestHeaders.Add("Authorization", token);
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learnersRegisteredAt"},
    {"options[dates][0]", "2026-01-25 00:00:00"},
    {"options[dates][1]", "2026-01-25 23:59:59"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
import requests

# Step 1: Get access token
auth_response = requests.post(
    'https://app.academyocean.com/oauth/token',
    data={
        'grant_type': 'client_credentials',
        'client_id': 'CLIENT_ID',
        'client_secret': 'CLIENT_SECRET',
        'scope': '',
    },
)

auth = auth_response.json()
token = auth['token_type'] + ' ' + auth['access_token']

# Step 2: Make API request
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'learnersRegisteredAt',
        'options[dates][0]': '2026-01-25 00:00:00',
        'options[dates][1]': '2026-01-25 23:59:59',
    },
    headers={'Authorization': token},
)

print(response.json())
Tip #1 Save the access_token so you don't have to go through the authorization process each time you wish to use the API. If you have the access_token, you can easily insert it into the request header to send requests to the API.
Tip #2 All dates returned in the request results are in the UTC timezone
Tip #3 Our API has a rate limit, which depends on your subscription plan:
• Professional: up to 200 requests per minute
• Enterprise: up to 500 requests per minute
Higher limits are available upon request — contact our support team.

Error messages

All errors are sent in the following format:

Field name Type Example Description
error[message] string Email is required Error message

Description of Available Functions

POST

academies

Retrieve a list of all academies in the account.

Parameters

Response

FieldTypeDescription
id string Encoded academy ID
name string Academy name
slug string Academy URL slug
is_cname_enabled boolean Whether custom domain is enabled
cname_domain string | null Custom domain name or null
is_private boolean Whether the academy is private
is_published boolean Whether the academy is published
learners_amount integer Number of learners in the academy
created_at string Creation date (ISO 8601)
content_amount integer Number of content items
access_request_amount integer Number of pending access requests
Request JSON example
{
    "action": "academies"
}
Response JSON example
[
    {
        "id": "Lm4nPqR8xW3vKbYjA2Oe",
        "name": "Sales Academy",
        "slug": "sales-academy",
        "is_cname_enabled": true,
        "cname_domain": "academy.example.com",
        "is_private": false,
        "is_published": true,
        "learners_amount": 150,
        "created_at": "2026-01-10T09:00:00+00:00",
        "content_amount": 45,
        "access_request_amount": 3
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=academies"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'academies',
    ],
]);
const params = new URLSearchParams();
params.append('action', 'academies');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "academies"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={'action': 'academies'},
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

acceptedLearnerInviteRequest

Retrieve a list of learner invitations in the academy, ordered by newest first.

Parameters

Response

FieldTypeDescription
id string Encoded invite ID
email string Learner email
name string Full name
first_name string First name
last_name string Last name
academyName string Academy name
academyUrl string Academy URL
learnerAuthUrl string Learner authentication URL

Returns an array of invite objects.

Request JSON example
{
    "action": "acceptedLearnerInviteRequest"
}
Response JSON example
[
    {
        "id": "W2DAjr0BkW8BvbypL86d",
        "email": "[email protected]",
        "name": "Alex Johnson",
        "first_name": "Alex",
        "last_name": "Johnson",
        "academyName": "My Academy",
        "academyUrl": "https://myacademy.academyocean.com",
        "learnerAuthUrl": "https://myacademy.academyocean.com/auth/token/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=acceptedLearnerInviteRequest"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'acceptedLearnerInviteRequest',
    ],
]);
const params = new URLSearchParams();
params.append('action', 'acceptedLearnerInviteRequest');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "acceptedLearnerInviteRequest"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={'action': 'acceptedLearnerInviteRequest'},
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

addLearnerQuizAttempts

Add quiz attempts for a specific learner.

Parameters

ParameterTypeRequiredDescription
quiz_id string Yes Encoded quiz ID
email string Yes Learner email address

Response

FieldTypeDescription
status string "done" on success, "warning" if learner hasn't attempted the quiz
message string (Optional) Warning message explaining why attempts were not added
Request JSON example
{
    "action": "addLearnerQuizAttempts",
    "options": {
        "quiz_id": "D6gJpqlk0RjjE4Aw9ymN",
        "email": "[email protected]"
    }
}
Response JSON example
{
    "status": "done"
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=addLearnerQuizAttempts" \
  --data-urlencode "options[quiz_id]=D6gJpqlk0RjjE4Aw9ymN" \
  --data-urlencode "options[email][email protected]"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'addLearnerQuizAttempts',
        'options' => [
            'quiz_id' => 'D6gJpqlk0RjjE4Aw9ymN',
            'email' => '[email protected]',
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'addLearnerQuizAttempts');
params.append('options[quiz_id]', 'D6gJpqlk0RjjE4Aw9ymN');
params.append('options[email]', '[email protected]');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "addLearnerQuizAttempts"},
    {"options[quiz_id]", "D6gJpqlk0RjjE4Aw9ymN"},
    {"options[email]", "[email protected]"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'addLearnerQuizAttempts',
        'options[quiz_id]': 'D6gJpqlk0RjjE4Aw9ymN',
        'options[email]': '[email protected]',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

addLearnersToTeams

Add learners to one or more teams.

Parameters

ParameterTypeRequiredDescription
teams array Yes Array of encoded team IDs
learners array Yes Array of learner email addresses
send_invites boolean No Send invite emails to learners (default: false)

Response

FieldTypeDescription
(array) array Returns ["done"] on success
Request JSON example
{
    "action": "addLearnersToTeams",
    "options": {
        "teams": [
            "Xt7nBqK4mP9wRvLjA3Oe"
        ],
        "learners": [
            "[email protected]"
        ]
    }
}
Response JSON example
[
    "done"
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=addLearnersToTeams" \
  --data-urlencode "options[teams][0]=Xt7nBqK4mP9wRvLjA3Oe" \
  --data-urlencode "options[learners][0][email protected]"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'addLearnersToTeams',
        'options' => [
            'teams' => [
                'Xt7nBqK4mP9wRvLjA3Oe',
            ],
            'learners' => [
                '[email protected]',
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'addLearnersToTeams');
params.append('options[teams][0]', 'Xt7nBqK4mP9wRvLjA3Oe');
params.append('options[learners][0]', '[email protected]');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "addLearnersToTeams"},
    {"options[teams][0]", "Xt7nBqK4mP9wRvLjA3Oe"},
    {"options[learners][0]", "[email protected]"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'addLearnersToTeams',
        'options[teams][0]': 'Xt7nBqK4mP9wRvLjA3Oe',
        'options[learners][0]': '[email protected]',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

certificates

Retrieve certificates issued to a specific learner.

Parameters

ParameterTypeRequiredDescription
email string Yes Learner email address

Response

FieldTypeDescription
issued_date string Certificate issue date (ISO 8601)
course_name string Name of the course the certificate was issued for
url_to_pdf string URL to certificate PDF file
url_to_image string URL to certificate image file
number string Certificate number
Request JSON example
{
    "action": "certificates",
    "options": {
        "email": "[email protected]"
    }
}
Response JSON example
[
    {
        "issued_date": "2026-03-01T12:00:00+00:00",
        "course_name": "Onboarding Course",
        "url_to_pdf": "https://public-files.academyocean.com/public/certificates/a1b2c3d4e5f6g7h8.pdf",
        "url_to_image": "https://public-files.academyocean.com/public/certificates/a1b2c3d4e5f6g7h8.png",
        "number": "CERT-2026-001"
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=certificates" \
  --data-urlencode "options[email][email protected]"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'certificates',
        'options' => [
            'email' => '[email protected]',
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'certificates');
params.append('options[email]', '[email protected]');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "certificates"},
    {"options[email]", "[email protected]"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'certificates',
        'options[email]': '[email protected]',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

checkLearnerInvite

Check whether learner invitations exist and their status.

Parameters

ParameterTypeRequiredDescription
emails array Yes Array of learner email addresses to check

Response

FieldTypeDescription
{email}.is_found boolean Whether an invitation was found
{email}.is_used boolean | null Whether the invitation has been used (null when is_found is false)
{email}.used_at string | null Date when the invitation was used (ISO 8601)
Request JSON example
{
    "action": "checkLearnerInvite",
    "options": {
        "emails": [
            "[email protected]",
            "[email protected]"
        ]
    }
}
Response JSON example
{
    "[email protected]": {
        "is_found": true,
        "is_used": true,
        "used_at": "2026-03-01T12:00:00+00:00"
    },
    "[email protected]": {
        "is_found": true,
        "is_used": false,
        "used_at": null
    }
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=checkLearnerInvite" \
  --data-urlencode "options[emails][0][email protected]" \
  --data-urlencode "options[emails][1][email protected]"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'checkLearnerInvite',
        'options' => [
            'emails' => [
                '[email protected]',
                '[email protected]',
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'checkLearnerInvite');
params.append('options[emails][0]', '[email protected]');
params.append('options[emails][1]', '[email protected]');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "checkLearnerInvite"},
    {"options[emails][0]", "[email protected]"},
    {"options[emails][1]", "[email protected]"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'checkLearnerInvite',
        'options[emails][0]': '[email protected]',
        'options[emails][1]': '[email protected]',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

courseCertificateVariables

Retrieve certificate template variables for a specific course.

Parameters

ParameterTypeRequiredDescription
course_id string Yes Encoded course ID

Response

FieldTypeDescription
template_id integer Certificate template ID
certificate_variables array Array of certificate variable objects
certificate_variables[].code string Variable code identifier
certificate_variables[].name string Human-readable variable name
certificate_variables[].type string Variable type (text or image_url)
certificate_variables[].value string | null Current variable value
Request JSON example
{
    "action": "courseCertificateVariables",
    "options": {
        "course_id": "R5VD9yZ3bO7QWk240axm"
    }
}
Response JSON example
{
    "template_id": 42,
    "certificate_variables": [
        {
            "code": "company_name",
            "name": "Company Name",
            "type": "text",
            "value": "Acme Inc."
        },
        {
            "code": "logo",
            "name": "Company Logo",
            "type": "image_url",
            "value": "https://public-files.academyocean.com/public/logos/acme.png"
        }
    ]
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=courseCertificateVariables" \
  --data-urlencode "options[course_id]=R5VD9yZ3bO7QWk240axm"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'courseCertificateVariables',
        'options' => [
            'course_id' => 'R5VD9yZ3bO7QWk240axm',
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'courseCertificateVariables');
params.append('options[course_id]', 'R5VD9yZ3bO7QWk240axm');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "courseCertificateVariables"},
    {"options[course_id]", "R5VD9yZ3bO7QWk240axm"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'courseCertificateVariables',
        'options[course_id]': 'R5VD9yZ3bO7QWk240axm',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

courseProgress

Returns a learner's progress in all active courses/groups, with detailed content-level breakdown.

Parameters

ParameterTypeRequiredDescription
email string Yes Search for a user by email
course_type string No Filter: all / published / unpublished
with_academy boolean No Include academy ID and name
with_teams boolean No Include team IDs and names
only_displayed_courses boolean No Filter to only courses displayed to the learner (published + assigned to learner's team)

Response

FieldTypeDescription
course_id string Encoded course ID
course_name string Course/Group name
course_description string Course description
course_img string Course cover image URL
course_open_url string Direct URL to open the course
start_date string | null Course start date (ISO 8601)
finish_date string | null Course completion date (ISO 8601)
lessons_amount integer Total lessons in course
progress_amount integer Lessons passed
progress float Progress percentage
content_progress[] array Per-content breakdown (see below)

content_progress[] fields

FieldTypeDescription
id string Content ID
name string Content name
type string Content type: "lesson" or "quiz"
is_passed boolean Whether learner passed this content
started_at string | null First opening date (ISO 8601)
passed_at string | null Completion date (ISO 8601)
pass_time_sec integer | null Time taken to pass (seconds)

Additional quiz-only fields in content_progress[] (only present when is_passed is true for the quiz item)

FieldTypeDescription
score integer Score learner got for quiz
passing_score integer Minimum score to pass
is_need_manual_approve boolean Manual check is required
is_timer_on boolean Returns true if quiz has timer configured, false otherwise
timer_minutes integer Time limit in minutes
is_timer_expired boolean Whether timer expired
Request JSON example
{
    "action": "courseProgress",
    "options": {
        "email": "[email protected]"
    }
}
Response JSON example
[
    {
        "course_id": "NVpz0PmrkjNJOQW9d1Ka",
        "course_name": "Onboarding Course",
        "course_description": "Welcome to our onboarding program",
        "course_img": "https://public-files.academyocean.com/public/courses/cover.jpg",
        "course_open_url": "https://myacademy.academyocean.com/course/onboarding/open",
        "start_date": "2026-03-15T10:00:00+00:00",
        "finish_date": "2026-03-15T10:20:00+00:00",
        "lessons_amount": 2,
        "progress_amount": 2,
        "progress": 100,
        "content_progress": [
            {
                "id": "9GKDJlMkpMREXyz2Awpa",
                "type": "lesson",
                "name": "Getting Started",
                "is_passed": true,
                "started_at": "2026-03-15T10:00:00+00:00",
                "passed_at": "2026-03-15T10:15:00+00:00",
                "pass_time_sec": 900
            },
            {
                "id": "NnO5GBYv4o53baMJo6pa",
                "type": "quiz",
                "name": "Knowledge Check",
                "is_passed": true,
                "started_at": "2026-03-15T10:15:00+00:00",
                "passed_at": "2026-03-15T10:20:00+00:00",
                "pass_time_sec": 300,
                "score": 5,
                "passing_score": 3,
                "is_need_manual_approve": false,
                "is_timer_on": true,
                "is_timer_expired": false,
                "timer_minutes": 10
            }
        ]
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=courseProgress" \
  --data-urlencode "options[email][email protected]" \
  --data-urlencode "options[with_academy]=true" \
  --data-urlencode "options[with_teams]=true"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'courseProgress',
        'options' => [
            'email' => '[email protected]',
            'with_academy' => true,
            'with_teams' => true,
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'courseProgress');
params.append('options[email]', '[email protected]');
params.append('options[with_academy]', 'true');
params.append('options[with_teams]', 'true');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "courseProgress"},
    {"options[email]", "[email protected]"},
    {"options[with_academy]", "true"},
    {"options[with_teams]", "true"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'courseProgress',
        'options[email]': '[email protected]',
        'options[with_academy]': 'true',
        'options[with_teams]': 'true',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

courses

Retrieve a list of all courses in the academy.

Parameters

Response

FieldTypeDescription
id string Encoded course ID
slug string Course URL slug
name string Course name
description string Course description
img string | null Cover image URL
course_open_url string Direct URL to open the course
is_in_group boolean Whether the course belongs to a group
is_scorm boolean Whether this is a SCORM course
is_ribbon_enabled boolean Whether ribbon label is enabled
ribbon_text string Ribbon label text
Request JSON example
{
    "action": "courses"
}
Response JSON example
[
    {
        "id": "R5VD9yZ3bO7QWk240axm",
        "slug": "onboarding-course",
        "name": "Onboarding Course",
        "description": "Welcome to our company onboarding program",
        "img": "https://public-files.academyocean.com/public/courses/cover.jpg",
        "course_open_url": "https://myacademy.academyocean.com/courses/onboarding-course",
        "is_in_group": false,
        "is_scorm": false,
        "is_ribbon_enabled": true,
        "ribbon_text": "New"
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=courses"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'courses',
    ],
]);
const params = new URLSearchParams();
params.append('action', 'courses');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "courses"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={'action': 'courses'},
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

createLoginToken

Generate a one-time seamless login URL for a learner.

Parameters

ParameterTypeRequiredDescription
academy_id string Yes Encoded academy ID
email string Yes Learner email address
first_name string No First name
last_name string No Last name
course_id string No Encoded course ID to redirect to
content_id string No Encoded content ID (requires course_id)
course string No Course slug (alternative to course_id)
lesson string No Lesson slug (requires course slug)
quiz string No Quiz slug (requires course slug)
ignore_academy_privacy_mode boolean No Bypass academy privacy mode. If omitted, privacy mode is ignored (default: true).
teams array No Array of team IDs to assign the learner to

You can specify a course by course_id (encoded ID) or course (slug). Content by content_id or lesson/quiz slug.

Response

FieldTypeDescription
link string One-time seamless login URL
Request JSON example
{
    "action": "createLoginToken",
    "options": {
        "academy_id": "Rk4mPqW8xN3vLbYjA2Oe",
        "email": "[email protected]"
    }
}
Response JSON example
{
    "link": "https://myacademy.academyocean.com/auth/token/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=createLoginToken" \
  --data-urlencode "options[academy_id]=Rk4mPqW8xN3vLbYjA2Oe" \
  --data-urlencode "options[email][email protected]" \
  --data-urlencode "options[first_name]=Sarah" \
  --data-urlencode "options[last_name]=Miller"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'createLoginToken',
        'options' => [
            'academy_id' => 'Rk4mPqW8xN3vLbYjA2Oe',
            'email' => '[email protected]',
            'first_name' => 'Sarah',
            'last_name' => 'Miller',
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'createLoginToken');
params.append('options[academy_id]', 'Rk4mPqW8xN3vLbYjA2Oe');
params.append('options[email]', '[email protected]');
params.append('options[first_name]', 'Sarah');
params.append('options[last_name]', 'Miller');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "createLoginToken"},
    {"options[academy_id]", "Rk4mPqW8xN3vLbYjA2Oe"},
    {"options[email]", "[email protected]"},
    {"options[first_name]", "Sarah"},
    {"options[last_name]", "Miller"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'createLoginToken',
        'options[academy_id]': 'Rk4mPqW8xN3vLbYjA2Oe',
        'options[email]': '[email protected]',
        'options[first_name]': 'Sarah',
        'options[last_name]': 'Miller',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

deleteLearnerInvite

Delete unused learner invitations and optionally delete learner accounts. Only unused invitations (is_used = false) are deleted.

Parameters

ParameterTypeRequiredDescription
emails array Yes Array of learner email addresses
is_delete_users boolean No Also delete learner accounts (default: false)

Response

FieldTypeDescription
(array) array Returns ["done"] on success
Request JSON example
{
    "action": "deleteLearnerInvite",
    "options": {
        "emails": [
            "[email protected]"
        ]
    }
}
Response JSON example
[
    "done"
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=deleteLearnerInvite" \
  --data-urlencode "options[emails][0][email protected]"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'deleteLearnerInvite',
        'options' => [
            'emails' => [
                '[email protected]',
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'deleteLearnerInvite');
params.append('options[emails][0]', '[email protected]');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "deleteLearnerInvite"},
    {"options[emails][0]", "[email protected]"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'deleteLearnerInvite',
        'options[emails][0]': '[email protected]',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

deleteLearners

Soft-delete one or more learners. Optionally permanently delete their data.

Parameters

ParameterTypeRequiredDescription
emails array No* Array of learner email addresses
ids array No* Array of encoded learner IDs
is_permanently_delete boolean No Permanently delete learner data (default: false)

* At least one of emails or ids is required.

Response

FieldTypeDescription
deleted integer Number of learners deleted
Request JSON example
{
    "action": "deleteLearners"
}
Response JSON example
{
    "deleted": 2
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=deleteLearners" \
  --data-urlencode "options[emails][0][email protected]"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'deleteLearners',
        'options' => [
            'emails' => [
                '[email protected]',
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'deleteLearners');
params.append('options[emails][0]', '[email protected]');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "deleteLearners"},
    {"options[emails][0]", "[email protected]"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'deleteLearners',
        'options[emails][0]': '[email protected]',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

getLearnersQuizStatistic

Retrieve detailed quiz statistics for specific learners and quizzes.

Parameters

ParameterTypeRequiredDescription
records object Yes Object with learner emails as keys, each containing an array of encoded quiz IDs

Response

Quiz attempt fields

FieldTypeDescription
quiz_content_id integer Quiz content ID
name string Quiz name
score integer Score achieved
passing_score integer Minimum score required to pass
questions_amount integer Total number of questions
is_passed boolean Whether the quiz was passed
spent_time string Time spent (HH:MM:SS, e.g. 00:05:30)
spent_time_seconds integer Time spent in seconds
pass_date string Pass date (MM/DD/YYYY, e.g. 02/01/2026)
pass_time string Pass time (H:MM AM/PM, e.g. 3:25 PM)
is_timer_on boolean Whether the timer was enabled
is_expired boolean Whether the attempt expired
is_manual_approve_on boolean Whether manual approval is enabled
isCustomPointsForQuestions boolean Whether custom points are set for questions
is_linked_questions_type boolean Whether linked questions type is used
quiz_tags array Array of tag strings
count_attempts integer Total number of attempts made
pass_percent integer Pass percentage
audioFeedback string | null Audio feedback URL or null
feedbackFileId integer | null Feedback file ID or null
globalComment string | null Global comment or null
checklist object | null Checklist data or null
questions array Array of question objects

questions[] item fields

FieldTypeDescription
id string Question UUID
name string Question text
number integer Question number
type string Question type (e.g. "checkbox", "single_choice")
isHasImage boolean Whether question has an image
imageUrl string Question image URL
hotspotImageUrl string Hotspot image URL
is_passed boolean Whether question was answered correctly
isExpired boolean Whether the question expired
points integer Points scored for this question
customPoints integer Maximum possible points
noteForReviewer string Note left for reviewer
noteForReviewerState boolean Whether reviewer note is active
isHasDescriptionMedia boolean Whether question has description media
descriptionMedia string Description media content
additionalDescription string Additional description text
audioFeedback string | null Audio feedback URL or null
feedbackFileId integer | null Feedback file ID or null
questionTags array Array of question tag strings
checkerComment string Comment from manual checker
time_spent string Time spent on question (HH:MM:SS, e.g. 00:00:01)
answers object Answer object with keys: selected, missed, others (each is an array)

Response is keyed by email, then by quiz ID. Each quiz ID contains an array of attempt objects.

Request JSON example
{
    "action": "getLearnersQuizStatistic",
    "options": {
        "records": {
            "[email protected]": [
                "D6gJpqlk0RjjE4Aw9ymN"
            ]
        }
    }
}
Response JSON example
{
    "[email protected]": {
        "D6gJpqlk0RjjE4Aw9ymN": [
            {
                "quiz_content_id": 123,
                "name": "Final Assessment",
                "score": 8,
                "passing_score": 7,
                "questions_amount": 10,
                "is_passed": true,
                "spent_time": "00:05:30",
                "spent_time_seconds": 330,
                "pass_date": "02/01/2026",
                "pass_time": "3:25 PM",
                "is_timer_on": true,
                "is_expired": false,
                "is_manual_approve_on": false,
                "isCustomPointsForQuestions": false,
                "is_linked_questions_type": false,
                "quiz_tags": [
                    "assessment",
                    "final"
                ],
                "count_attempts": 2,
                "pass_percent": 80,
                "audioFeedback": null,
                "feedbackFileId": null,
                "globalComment": null,
                "checklist": null,
                "questions": [
                    {
                        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                        "name": "What is the main benefit?",
                        "number": 1,
                        "type": "single_choice",
                        "isHasImage": false,
                        "imageUrl": "",
                        "hotspotImageUrl": "",
                        "is_passed": true,
                        "isExpired": false,
                        "points": 1,
                        "customPoints": 1,
                        "noteForReviewer": "",
                        "noteForReviewerState": false,
                        "isHasDescriptionMedia": false,
                        "descriptionMedia": "",
                        "additionalDescription": "",
                        "audioFeedback": null,
                        "feedbackFileId": null,
                        "questionTags": [],
                        "checkerComment": "",
                        "time_spent": "00:00:15",
                        "answers": {
                            "selected": [
                                {
                                    "answer": "Efficiency",
                                    "is_right": true,
                                    "isHasImage": false,
                                    "imageUrl": ""
                                }
                            ],
                            "missed": [],
                            "others": []
                        }
                    }
                ]
            }
        ]
    }
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=getLearnersQuizStatistic" \
  --data-urlencode "options[records][[email protected]][0]=D6gJpqlk0RjjE4Aw9ymN"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'getLearnersQuizStatistic',
        'options' => [
            'records' => [
                '[email protected]' => [
                    'D6gJpqlk0RjjE4Aw9ymN',
                ],
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'getLearnersQuizStatistic');
params.append('options[records][[email protected]][0]', 'D6gJpqlk0RjjE4Aw9ymN');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "getLearnersQuizStatistic"},
    {"options[records][[email protected]][0]", "D6gJpqlk0RjjE4Aw9ymN"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'getLearnersQuizStatistic',
        'options[records][[email protected]][0]': 'D6gJpqlk0RjjE4Aw9ymN',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

getLearnerVariable

Retrieve variable values for one or more learners.

Parameters

ParameterTypeRequiredDescription
records object Yes Object with learner emails as keys, each containing an array of variable key names

Response

FieldTypeDescription
{email} object Key-value pairs of variable names and their values for each learner

Returns custom variables only - system variables (first_name, last_name, full_name, email, city, country) are not available. Returns an empty array [] if learner or variable not found.

Request JSON example
{
    "action": "getLearnerVariable",
    "options": {
        "records": {
            "[email protected]": [
                "department",
                "manager_name"
            ]
        }
    }
}
Response JSON example
{
    "[email protected]": {
        "department": "Engineering",
        "manager_name": "Jane Doe"
    },
    "[email protected]": {
        "department": "Marketing"
    }
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=getLearnerVariable" \
  --data-urlencode "options[records][[email protected]][0]=department" \
  --data-urlencode "options[records][[email protected]][1]=manager_name"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'getLearnerVariable',
        'options' => [
            'records' => [
                '[email protected]' => [
                    'department',
                    'manager_name',
                ],
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'getLearnerVariable');
params.append('options[records][[email protected]][0]', 'department');
params.append('options[records][[email protected]][1]', 'manager_name');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "getLearnerVariable"},
    {"options[records][[email protected]][0]", "department"},
    {"options[records][[email protected]][1]", "manager_name"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'getLearnerVariable',
        'options[records][[email protected]][0]': 'department',
        'options[records][[email protected]][1]': 'manager_name',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

getTeams

Retrieve a list of teams in the academy.

Parameters

ParameterTypeRequiredDescription
records string No Pass "course" to include course assignments in the response

Response

Without records parameter

FieldTypeDescription
id string Encoded team ID
name string Team name

With records="course"

FieldTypeDescription
id string Encoded team ID
name string Team name
courses array List of assigned courses
courses[].id string Encoded course ID
courses[].name string Course name
Request JSON example
{
    "action": "getTeams"
}
Response JSON example (without courses)
[
    {
        "id": "Xt7nBqK4mP9wRvLjA3Oe",
        "name": "Sales Team"
    },
    {
        "id": "Yp9kMrN5wQ8xTvBjL4Re",
        "name": "Engineering Team"
    }
]
Response JSON example (with courses)
[
    {
        "id": "Xt7nBqK4mP9wRvLjA3Oe",
        "name": "Sales Team",
        "courses": [
            {
                "id": "R5VD9yZ3bO7QWk240axm",
                "name": "Sales Onboarding"
            },
            {
                "id": "Kp8mNqW4xR3vLbYjD2Ae",
                "name": "Product Knowledge"
            }
        ]
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=getTeams" \
  --data-urlencode "options[records]=course"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'getTeams',
        'options' => [
            'records' => 'course',
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'getTeams');
params.append('options[records]', 'course');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "getTeams"},
    {"options[records]", "course"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'getTeams',
        'options[records]': 'course',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

inviteLearners

Invite learners to the academy.

Parameters

ParameterTypeRequiredDescription
learners[].email string Yes Learner email address
learners[].name string No Full name
learners[].last_name string No Last name
learners[].type string Yes Learner role. Allowed values: "learner" or "manager".
learners[].teams array No Array of team IDs to assign
is_send_email boolean No Send invitation email (default: false)

If a learner with the given email already exists in the academy, they will be added to the specified teams instead of creating an invitation.

Response

FieldTypeDescription
invited integer Number of created invitations
already_existing array<string> List of emails that were not invited because they already exist as learners.

The number of invited users may be fewer than the number sent (if some are already learners in the academy).

Request JSON example
{
    "action": "inviteLearners",
    "options": {
        "learners": [
            {
                "email": "[email protected]",
                "name": "Alex Johnson",
                "type": "learner"
            }
        ]
    }
}
Response JSON example
{
    "invited": 1,
    "already_existing": [
        "[email protected]"
    ]
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=inviteLearners" \
  --data-urlencode "options[learners][0][email][email protected]" \
  --data-urlencode "options[learners][0][name]=Alex Johnson" \
  --data-urlencode "options[learners][0][type]=learner" \
  --data-urlencode "options[is_send_email]=true"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'inviteLearners',
        'options' => [
            'learners' => [
                [
                    'email' => '[email protected]',
                    'name' => 'Alex Johnson',
                    'type' => 'learner',
                ],
            ],
            'is_send_email' => true,
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'inviteLearners');
params.append('options[learners][0][email]', '[email protected]');
params.append('options[learners][0][name]', 'Alex Johnson');
params.append('options[learners][0][type]', 'learner');
params.append('options[is_send_email]', 'true');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "inviteLearners"},
    {"options[learners][0][email]", "[email protected]"},
    {"options[learners][0][name]", "Alex Johnson"},
    {"options[learners][0][type]", "learner"},
    {"options[is_send_email]", "true"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'inviteLearners',
        'options[learners][0][email]': '[email protected]',
        'options[learners][0][name]': 'Alex Johnson',
        'options[learners][0][type]': 'learner',
        'options[is_send_email]': 'true',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

learners

Retrieve a list of learners registered in the academy.

Parameters

ParameterTypeRequiredDescription
email string No Filter by specific learner email
with_academy boolean No Include academy ID and name
with_teams boolean No Include team IDs and names
limit integer No Maximum number of learners to return

Response

FieldTypeDescription
id string Encoded learner ID
name string Full name
first_name string First name
last_name string Last name
email string Learner email
registered_at string Registration date (ISO 8601)
score integer Learner score
lessons_opened integer Number of opened lessons
lessons_completed integer Number of completed lessons
courses_completed integer Number of completed courses
spent_time_in_academy integer Time spent in academy (seconds)
certificates_amount integer Number of issued certificates
company string Learner company
country string Country name
country_iso_code string Country ISO code
city string City name
os string Operating system
device string Device type
browser string Browser name
last_login_at string | null Last login date (ISO 8601) or null
academy object Academy info (if with_academy=true)
academy.id string Encoded academy ID
academy.name string Academy name
teams array Teams list (if with_teams=true)
teams[].id string Encoded team ID
teams[].name string Team name
Request JSON example
{
    "action": "learners"
}
Response JSON example
[
    {
        "id": "eNdBZaWElnYLykXJbyAO",
        "name": "David Chen",
        "first_name": "David",
        "last_name": "Chen",
        "email": "[email protected]",
        "registered_at": "2026-03-15T10:26:23+00:00",
        "score": 85,
        "lessons_opened": 20,
        "lessons_completed": 15,
        "courses_completed": 3,
        "spent_time_in_academy": 45200,
        "certificates_amount": 2,
        "company": "TechCorp",
        "country": "United States",
        "country_iso_code": "US",
        "city": "Los Angeles",
        "os": "OS X",
        "device": "Macintosh",
        "browser": "Chrome",
        "last_login_at": "2026-03-20T14:33:10+00:00",
        "academy": {
            "id": "Rk4mPqW8xN3vLbYjA2Oe",
            "name": "My Academy"
        },
        "teams": [
            {
                "id": "Xt7nBqK4mP9wRvLjA3Oe",
                "name": "Sales Team"
            }
        ]
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learners" \
  --data-urlencode "options[with_academy]=true" \
  --data-urlencode "options[with_teams]=true" \
  --data-urlencode "options[limit]=10"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'learners',
        'options' => [
            'with_academy' => true,
            'with_teams' => true,
            'limit' => 10,
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'learners');
params.append('options[with_academy]', 'true');
params.append('options[with_teams]', 'true');
params.append('options[limit]', '10');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learners"},
    {"options[with_academy]", "true"},
    {"options[with_teams]", "true"},
    {"options[limit]", "10"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'learners',
        'options[with_academy]': 'true',
        'options[with_teams]': 'true',
        'options[limit]': '10',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

learnersAmountFromCountries

Returns a breakdown of learners by country.

Parameters

ParameterTypeRequiredDescription
country_iso_code string No Filter by country ISO code

Response

FieldTypeDescription
country_iso_code string Country ISO code
country string Country name
amount integer Number of learners from this country
Request JSON example
{
    "action": "learnersAmountFromCountries"
}
Response JSON example
[
    {
        "country_iso_code": "US",
        "country": "United States",
        "amount": 45
    },
    {
        "country_iso_code": "GB",
        "country": "United Kingdom",
        "amount": 12
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learnersAmountFromCountries"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'learnersAmountFromCountries',
    ],
]);
const params = new URLSearchParams();
params.append('action', 'learnersAmountFromCountries');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learnersAmountFromCountries"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={'action': 'learnersAmountFromCountries'},
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

learnersChurnAtContent

Returns learners who churned (stopped progressing) at specific content.

Parameters

ParameterTypeRequiredDescription
course_id string Yes Course URL slug or encoded course ID
content_id string Yes Encoded content ID

Response

FieldTypeDescription
churn_date string Date of churn
email string Learner email
name string Full name
spent_time_in_academy integer Total time in academy (seconds)
spent_time_in_content integer Time spent on this content (seconds)
registered_at string Registration date (ISO 8601)
first_name string First name
last_name string | null Last name
Request JSON example
{
    "action": "learnersChurnAtContent",
    "options": {
        "course_id": "R5VD9yZ3bO7QWk240axm",
        "content_id": "Kp8mNqW4xR3vLbYjD2Ae"
    }
}
Response JSON example
[
    {
        "churn_date": "2026-02-10T14:30:00+00:00",
        "email": "[email protected]",
        "name": "Emily Parker",
        "first_name": "Emily",
        "last_name": "Parker",
        "spent_time_in_academy": 3600,
        "spent_time_in_content": 120,
        "registered_at": "2026-03-15T10:26:23+00:00"
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learnersChurnAtContent" \
  --data-urlencode "options[course_id]=R5VD9yZ3bO7QWk240axm" \
  --data-urlencode "options[content_id]=Kp8mNqW4xR3vLbYjD2Ae"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'learnersChurnAtContent',
        'options' => [
            'course_id' => 'R5VD9yZ3bO7QWk240axm',
            'content_id' => 'Kp8mNqW4xR3vLbYjD2Ae',
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'learnersChurnAtContent');
params.append('options[course_id]', 'R5VD9yZ3bO7QWk240axm');
params.append('options[content_id]', 'Kp8mNqW4xR3vLbYjD2Ae');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learnersChurnAtContent"},
    {"options[course_id]", "R5VD9yZ3bO7QWk240axm"},
    {"options[content_id]", "Kp8mNqW4xR3vLbYjD2Ae"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'learnersChurnAtContent',
        'options[course_id]': 'R5VD9yZ3bO7QWk240axm',
        'options[content_id]': 'Kp8mNqW4xR3vLbYjD2Ae',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

learnersCourseComplete

Returns learners who completed a given course.

Parameters

ParameterTypeRequiredDescription
course_id string Yes Course URL slug or encoded Course ID

Response

FieldTypeDescription
date_of_passing string Course completion date
name string Full name
first_name string First name
last_name string | null Last name
email string Learner email
score integer Learner score
courses_completed integer Number of completed courses
spent_time_in_academy integer Time spent (seconds)
certificates_amount integer Number of certificates
company string Company name
Request JSON example
{
    "action": "learnersCourseComplete",
    "options": {
        "course_id": "onboarding-course"
    }
}
Response JSON example
[
    {
        "date_of_passing": "2026-03-01T12:00:00+00:00",
        "name": "Mark Wilson",
        "first_name": "Mark",
        "last_name": "Wilson",
        "email": "[email protected]",
        "score": 85,
        "courses_completed": 3,
        "spent_time_in_academy": 45200,
        "certificates_amount": 2,
        "company": "Globex Corp."
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learnersCourseComplete" \
  --data-urlencode "options[course_id]=onboarding-course"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'learnersCourseComplete',
        'options' => [
            'course_id' => 'onboarding-course',
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'learnersCourseComplete');
params.append('options[course_id]', 'onboarding-course');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learnersCourseComplete"},
    {"options[course_id]", "onboarding-course"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'learnersCourseComplete',
        'options[course_id]': 'onboarding-course',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

learnersFromCountry

Returns a list of learners from the chosen country.

Parameters

ParameterTypeRequiredDescription
country_iso_code string Yes Country ISO code

Response

FieldTypeDescription
country_iso_code string Country ISO code
country string Country name
amount integer Total learners from this country
learners array Array of learner objects
learners[].name string Full name
learners[].email string Learner email
learners[].registered_at string Registration date (ISO 8601)
learners[].score integer Learner score
learners[].lessons_opened integer Number of opened lessons
learners[].lessons_completed integer Number of completed lessons
learners[].courses_completed integer Number of completed courses
learners[].spent_time_in_academy integer Time spent (seconds)
learners[].certificates_amount integer Number of certificates
learners[].company string Company name
learners[].country string Country name
learners[].country_iso_code string Country ISO code
learners[].city string City name
learners[].os string Operating system
learners[].device string Device type
learners[].browser string Browser name
learners[].last_login_at string | null Last login date (ISO 8601)
learners[].id string Encoded learner ID
learners[].first_name string First name
learners[].last_name string Last name
Request JSON example
{
    "action": "learnersFromCountry",
    "options": {
        "country_iso_code": "UA"
    }
}
Response JSON example
{
    "country_iso_code": "UA",
    "country": "Ukraine",
    "amount": 2,
    "learners": [
        {
            "id": "eNdBZaWElnYLykXJbyAO",
            "name": "Lisa Kim",
            "first_name": "Lisa",
            "last_name": "Kim",
            "email": "[email protected]",
            "registered_at": "2026-03-15T10:26:23+00:00",
            "score": 85,
            "lessons_opened": 20,
            "lessons_completed": 15,
            "courses_completed": 3,
            "spent_time_in_academy": 45200,
            "certificates_amount": 2,
            "company": "TechCorp",
            "country": "Ukraine",
            "country_iso_code": "UA",
            "city": "Kyiv",
            "os": "OS X",
            "device": "Macintosh",
            "browser": "Chrome",
            "last_login_at": "2026-03-20T14:33:10+00:00"
        }
    ]
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learnersFromCountry" \
  --data-urlencode "options[country_iso_code]=UA"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'learnersFromCountry',
        'options' => [
            'country_iso_code' => 'UA',
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'learnersFromCountry');
params.append('options[country_iso_code]', 'UA');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learnersFromCountry"},
    {"options[country_iso_code]", "UA"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'learnersFromCountry',
        'options[country_iso_code]': 'UA',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

learnersProgressByCourse

Returns learners' progress in specified courses.

Parameters

ParameterTypeRequiredDescription
course_id string Yes* Course ID (from course settings, "API, Course ID" field)
course_slug string Yes* Course slug (from course settings, "course URL" field)
page integer No Page number in API response
amount integer No Number of learners shown per page
show_not_started_learners boolean No Include learners who did not start the course
only_assigned_learners boolean No Filter to only learners whose team has this course assigned

* Provide either course_id or course_slug. If both provided, course_id takes priority.

Response

FieldTypeDescription
email string Learner email
fullName string Full name
firstName string First name
lastName string Last name
startDate string Date when the learner started the course
startTime string Time when the learner started the course
completionDate string Date when the learner completed the course
completionTime string Time when the learner completed the course
status string Whether the learner completed the course
duration string Estimated time to finish (hh:mm:ss)
viewTime string Time spent in the course (hh:mm:ss)
totalAmount integer Total content items in course
passedAmount integer Content items passed by learner
courseScore integer Learner's score for the course
teams array Teams in which the course is available
totalLearnersAmount integer Total number of learners matching the query
Request JSON example
{
    "action": "learnersProgressByCourse",
    "options": {
        "course_id": "R5VD9yZ3bO7QWk240axm"
    }
}
Response JSON example
{
    "learners": [
        {
            "email": "[email protected]",
            "fullName": "Sarah Miller",
            "firstName": "Sarah",
            "lastName": "Miller",
            "startDate": "01/15/2026",
            "startTime": "7:41 AM",
            "completionDate": "01/15/2026",
            "completionTime": "7:51 AM",
            "status": "Completed",
            "duration": "00:10:50",
            "viewTime": "00:10:55",
            "totalAmount": 5,
            "passedAmount": 5,
            "courseScore": 100,
            "teams": [
                "Sales Team"
            ]
        }
    ],
    "totalLearnersAmount": 1
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learnersProgressByCourse" \
  --data-urlencode "options[course_id]=R5VD9yZ3bO7QWk240axm" \
  --data-urlencode "options[page]=1" \
  --data-urlencode "options[amount]=50" \
  --data-urlencode "options[show_not_started_learners]=true" \
  --data-urlencode "options[only_assigned_learners]=true"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'learnersProgressByCourse',
        'options' => [
            'course_id' => 'R5VD9yZ3bO7QWk240axm',
            'page' => 1,
            'amount' => 50,
            'show_not_started_learners' => true,
            'only_assigned_learners' => true,
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'learnersProgressByCourse');
params.append('options[course_id]', 'R5VD9yZ3bO7QWk240axm');
params.append('options[page]', '1');
params.append('options[amount]', '50');
params.append('options[show_not_started_learners]', 'true');
params.append('options[only_assigned_learners]', 'true');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learnersProgressByCourse"},
    {"options[course_id]", "R5VD9yZ3bO7QWk240axm"},
    {"options[page]", "1"},
    {"options[amount]", "50"},
    {"options[show_not_started_learners]", "true"},
    {"options[only_assigned_learners]", "true"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'learnersProgressByCourse',
        'options[course_id]': 'R5VD9yZ3bO7QWk240axm',
        'options[page]': '1',
        'options[amount]': '50',
        'options[show_not_started_learners]': 'true',
        'options[only_assigned_learners]': 'true',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

learnersRegisteredAt

Returns learners who registered on a certain date or within a date range.

Parameters

ParameterTypeRequiredDescription
date string Yes* Date to search (e.g. "2026-01-25 00:00:00")
operator string No =, >, <, >=, <=, <>
dates array Yes* Array of two dates for interval search

* Provide either date (with optional operator) or dates (array of two dates). If both are provided, dates takes priority.

Response

FieldTypeDescription
id string Encoded learner ID
name string Full name
first_name string First name
last_name string Last name
email string Learner email
registered_at string Registration date (ISO 8601)
score integer Learner score
lessons_opened integer Number of opened lessons
lessons_completed integer Number of completed lessons
courses_completed integer Number of completed courses
spent_time_in_academy integer Time spent (seconds)
certificates_amount integer Number of certificates
company string Company name
country string Country name
country_iso_code string Country ISO code
city string City name
os string Operating system
device string Device type
browser string Browser name
last_login_at string | null Last login date (ISO 8601)
Request JSON example
{
    "action": "learnersRegisteredAt",
    "options": {
        "dates": [
            "2026-01-25 00:00:00",
            "2026-01-25 23:59:59"
        ]
    }
}
Response JSON example
[
    {
        "id": "eNdBZaWElnYLykXJbyAO",
        "name": "Alex Johnson",
        "first_name": "Alex",
        "last_name": "Johnson",
        "email": "[email protected]",
        "registered_at": "2026-01-25T08:30:00+00:00",
        "score": 42,
        "lessons_opened": 10,
        "lessons_completed": 8,
        "courses_completed": 1,
        "spent_time_in_academy": 18000,
        "certificates_amount": 1,
        "company": "Bright Solutions",
        "country": "United States",
        "country_iso_code": "US",
        "city": "New York",
        "os": "Windows 10",
        "device": "PC",
        "browser": "Firefox",
        "last_login_at": "2026-03-10T09:15:00+00:00"
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learnersRegisteredAt" \
  --data-urlencode "options[dates][0]=2026-01-25 00:00:00" \
  --data-urlencode "options[dates][1]=2026-01-25 23:59:59"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'learnersRegisteredAt',
        'options' => [
            'dates' => [
                '2026-01-25 00:00:00',
                '2026-01-25 23:59:59',
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'learnersRegisteredAt');
params.append('options[dates][0]', '2026-01-25 00:00:00');
params.append('options[dates][1]', '2026-01-25 23:59:59');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learnersRegisteredAt"},
    {"options[dates][0]", "2026-01-25 00:00:00"},
    {"options[dates][1]", "2026-01-25 23:59:59"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'learnersRegisteredAt',
        'options[dates][0]': '2026-01-25 00:00:00',
        'options[dates][1]': '2026-01-25 23:59:59',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

learnersRepeatedLogIns

Returns learners with more than one login.

Parameters

ParameterTypeRequiredDescription
dates array No Array of 2 date strings [start, end] to filter by login date range

Response

FieldTypeDescription
id string Encoded learner ID
first_name string First name
last_name string Last name
name string Full name
email string Learner email
registered_at string Registration date (ISO 8601)
score integer Learner score
lessons_opened integer Number of opened lessons
lessons_completed integer Number of completed lessons
courses_completed integer Number of completed courses
spent_time_in_academy integer Time spent (seconds)
certificates_amount integer Number of certificates
company string Company name
country string Country name
country_iso_code string Country ISO code
city string City name
os string Operating system
device string Device type
browser string Browser name
last_login_at string | null Last login date (ISO 8601)
log_ins_amount integer Number of logins
Request JSON example
{
    "action": "learnersRepeatedLogIns"
}
Response JSON example
[
    {
        "id": "eNdBZaWElnYLykXJbyAO",
        "name": "David Chen",
        "first_name": "David",
        "last_name": "Chen",
        "email": "[email protected]",
        "registered_at": "2026-03-15T10:26:23+00:00",
        "score": 85,
        "lessons_opened": 20,
        "lessons_completed": 15,
        "courses_completed": 3,
        "spent_time_in_academy": 45200,
        "certificates_amount": 2,
        "company": "TechCorp",
        "country": "United States",
        "country_iso_code": "US",
        "city": "Los Angeles",
        "os": "OS X",
        "device": "Macintosh",
        "browser": "Chrome",
        "last_login_at": "2026-03-20T14:33:10+00:00",
        "log_ins_amount": 12
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learnersRepeatedLogIns"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'learnersRepeatedLogIns',
    ],
]);
const params = new URLSearchParams();
params.append('action', 'learnersRepeatedLogIns');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learnersRepeatedLogIns"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={'action': 'learnersRepeatedLogIns'},
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

learnersWithScore

Returns a list of learners filtered by their score.

Parameters

ParameterTypeRequiredDescription
score integer Yes Score value to filter by
operator string No =, >, <, >=, <=, <> (default: =)

Response

FieldTypeDescription
id string Encoded learner ID
first_name string First name
last_name string Last name
name string Full name
email string Learner email
registered_at string Registration date (ISO 8601)
score integer Learner score
lessons_opened integer Number of opened lessons
lessons_completed integer Number of completed lessons
courses_completed integer Number of completed courses
spent_time_in_academy integer Time spent (seconds)
certificates_amount integer Number of certificates
company string Company name
country string Country name
country_iso_code string Country ISO code
city string City name
os string Operating system
device string Device type
browser string Browser name
last_login_at string | null Last login date (ISO 8601)
Request JSON example
{
    "action": "learnersWithScore",
    "options": {
        "score": 80
    }
}
Response JSON example
[
    {
        "id": "eNdBZaWElnYLykXJbyAO",
        "name": "Emily Parker",
        "first_name": "Emily",
        "last_name": "Parker",
        "email": "[email protected]",
        "registered_at": "2026-03-15T10:26:23+00:00",
        "score": 85,
        "lessons_opened": 20,
        "lessons_completed": 15,
        "courses_completed": 3,
        "spent_time_in_academy": 45200,
        "certificates_amount": 2,
        "company": "Acme Inc.",
        "country": "United States",
        "country_iso_code": "US",
        "city": "Los Angeles",
        "os": "OS X",
        "device": "Macintosh",
        "browser": "Chrome",
        "last_login_at": "2026-03-20T14:33:10+00:00"
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=learnersWithScore" \
  --data-urlencode "options[score]=80" \
  --data-urlencode "options[operator]=>="
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'learnersWithScore',
        'options' => [
            'score' => 80,
            'operator' => '>=',
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'learnersWithScore');
params.append('options[score]', '80');
params.append('options[operator]', '>=');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "learnersWithScore"},
    {"options[score]", "80"},
    {"options[operator]", ">="}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'learnersWithScore',
        'options[score]': '80',
        'options[operator]': '>=',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

passiveLearners

Returns learners who registered but took no further actions (no lessons opened).

Parameters

Response

FieldTypeDescription
registered_at string Registration date (ISO 8601)
first_name string First name
last_name string | null Last name
email string Learner email
company string Company name
Request JSON example
{
    "action": "passiveLearners"
}
Response JSON example
[
    {
        "registered_at": "2026-02-01T09:00:00+00:00",
        "email": "[email protected]",
        "first_name": "Mark",
        "last_name": "Wilson",
        "company": "Globex Corp."
    }
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=passiveLearners"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'passiveLearners',
    ],
]);
const params = new URLSearchParams();
params.append('action', 'passiveLearners');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "passiveLearners"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={'action': 'passiveLearners'},
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

quizManualApprovePendingLearner

Retrieve a list of quiz submissions that are pending manual approval.

Parameters

Response

FieldTypeDescription
pending array Array of pending submissions
pending[].quiz object Quiz info object
pending[].quiz.id string Encoded quiz ID
pending[].quiz.name string Quiz name
pending[].learner object Learner info object
pending[].learner.email string Learner email
pending[].answer_date string Submission date (ISO 8601)
pending[].action_link string URL to manual approving tab in admin panel
Request JSON example
{
    "action": "quizManualApprovePendingLearner"
}
Response JSON example
{
    "pending": [
        {
            "quiz": {
                "id": "K61OzXl38RorvogrmP0y",
                "name": "Final Assessment"
            },
            "learner": {
                "email": "[email protected]"
            },
            "answer_date": "2026-02-01T15:25:00+00:00",
            "action_link": "https://app.academyocean.com/account/courses/edit/my-course/quiz/final-assessment#manual-approving-tab"
        }
    ]
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=quizManualApprovePendingLearner"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'quizManualApprovePendingLearner',
    ],
]);
const params = new URLSearchParams();
params.append('action', 'quizManualApprovePendingLearner');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "quizManualApprovePendingLearner"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={'action': 'quizManualApprovePendingLearner'},
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

removeLearnersFromTeams

Remove learners from specific teams.

Parameters

ParameterTypeRequiredDescription
learners array Yes Array of learner objects
learners[].email string Yes Learner email address
learners[].teams array Yes Array of encoded team IDs to remove from

Response

FieldTypeDescription
(array) array Returns ["done"] on success
Request JSON example
{
    "action": "removeLearnersFromTeams",
    "options": {
        "learners": [
            {
                "email": "[email protected]",
                "teams": [
                    "Xt7nBqK4mP9wRvLjA3Oe"
                ]
            }
        ]
    }
}
Response JSON example
[
    "done"
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=removeLearnersFromTeams" \
  --data-urlencode "options[learners][0][email][email protected]" \
  --data-urlencode "options[learners][0][teams][0]=Xt7nBqK4mP9wRvLjA3Oe"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'removeLearnersFromTeams',
        'options' => [
            'learners' => [
                [
                    'email' => '[email protected]',
                    'teams' => [
                        'Xt7nBqK4mP9wRvLjA3Oe',
                    ],
                ],
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'removeLearnersFromTeams');
params.append('options[learners][0][email]', '[email protected]');
params.append('options[learners][0][teams][0]', 'Xt7nBqK4mP9wRvLjA3Oe');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "removeLearnersFromTeams"},
    {"options[learners][0][email]", "[email protected]"},
    {"options[learners][0][teams][0]", "Xt7nBqK4mP9wRvLjA3Oe"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'removeLearnersFromTeams',
        'options[learners][0][email]': '[email protected]',
        'options[learners][0][teams][0]': 'Xt7nBqK4mP9wRvLjA3Oe',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

returnLearnersAccess

Restore academy access for previously revoked learners.

Parameters

ParameterTypeRequiredDescription
learners array Yes Array of learner email addresses

Response

FieldTypeDescription
status string Always "done" on success
affected integer Number of learners whose access was restored
not_found array (Optional) Emails not found in academy
Request JSON example
{
    "action": "returnLearnersAccess",
    "options": {
        "learners": [
            "[email protected]"
        ]
    }
}
Response JSON example
{
    "status": "done",
    "affected": 2
}
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=returnLearnersAccess" \
  --data-urlencode "options[learners][0][email protected]"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'returnLearnersAccess',
        'options' => [
            'learners' => [
                '[email protected]',
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'returnLearnersAccess');
params.append('options[learners][0]', '[email protected]');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "returnLearnersAccess"},
    {"options[learners][0]", "[email protected]"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'returnLearnersAccess',
        'options[learners][0]': '[email protected]',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

revokeLearnersAccess

Revoke academy access for one or more learners.

Parameters

ParameterTypeRequiredDescription
learners array Yes Array of learner email addresses

Response

FieldTypeDescription
(array) array Array of revoked learner email addresses
Request JSON example
{
    "action": "revokeLearnersAccess",
    "options": {
        "learners": [
            "[email protected]"
        ]
    }
}
Response JSON example
[
    "[email protected]",
    "[email protected]"
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=revokeLearnersAccess" \
  --data-urlencode "options[learners][0][email protected]"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'revokeLearnersAccess',
        'options' => [
            'learners' => [
                '[email protected]',
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'revokeLearnersAccess');
params.append('options[learners][0]', '[email protected]');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "revokeLearnersAccess"},
    {"options[learners][0]", "[email protected]"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'revokeLearnersAccess',
        'options[learners][0]': '[email protected]',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

setLessonVariables

Set or create learner variables for dynamic content personalization.

Parameters

ParameterTypeRequiredDescription
academy_id string Yes Encoded academy ID
records object Yes Object with learner emails as keys
records.{email}[].key string Yes Variable key name
records.{email}[].value string Yes Variable value
records.{email}[].type integer No Value type: 0=String, 1=Paragraph, 2=Number, 3=HTML, 4=Markdown
records.{email}[].default_value string No Default value if not set
is_invite_learner_if_not_found boolean No Invite learner if not found (default: false)

Variable keys are normalized to snake_case. The default_value is only applied when the variable is first created; subsequent calls will not change the existing default. When type is 2 (Number), value must be a valid numeric string.

Response

FieldTypeDescription
(array) array Returns ["done"] on success
Request JSON example
{
    "action": "setLessonVariables",
    "options": {
        "academy_id": "Rk4mPqW8xN3vLbYjA2Oe",
        "records": {
            "[email protected]": [
                {
                    "key": "department",
                    "value": "Engineering",
                    "type": 0
                }
            ]
        }
    }
}
Response JSON example
[
    "done"
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=setLessonVariables" \
  --data-urlencode "options[academy_id]=Rk4mPqW8xN3vLbYjA2Oe" \
  --data-urlencode "options[records][[email protected]][0][key]=department" \
  --data-urlencode "options[records][[email protected]][0][value]=Engineering" \
  --data-urlencode "options[records][[email protected]][0][type]=0" \
  --data-urlencode "options[is_invite_learner_if_not_found]=true"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'setLessonVariables',
        'options' => [
            'academy_id' => 'Rk4mPqW8xN3vLbYjA2Oe',
            'records' => [
                '[email protected]' => [
                    [
                        'key' => 'department',
                        'value' => 'Engineering',
                        'type' => 0,
                    ],
                ],
            ],
            'is_invite_learner_if_not_found' => true,
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'setLessonVariables');
params.append('options[academy_id]', 'Rk4mPqW8xN3vLbYjA2Oe');
params.append('options[records][[email protected]][0][key]', 'department');
params.append('options[records][[email protected]][0][value]', 'Engineering');
params.append('options[records][[email protected]][0][type]', '0');
params.append('options[is_invite_learner_if_not_found]', 'true');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "setLessonVariables"},
    {"options[academy_id]", "Rk4mPqW8xN3vLbYjA2Oe"},
    {"options[records][[email protected]][0][key]", "department"},
    {"options[records][[email protected]][0][value]", "Engineering"},
    {"options[records][[email protected]][0][type]", "0"},
    {"options[is_invite_learner_if_not_found]", "true"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'setLessonVariables',
        'options[academy_id]': 'Rk4mPqW8xN3vLbYjA2Oe',
        'options[records][[email protected]][0][key]': 'department',
        'options[records][[email protected]][0][value]': 'Engineering',
        'options[records][[email protected]][0][type]': '0',
        'options[is_invite_learner_if_not_found]': 'true',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)
POST

updateCourseCertificateVariables

Update certificate template variables for a specific course.

Parameters

ParameterTypeRequiredDescription
course_id string Yes Encoded course ID
certificate_variables array Yes Array of certificate variable objects
certificate_variables[].code string Yes Variable code identifier
certificate_variables[].name string Yes Human-readable variable name
certificate_variables[].type string Yes Variable type: text or img_url
certificate_variables[].value string | null Yes Variable value (null to clear)

Response

FieldTypeDescription
(array) array Returns ["done"] on success
Request JSON example
{
    "action": "updateCourseCertificateVariables",
    "options": {
        "course_id": "R5VD9yZ3bO7QWk240axm",
        "certificate_variables": [
            {
                "code": "company_name",
                "name": "Company Name",
                "type": "text",
                "value": "New Company Name"
            }
        ]
    }
}
Response JSON example
[
    "done"
]
Sample code
curl -X POST https://app.academyocean.com/api/v1 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --data-urlencode "action=updateCourseCertificateVariables" \
  --data-urlencode "options[course_id]=R5VD9yZ3bO7QWk240axm" \
  --data-urlencode "options[certificate_variables][0][code]=company_name" \
  --data-urlencode "options[certificate_variables][0][name]=Company Name" \
  --data-urlencode "options[certificate_variables][0][type]=text" \
  --data-urlencode "options[certificate_variables][0][value]=New Company Name"
$response = $client->post('https://app.academyocean.com/api/v1', [
    'headers' => [
        'Authorization' => 'Bearer ' . $apiToken,
    ],
    'form_params' => [
        'action' => 'updateCourseCertificateVariables',
        'options' => [
            'course_id' => 'R5VD9yZ3bO7QWk240axm',
            'certificate_variables' => [
                [
                    'code' => 'company_name',
                    'name' => 'Company Name',
                    'type' => 'text',
                    'value' => 'New Company Name',
                ],
            ],
        ],
    ],
]);
const params = new URLSearchParams();
params.append('action', 'updateCourseCertificateVariables');
params.append('options[course_id]', 'R5VD9yZ3bO7QWk240axm');
params.append('options[certificate_variables][0][code]', 'company_name');
params.append('options[certificate_variables][0][name]', 'Company Name');
params.append('options[certificate_variables][0][type]', 'text');
params.append('options[certificate_variables][0][value]', 'New Company Name');

const response = await axios.post(
    'https://app.academyocean.com/api/v1', params,
    { headers: { Authorization: 'Bearer ' + apiToken } });
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    {"action", "updateCourseCertificateVariables"},
    {"options[course_id]", "R5VD9yZ3bO7QWk240axm"},
    {"options[certificate_variables][0][code]", "company_name"},
    {"options[certificate_variables][0][name]", "Company Name"},
    {"options[certificate_variables][0][type]", "text"},
    {"options[certificate_variables][0][value]", "New Company Name"}
});
var response = await client.PostAsync(
    "https://app.academyocean.com/api/v1", content);

var result = await response.Content.ReadAsStringAsync();
response = requests.post(
    'https://app.academyocean.com/api/v1',
    data={
        'action': 'updateCourseCertificateVariables',
        'options[course_id]': 'R5VD9yZ3bO7QWk240axm',
        'options[certificate_variables][0][code]': 'company_name',
        'options[certificate_variables][0][name]': 'Company Name',
        'options[certificate_variables][0][type]': 'text',
        'options[certificate_variables][0][value]': 'New Company Name',
    },
    headers={'Authorization': f'Bearer {api_token}'},
)

Webhooks

Catches the events on your AcademyOcean account so your integration can automatically trigger reactions. Available by request as an add-on to a Professional or Enterprise plan.

To maintain data integrity in webhook requests, a secret key and signature header are employed. Each webhook request includes a Signature field in its header. This signature is created using the HMAC (Hash-based Message Authentication Code) algorithm in combination with the SHA-256 hashing function.

Here's how the signing process works:

  • Secret Key: A unique secret key is generated and associated with your webhook. This key is known only to your system and can be accessed in the webhook settings.
  • Payload: When a webhook event occurs, the payload (the data being sent) is used to generate the signature.
  • Signature Generation: The HMAC algorithm takes the payload and the secret key as inputs. It then applies the SHA-256 hashing function to produce a hash value. This hash value is the signature.
  • Signature Header: The resulting signature is included in the request header as the Signature field.
  • Verification: On the receiving end, the signature can be verified by recalculating it using the same secret key and payload. If the recalculated signature matches the one sent in the header, the request is confirmed to be authentic and untampered.

This process ensures that the data received via the webhook has not been altered and that it originates from a trusted source.

WEBHOOK

academy.learner.sign_up

Triggered when new learner registers.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000001",
    "event": "academy.learner.sign_up",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

course_group.started

Triggered when learner starts group of courses.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000002",
    "event": "course_group.started",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "group": {
        "id": "GroupExample00000001",
        "name": "Group",
        "is_in_folder": true,
        "folder_name": "Folder",
        "folder_id": "FolderExample0000001",
        "folder_category": "Category (recommended)"
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

course_group.finished

Triggered when learner finishes group of courses.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000003",
    "event": "course_group.finished",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "group": {
        "id": "GroupExample00000001",
        "name": "Group",
        "is_in_folder": true,
        "folder_name": "Folder",
        "folder_id": "FolderExample0000001",
        "folder_category": "Category (recommended)"
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

course.deadline_reminder.three_days

Triggered 3 days before deadline.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000004",
    "event": "course.deadline_reminder.three_days",
    "first_name": "John",
    "last_name": "Smith",
    "learner_email": "[email protected]",
    "course_name": "Onboarding Course",
    "event_date": "05/22/2026 14:33:29"
}
WEBHOOK

course.deadline_reminder.seven_days

Triggered 7 days before deadline.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000005",
    "event": "course.deadline_reminder.seven_days",
    "first_name": "John",
    "last_name": "Smith",
    "learner_email": "[email protected]",
    "course_name": "Onboarding Course",
    "event_date": "05/22/2026 14:33:29"
}
WEBHOOK

course.started

Triggered when learner starts the course.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000006",
    "event": "course.started",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "course": {
        "id": "CourseExample0000001",
        "name": "Course",
        "is_in_folder": true,
        "folder_name": "Folder",
        "folder_id": "FolderExample0000001",
        "folder_category": "Category (recommended)"
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

course.finished

Triggered when learner finishes the course.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000007",
    "event": "course.finished",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy Name"
    },
    "course": {
        "id": "CourseExample0000001",
        "name": "Course Name",
        "is_in_folder": false,
        "folder_name": "",
        "folder_id": "",
        "folder_category": ""
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

learner.course_certificate.issued

Triggered when learner receives a certificate.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000008",
    "event": "learner.course_certificate.issued",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy Name"
    },
    "course": {
        "id": "CourseExample0000001",
        "name": "Course Name",
        "is_in_folder": false,
        "folder_name": "",
        "folder_id": "",
        "folder_category": ""
    },
    "certificate": {
        "link_to_pdf": "https://public-files.academyocean.com/public/certificates/a1b2c3d4e5f6g7h8.pdf",
        "link_to_img": "https://public-files.academyocean.com/public/certificates/a1b2c3d4e5f6g7h8.png",
        "verify_link": "https://app.academyocean.com/verify/a1b2c3d4e5f6g7h8",
        "issue_date": "2026-05-22T11:45:32+00:00",
        "number": "2026/abc/42"
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

learner.course.available

Triggered when learner gets access to new course.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000009",
    "event": "learner.course.available",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "teams": [
        {
            "id": "TeamExample000000001",
            "name": "Team"
        }
    ],
    "learners": [
        {
            "email": "[email protected]",
            "full_name": "John Smith",
            "first_name": "John",
            "last_name": "Smith"
        }
    ],
    "courses": [
        {
            "id": "CourseExample0000001",
            "name": "Course",
            "is_in_folder": false,
            "folder_name": "",
            "folder_id": "",
            "folder_category": ""
        }
    ],
    "course_groups": []
}
WEBHOOK

learner.end_quiz_attempts

Triggered when learner uses all quiz attempts.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000010",
    "event": "learner.end_quiz_attempts",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "quiz": {
        "id": "QuizExample000000001",
        "name": "Quiz"
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

lesson.started

Triggered when learner starts lesson.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000011",
    "event": "lesson.started",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "course": {
        "id": "CourseExample0000001",
        "name": "Course",
        "is_in_folder": false,
        "folder_name": "",
        "folder_id": "",
        "folder_category": ""
    },
    "lesson": {
        "id": "LessonExample0000001",
        "name": "Test",
        "start_date": "2026-05-22T14:12:01+00:00"
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

lesson.finished

Triggered when learner finishes lesson.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000012",
    "event": "lesson.finished",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "course": {
        "id": "CourseExample0000001",
        "name": "Course",
        "is_in_folder": false,
        "folder_name": "",
        "folder_id": "",
        "folder_category": ""
    },
    "lesson": {
        "id": "LessonExample0000001",
        "name": "Test",
        "start_date": "2026-05-22T14:12:01+00:00",
        "finish_date": "2026-05-22T14:12:43+00:00"
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

quiz.started

Triggered when learner starts quiz.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000013",
    "event": "quiz.started",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "course": {
        "id": "CourseExample0000001",
        "name": "Course",
        "is_in_folder": false,
        "folder_name": "",
        "folder_id": "",
        "folder_category": ""
    },
    "quiz": {
        "id": "QuizExample000000001",
        "name": "Test",
        "start_date": "2026-05-22T14:12:52+00:00"
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

quiz.finished

Triggered when learner finishes quiz.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000014",
    "event": "quiz.finished",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy"
    },
    "course": {
        "id": "CourseExample0000001",
        "name": "Course",
        "is_in_folder": false,
        "folder_name": "",
        "folder_id": "",
        "folder_category": ""
    },
    "quiz": {
        "id": "QuizExample000000001",
        "name": "Test",
        "start_date": "2026-05-22T14:12:52+00:00",
        "finish_date": "2026-05-22T14:12:58+00:00",
        "spent_time_sec": 46,
        "score": 5,
        "passing_score": 0,
        "is_need_manual_approve": false,
        "is_timer_on": false,
        "timer_minutes": 5,
        "is_timer_expired": false,
        "is_passed": true
    },
    "learner": {
        "email": "[email protected]",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith"
    }
}
WEBHOOK

team.new_learners

Triggered when learner added to team or enters No Team.

Payload example
{
    "id": "00000000-0000-4000-8000-000000000015",
    "event": "team.new_learners",
    "academy": {
        "id": "AcademyExample000001",
        "name": "Academy Name"
    },
    "team": [
        {
            "id": "TeamExample000000001",
            "name": "Team Name"
        }
    ],
    "learners": [
        {
            "email": "[email protected]",
            "full_name": "John Smith",
            "first_name": "John",
            "last_name": "Smith"
        }
    ],
    "courses": [
        {
            "id": "CourseExample0000001",
            "name": "Course Name"
        }
    ],
    "course_groups": [
        {
            "id": "GroupExample00000001",
            "name": "Course Group Name"
        }
    ]
}
Link copied