Overview
Publish HowToo Academy courses inside your own website, portal, or learning environment via a secure, token-based URL. Your platform retrieves a course list, requests an embed URL for a specific learner and course, then displays the course in an iframe, modal, new tab, or full page — whichever fits your UX.
Flow
- List published courses with
POST /v1/learners/bulk/progress - When a learner selects a course, call
POST /v1/embed/courses/{courseId}with the learner's email. - Drop the returned url into an iframe (or new tab, modal, full page).
- The URL handles authentication, access checks, and rendering on its own. Your platform doesn't construct redirect URLs or handle tokens directly.
Authentication
All API calls require an x-api-key header containing your organisation's API key. The key must remain server-side — never expose it to the browser. Your frontend should call a route on your own backend, which forwards the request to HowToo with the key attached.
Prerequisite: Register Your Origins
Before you can generate embed URLs, the origins of any pages that will embed HowToo courses must be registered with HowToo. Provide the full origins of every site that needs to host the iframe — for example https://app.customer.com and https://staging.customer.com. HowToo configures these on your organisation; the embedded course page will only render inside iframes hosted on these origins.
If no origins are configured, calls to POST /v1/embed/courses/{courseId} return 403.
API: List Courses
POST https://api.howtoo.co/v1/learners/bulk/progress
Headers
x-api-key: <your-api-key> Accept: application/json
Body
{ "learnerIds": [1] }
Response
{
"data": [
{
"learnerId": 1,
"fullname": "Learner Name",
"email": "learner@email.com",
"courses": [
{
"courseId": 100, "courseName": "Course name", "score": 0,
"progress": 50, "status": "in progress",
"startDate": "2025-05-30T02:28:08.000Z",
"completedDate": null,
"recentLaunchedDate": "2025-05-30T02:28:08.000Z",
...
}
]
}
]
}API: Get Embed URL
POST https://api.howtoo.co/v1/embed/courses/{courseId}
Headers
x-api-key: <your-api-key>
Content-Type: application/json
Body
{ "email": "learner@example.com" }
Response
{
"message": "Embed URL generated successfully",
"data": {
"url": "https://<site>/embedded/course/<courseId>?token=<token>"
}
}
The returned URL is valid for 15 minutes and must be treated as sensitive — do not log it or store it in shared analytics.
Displaying the Course
The returned URL can be presented however suits your platform:
- Iframe — set as the src of an <iframe>.
- Modal — render an <iframe> inside your modal.
- New tab — window.open(url, '_blank', 'noopener,noreferrer').
- Full page — navigate the browser to the URL.
Lessons launched from within the course always open in a new browser window, regardless of how the course itself is displayed.
Access Requirements
The embed URL is only generated and viewable when all of the following are true: • A valid API key with access to the organisation
- Allowed embed origins are registered for the organisation
- The course exists, belongs to the organisation, and is published
- The learner exists in the organisation
- The learner is enrolled in the course
If any check fails, the API responds with an error. If the URL is opened after these conditions change, the embedded page shows an error message to the learner.
Errors
| Code | Description |
| 400 | Invalid page / pageSize parameters |
| 401 | Missing/invalid API key, or expired/invalid embed token at view time |
| 403 | Organisation has no Learning Academy, learner is not enrolled in the course, or allowed embed origins are not configured for the organisation |
| 404 | Learner not found in organisation, course not found, or organisation's primary site not found |
Error responses are JSON with a message field describing the cause.
Example
Backend (Node.js / Express)
// Customer's backend — holds the API key.
app.post('/my-api/howtoo-embed/:courseId', requireUserSession, async (req, res) =>
{
const { courseId } = req.params; const { email } = req.user;
const r = await fetch(`https://<site>/v1/embed/courses/${courseId}`, {
method: 'POST',
headers: {
'x-api-key': process.env.HOWTOO_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
if (!r.ok) return res.status(r.status).json(await r.json());
res.json(await r.json());
});
Frontend (JavaScript)
// Customer's frontend.
async function openCourse(courseId, displayMode = 'iframe') {
const res = await fetch(`/my-api/howtoo-embed/${courseId}`, { method: 'POST' }); const { data } = await res.json();
if (displayMode === 'iframe') {
document.getElementById('courseFrame').src = data.url;
} else if (displayMode === 'new-tab') {
window.open(data.url, '_blank', 'noopener,noreferrer');
} else if (displayMode === 'modal') {
document.getElementById('courseModalFrame').src = data.url;
document.getElementById('courseModal').style.display = 'block';
}
}
Comments
0 comments
Please sign in to leave a comment.