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:
You must obtain an access key. To do so, go to the Settings section of your Academy and click API tab
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."
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.
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..."
}
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
cURL
PHP
Node.js
C#
Python
Copy
# 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"
Copy
$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);
Copy
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);
Copy
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();
Copy
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
Parameters
Response
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
curl -X POST https://app.academyocean.com/api/v1 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--data-urlencode "action=academies"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'academies',
],
]);
Copy
const params = new URLSearchParams();
params.append('action', 'academies');
const response = await axios.post(
'https://app.academyocean.com/api/v1', params,
{ headers: { Authorization: 'Bearer ' + apiToken } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={'action': 'academies'},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Response
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
curl -X POST https://app.academyocean.com/api/v1 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--data-urlencode "action=acceptedLearnerInviteRequest"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'acceptedLearnerInviteRequest',
],
]);
Copy
const params = new URLSearchParams();
params.append('action', 'acceptedLearnerInviteRequest');
const response = await axios.post(
'https://app.academyocean.com/api/v1', params,
{ headers: { Authorization: 'Bearer ' + apiToken } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={'action': 'acceptedLearnerInviteRequest'},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
quiz_id
string
Yes
Encoded quiz ID
email
string
Yes
Learner email address
Response
Field Type Description
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
Copy
{
"action": "addLearnerQuizAttempts",
"options": {
"quiz_id": "D6gJpqlk0RjjE4Aw9ymN",
"email": "[email protected] "
}
}
Response JSON example
{
"status": "done"
}
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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] "
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'addLearnerQuizAttempts',
'options' => [
'quiz_id' => 'D6gJpqlk0RjjE4Aw9ymN',
'email' => '[email protected] ',
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
(array)
array
Returns ["done"] on success
Request JSON example
Response JSON example
[
"done"
]
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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] "
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'addLearnersToTeams',
'options' => [
'teams' => [
'Xt7nBqK4mP9wRvLjA3Oe',
],
'learners' => [
'[email protected] ',
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
email
string
Yes
Learner email address
Response
Field Type Description
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
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
PHP
Node.js
C#
Python
Copy
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] "
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'certificates',
'options' => [
'email' => '[email protected] ',
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'certificates',
'options[email]': '[email protected] ',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
emails
array
Yes
Array of learner email addresses to check
Response
Field Type Description
{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
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
PHP
Node.js
C#
Python
Copy
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] "
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'checkLearnerInvite',
'options' => [
'emails' => [
'[email protected] ',
'[email protected] ',
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
course_id
string
Yes
Encoded course ID
Response
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
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"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'courseCertificateVariables',
'options' => [
'course_id' => 'R5VD9yZ3bO7QWk240axm',
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'courseCertificateVariables',
'options[course_id]': 'R5VD9yZ3bO7QWk240axm',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
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
Field Type Description
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)
Field Type Description
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
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
PHP
Node.js
C#
Python
Copy
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"
Copy
$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,
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Response
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
curl -X POST https://app.academyocean.com/api/v1 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--data-urlencode "action=courses"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'courses',
],
]);
Copy
const params = new URLSearchParams();
params.append('action', 'courses');
const response = await axios.post(
'https://app.academyocean.com/api/v1', params,
{ headers: { Authorization: 'Bearer ' + apiToken } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={'action': 'courses'},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
link
string
One-time seamless login URL
Request JSON example
Response JSON example
{
"link": "https://myacademy.academyocean.com/auth/token/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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"
Copy
$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',
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
emails
array
Yes
Array of learner email addresses
is_delete_users
boolean
No
Also delete learner accounts (default: false)
Response
Field Type Description
(array)
array
Returns ["done"] on success
Request JSON example
Response JSON example
[
"done"
]
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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] "
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'deleteLearnerInvite',
'options' => [
'emails' => [
'[email protected] ',
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'deleteLearnerInvite',
'options[emails][0]': '[email protected] ',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
deleted
integer
Number of learners deleted
Request JSON example
Response JSON example
{
"deleted": 2
}
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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] "
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'deleteLearners',
'options' => [
'emails' => [
'[email protected] ',
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'deleteLearners',
'options[emails][0]': '[email protected] ',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
records
object
Yes
Object with learner emails as keys, each containing an array of encoded quiz IDs
Response
Quiz attempt fields
Field Type Description
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
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
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"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'getLearnersQuizStatistic',
'options' => [
'records' => [
'[email protected] ' => [
'D6gJpqlk0RjjE4Aw9ymN',
],
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'getLearnersQuizStatistic',
'options[records][[email protected] ][0]': 'D6gJpqlk0RjjE4Aw9ymN',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
records
object
Yes
Object with learner emails as keys, each containing an array of variable key names
Response
Field Type Description
{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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
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"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'getLearnerVariable',
'options' => [
'records' => [
'[email protected] ' => [
'department',
'manager_name',
],
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
records
string
No
Pass "course" to include course assignments in the response
Response
Without records parameter
Field Type Description
id
string
Encoded team ID
name
string
Team name
With records="course"
Field Type Description
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
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
PHP
Node.js
C#
Python
Copy
curl -X POST https://app.academyocean.com/api/v1 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--data-urlencode "action=getTeams" \
--data-urlencode "options[records]=course"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'getTeams',
'options' => [
'records' => 'course',
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'getTeams',
'options[records]': 'course',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
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
Response JSON example
{
"invited": 1,
"already_existing": [
"[email protected] "
]
}
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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"
Copy
$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,
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
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
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
PHP
Node.js
C#
Python
Copy
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"
Copy
$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,
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
country_iso_code
string
No
Filter by country ISO code
Response
Field Type Description
country_iso_code
string
Country ISO code
country
string
Country name
amount
integer
Number of learners from this country
Request JSON example
Response JSON example
[
{
"country_iso_code": "US",
"country": "United States",
"amount": 45
},
{
"country_iso_code": "GB",
"country": "United Kingdom",
"amount": 12
}
]
Sample code
cURL
PHP
Node.js
C#
Python
Copy
curl -X POST https://app.academyocean.com/api/v1 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--data-urlencode "action=learnersAmountFromCountries"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'learnersAmountFromCountries',
],
]);
Copy
const params = new URLSearchParams();
params.append('action', 'learnersAmountFromCountries');
const response = await axios.post(
'https://app.academyocean.com/api/v1', params,
{ headers: { Authorization: 'Bearer ' + apiToken } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={'action': 'learnersAmountFromCountries'},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
course_id
string
Yes
Course URL slug or encoded course ID
content_id
string
Yes
Encoded content ID
Response
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
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"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'learnersChurnAtContent',
'options' => [
'course_id' => 'R5VD9yZ3bO7QWk240axm',
'content_id' => 'Kp8mNqW4xR3vLbYjD2Ae',
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
course_id
string
Yes
Course URL slug or encoded Course ID
Response
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
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"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'learnersCourseComplete',
'options' => [
'course_id' => 'onboarding-course',
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'learnersCourseComplete',
'options[course_id]': 'onboarding-course',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
country_iso_code
string
Yes
Country ISO code
Response
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
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"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'learnersFromCountry',
'options' => [
'country_iso_code' => 'UA',
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'learnersFromCountry',
'options[country_iso_code]': 'UA',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
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
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
PHP
Node.js
C#
Python
Copy
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"
Copy
$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,
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
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"
Copy
$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',
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
dates
array
No
Array of 2 date strings [start, end] to filter by login date range
Response
Field Type Description
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
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
PHP
Node.js
C#
Python
Copy
curl -X POST https://app.academyocean.com/api/v1 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--data-urlencode "action=learnersRepeatedLogIns"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'learnersRepeatedLogIns',
],
]);
Copy
const params = new URLSearchParams();
params.append('action', 'learnersRepeatedLogIns');
const response = await axios.post(
'https://app.academyocean.com/api/v1', params,
{ headers: { Authorization: 'Bearer ' + apiToken } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={'action': 'learnersRepeatedLogIns'},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
score
integer
Yes
Score value to filter by
operator
string
No
=, >, <, >=, <=, <> (default: =)
Response
Field Type Description
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
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
PHP
Node.js
C#
Python
Copy
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]=>="
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'learnersWithScore',
'options' => [
'score' => 80,
'operator' => '>=',
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'learnersWithScore',
'options[score]': '80',
'options[operator]': '>=',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Response
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
curl -X POST https://app.academyocean.com/api/v1 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--data-urlencode "action=passiveLearners"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'passiveLearners',
],
]);
Copy
const params = new URLSearchParams();
params.append('action', 'passiveLearners');
const response = await axios.post(
'https://app.academyocean.com/api/v1', params,
{ headers: { Authorization: 'Bearer ' + apiToken } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={'action': 'passiveLearners'},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Response
Field Type Description
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
Copy
{
"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
PHP
Node.js
C#
Python
Copy
curl -X POST https://app.academyocean.com/api/v1 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--data-urlencode "action=quizManualApprovePendingLearner"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'quizManualApprovePendingLearner',
],
]);
Copy
const params = new URLSearchParams();
params.append('action', 'quizManualApprovePendingLearner');
const response = await axios.post(
'https://app.academyocean.com/api/v1', params,
{ headers: { Authorization: 'Bearer ' + apiToken } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={'action': 'quizManualApprovePendingLearner'},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
(array)
array
Returns ["done"] on success
Request JSON example
Copy
{
"action": "removeLearnersFromTeams",
"options": {
"learners": [
{
"email": "[email protected] ",
"teams": [
"Xt7nBqK4mP9wRvLjA3Oe"
]
}
]
}
}
Response JSON example
[
"done"
]
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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"
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'removeLearnersFromTeams',
'options' => [
'learners' => [
[
'email' => '[email protected] ',
'teams' => [
'Xt7nBqK4mP9wRvLjA3Oe',
],
],
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
learners
array
Yes
Array of learner email addresses
Response
Field Type Description
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
Copy
{
"action": "returnLearnersAccess",
"options": {
"learners": [
"[email protected] "
]
}
}
Response JSON example
{
"status": "done",
"affected": 2
}
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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] "
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'returnLearnersAccess',
'options' => [
'learners' => [
'[email protected] ',
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'returnLearnersAccess',
'options[learners][0]': '[email protected] ',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
learners
array
Yes
Array of learner email addresses
Response
Field Type Description
(array)
array
Array of revoked learner email addresses
Request JSON example
Copy
{
"action": "revokeLearnersAccess",
"options": {
"learners": [
"[email protected] "
]
}
}
Response JSON example
[
"[email protected] ",
"[email protected] "
]
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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] "
Copy
$response = $client->post('https://app.academyocean.com/api/v1', [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
],
'form_params' => [
'action' => 'revokeLearnersAccess',
'options' => [
'learners' => [
'[email protected] ',
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
response = requests.post(
'https://app.academyocean.com/api/v1',
data={
'action': 'revokeLearnersAccess',
'options[learners][0]': '[email protected] ',
},
headers={'Authorization': f'Bearer {api_token}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
(array)
array
Returns ["done"] on success
Request JSON example
Response JSON example
[
"done"
]
Sample code
cURL
PHP
Node.js
C#
Python
Copy
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"
Copy
$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,
],
],
]);
Copy
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 } });
Copy
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();
Copy
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}'},
)
Parameters
Parameter Type Required Description
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
Field Type Description
(array)
array
Returns ["done"] on success
Request JSON example
Copy
{
"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
PHP
Node.js
C#
Python
Copy
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"
Copy
$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',
],
],
],
],
]);
Copy
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 } });
Copy
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();
Copy
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.
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"
}
}
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"
}
}
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"
}
}
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"
}
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"
}
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"
}
}
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"
}
}
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"
}
}
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": []
}
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"
}
}
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"
}
}
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"
}
}
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"
}
}
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"
}
}
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"
}
]
}