Release Notes
Video Vault 1.0
Released October 24, 2026
Video Vault is the new home of the CMN video library API. It replaces the AchieveLab API with the same catalog of channels, speakers and videos and the same set of read-only endpoints, now kept in sync directly with Vimeo.
At a glance
- Same data, same endpoints. The same 12 read-only endpoints return channels, speakers and videos, with the same relationships between them.
- New address. The API is now at
https://videovault.cmn.men/api. - New access tokens. AchieveLab tokens do not work with Video Vault. To get one, email admin@cmn.men.
- A few response differences. Most integrations will work with small changes. Review Changes that may affect your integration before switching over.
- Rate limit. 100 requests per minute per consumer.
Moving from AchieveLab
- Email admin@cmn.men to request your access token.
- Change your base URL to
https://videovault.cmn.men/api. - Send the token as a bearer token, and ask for JSON:
Authorization: Bearer YOUR_TOKEN Accept: application/json - Update your integration for the differences listed below, in particular
GET /videosis now paginated. - Test against the new API, then switch over.
Authentication
Every endpoint requires a bearer token. A request with a missing or invalid token returns 401:
{ "message": "Unauthenticated." }
Keep your token secret and store it server-side, not in browser or mobile app code. If you think a token has been exposed, tell admin@cmn.men right away so it can be replaced. Consumers can also ask for a second, reserve token, so a leaked token can be revoked and replaced without any downtime.
Endpoints
All endpoints use GET and return JSON with the results under a data key.
| Endpoint | Returns |
|---|---|
/channels |
All channels |
/channels/{id} |
One channel |
/channels/{id}/videos |
Videos in a channel |
/channels/{id}/speakers |
Speakers who appear in a channel's videos |
/videos |
All videos, paginated |
/videos/{id} |
One video, with its speakers and channels |
/videos/{id}/channels |
Channels a video belongs to |
/videos/{id}/speakers |
Speakers in a video |
/speakers |
All speakers |
/speakers/{id} |
One speaker |
/speakers/{id}/videos |
Videos a speaker appears in |
/speakers/{id}/channels |
Channels a speaker appears in |
An unknown ID returns 404:
{ "message": "Not found." }
Pagination
GET /videos returns 100 videos per page. Choose a page with ?page=2. AchieveLab returned every video in a single response, so integrations that read only the first response will miss videos.
The response includes links and meta alongside data:
{
"data": [ ... ],
"links": {
"first": "https://videovault.cmn.men/api/videos?page=1",
"last": "https://videovault.cmn.men/api/videos?page=6",
"prev": null,
"next": "https://videovault.cmn.men/api/videos?page=2"
},
"meta": { "current_page": 1, "last_page": 6, "per_page": 100, "total": 573 }
}
Keep requesting links.next until it is null. A page number past the end returns an empty data list. All other list endpoints return their full results in one response.
Rate limits
Each consumer can make 100 requests per minute (AchieveLab allowed 60). Every response includes your limit and what remains:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
Going over the limit returns 429 Too Many Requests with a Retry-After header giving the number of seconds to wait.
Changes that may affect your integration
| Area | AchieveLab | Video Vault |
|---|---|---|
Video id |
AchieveLab's video ID | A new ID. The former AchieveLab ID is available as legacy_id. |
Channel and speaker id |
Unchanged | Unchanged |
Video vimeo_id |
String, e.g. "240877013" |
Integer, e.g. 240877013 |
| Date format | 2017-11-08 07:49:22 |
ISO 8601 in UTC, e.g. 2017-11-08T07:49:22.000000Z |
Video created_at / updated_at |
When the record was added or changed in AchieveLab | When the video was created or last modified on Vimeo |
Video title, length, image |
AchieveLab's stored values | Taken from Vimeo, so wording and durations may differ slightly. image is a Vimeo-hosted thumbnail about 295 pixels wide. |
pivot on nested speakers and channels |
Included | Removed. It only repeated IDs already present. |
GET /videos |
All videos in one response | Paginated, 100 per page |
| Rate limit | 60 per minute | 100 per minute |
| Videos no longer on Vimeo | Listed | No longer listed |
Matching your stored videos. If you saved AchieveLab video IDs, match them using legacy_id, or better, vimeo_id, which is stable. A small number of duplicate entries in the old catalog were consolidated into one video, so legacy_id can be null for those, and for any video added after launch.
New in Video Vault
These additions do not change existing fields.
Videos now also include:
| Field | Description |
|---|---|
legacy_id |
The video's former AchieveLab ID, or null |
description |
The description from Vimeo, or null |
thumbnails |
Thumbnail images in several sizes, each with link, width and height |
vimeo_link |
The video's page on Vimeo |
player_url |
The Vimeo player URL for embedding |
privacy |
The video's Vimeo view-privacy setting |
Channels now also include image_url, the full address of the channel's image, or null if it has none. The existing image field still holds the image's file name, unchanged, so use image_url to display the image.
Speakers now include full_name, the first, middle and last names combined.
Kept in sync with Vimeo. Videos are managed in Vimeo, and the catalog is refreshed from it once a day. A change made in Vimeo, such as a new title, thumbnail or video, can take up to a day to appear in the API. The refresh schedule can be adjusted, so if you need updates to appear sooner, email admin@cmn.men.
Field reference
Video
| Field | Type |
|---|---|
id |
integer |
legacy_id |
integer or null |
vimeo_id |
integer |
title |
string |
description |
string or null |
length |
integer, seconds |
image |
string or null, URL |
thumbnails |
list of { link, width, height, link_with_play_button } |
vimeo_link |
string, URL |
player_url |
string, URL |
privacy |
string |
created_at, updated_at |
string, ISO 8601 UTC |
speakers, channels |
lists (included by /videos and /videos/{id}) |
Channel: id, image (file name or null), image_url (full URL or null), name, slug, description, sort_order (integer or null), created_at, updated_at.
Speaker: id, first_name, middle_name (or null), last_name, full_name, created_at, updated_at.
Example
GET /api/videos/{id}, abridged:
{
"data": {
"id": 130,
"legacy_id": 58,
"vimeo_id": 240312902,
"title": "Example Video",
"description": null,
"length": 1888,
"image": "https://i.vimeocdn.com/video/...",
"vimeo_link": "https://vimeo.com/240312902",
"player_url": "https://player.vimeo.com/video/240312902",
"privacy": "anybody",
"created_at": "2017-10-28T20:30:28.000000Z",
"updated_at": "2026-10-07T18:35:02.000000Z",
"speakers": [ { "id": 1, "full_name": "Edwin Louis Cole", "...": "..." } ],
"channels": [ { "id": 6, "name": "Dad Academy", "slug": "dad-academy", "...": "..." } ]
}
}
Need help?
Email admin@cmn.men to request access, report a problem, or ask about moving your integration over.