Training sessions and recordings
Page the published recording catalogue for CRM2 tables
/a/Training_videosTable
Page the published recording catalogue for CRM2 tables
When to use this endpoint
Use the recording catalogue with the shared CRM2 table widget’s start/length controls. This is a focused wrapper over Training_list(on_demand), not a generic mapped getDataTable endpoint; it does not accept arbitrary selected fields or general table filters.
Authentication and permissions
Authenticate with signed API-key fields or a logged-in CRM session. Every Training handler requires login; there is no additional page, role, department, or tenant feature-flag check. CRM2 exposes Training to authenticated users.
Webinars and session IDs belong to a shared global catalogue. Interests and provider registrations are scoped to the authenticated tenant and user even though they are stored globally. Client-supplied tenant/user IDs do not select another person’s bookings or progress.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
start | Integer | No | Default zero, integer-converted and clamped nonnegative. Converted to page=floor(start/effectiveLength), so an unaligned offset rounds down to its page boundary rather than returning exactly that offset. |
length | Integer | No | Default 10, integer-converted and clamped to 1–200. Zero/negative/nonnumeric values become 1; -1 does not mean export all. |
category | String | No | Same trimmed exact category IDs as Training_list; blank means all. No title/searchValue/general advanced-filter implementation exists here. |
sort | String | No | Exact newest (default), az or za. Ignored/overridden when orderBy is exactly title. |
orderBy / orderDesc | String | No | Only orderBy=title changes sort: exact orderDesc=desc selects za, everything else selects az. Other orderBy values do not select arbitrary database fields. |
view / page / page_length | Mixed | Ignored | Wrapper forces on_demand and derives page/page_length from start/length. Do not use these to bypass table pagination. |
Example request
$result = FM_api('Training_videosTable', [
'start' => '0',
'length' => '10',
'category' => 'jobs',
'orderBy' => 'title',
'orderDesc' => 'asc',
]);
Example response
{
"data": [
{
"id": 1042,
"webinar_id": 91,
"title": "Jobs from start to finish",
"description": "Create and schedule jobs with Fieldmotion.",
"category": "jobs",
"category_name": "Jobs",
"image_url": "https://example.test/training.webp",
"image_alt": "Fieldmotion Training",
"presenter": "Fieldmotion trainer",
"starts_at": 1792569600,
"ends_at": 1792573200,
"timezone": "Europe/Dublin",
"status": "completed",
"capacity": null,
"registration_closes_at": 0,
"registration_open": false,
"registration_url": "",
"interested": false,
"registration_opened_at": 0,
"join_available": false,
"join_url": "",
"registration_state": "none",
"provider_mapped": false,
"provider_available": true,
"recording_provider_mapped": true,
"recording_registration_state": "confirmed",
"recording_duration": 3600,
"watched_seconds": 3240,
"recording_available": true,
"recording_url": "",
"recording_published_at": 1792659600
}
],
"categories": [
{
"id": "general",
"name": "General"
},
{
"id": "getting_started",
"name": "Getting Started"
},
{
"id": "jobs",
"name": "Jobs"
},
{
"id": "assets_stock",
"name": "Assets & Stock"
},
{
"id": "advanced",
"name": "Advanced"
},
{
"id": "scheduling",
"name": "Scheduling"
},
{
"id": "assets",
"name": "Assets"
},
{
"id": "stock",
"name": "Stock"
},
{
"id": "finance",
"name": "Finance"
},
{
"id": "forms",
"name": "Forms"
},
{
"id": "reports",
"name": "Reports"
}
],
"total": 1,
"stale": false,
"unavailable": false,
"recordsFiltered": 1,
"recordsTotal": 1
}
Each session object includes integer id/webinar_id, Unix-second starts_at/ends_at, stored timezone, title/description/category/category_name, image_url/image_alt/presenter, session status, nullable capacity and integer registration_closes_at. Unknown category values normalize to general. Descriptions are returned as text values without HTML sanitization; render them as text, as CRM2 does.
Action/state fields are registration_open, registration_url, interested, registration_opened_at, join_available, join_url, registration_state, provider_mapped, provider_available, recording_provider_mapped, recording_registration_state, recording_duration, watched_seconds, recording_available, recording_url and recording_published_at. Flags are booleans, durations/times/counts are integers, and URL/state fields are strings. Unavailable URLs are empty strings, not omitted.
A provider-mapped live session requires cached registration_state=confirmed for joining; a legacy unmapped session requires local interested=true. The session must be scheduled, current time must be at least starts_at−900 and no later than ends_at, and the chosen personal/legacy join URL must be valid HTTPS without embedded credentials. Joining is not exposed earlier by get/list.
Recordings require completed session status, published recording status, and ends_at no later than now. Mapped recordings expose no shared recording_url: use Training_watch for personal access. Unmapped recordings expose a valid HTTPS URL. A recording can be available without live interest or a live booking. Images also require valid HTTPS without embedded credentials.
Adds recordsFiltered and recordsTotal to the Training_list envelope; both equal total after the selected category and publication/recording filters. These are not separate filtered/unfiltered catalogue counts. No draw echo or next-page cursor is supplied.
Errors and unusual responses
| Response | Condition |
|---|---|
{"error":"not logged in"} | No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs. |
{"error":"invalid Training category"} | Nonblank category is not in the list allowlist. |
{"error":"invalid Training sort"} | sort is unknown and was not replaced by title ordering. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Requires a published parent webinar, completed session, ends_at no later than now and published recording status. Mapped recordings or SQL https://-prefixed legacy URLs qualify according to provider-schema readiness. Final public URL validation can still mark a listed legacy record unavailable.
- Pagination is page-based under the table wrapper. With start=15 and length=10, the effective offset is 10, not 15. start=20/length=10 selects the expected third page. There is no stable snapshot across requests.
- Newest orders recording publication time descending then session ID descending. Alphabetical title order uses ID ascending as tie-breaker for both az and za. Records count session rows, not deduplicated webinar titles or recording broadcasts.
- General widget fields/projection, draw, searchValue and arbitrary advanced filters are not implemented; only the explicit parameters above reach the list logic.
- Provider workflows require the global provider schema (migration 011) and server WebinarGeek credentials. This is deployment configuration, not the account’s HR setting or a request-supplied API key. Without the provider schema, legacy URL-based catalogue/interests still work; without credentials, provider actions fail while cached catalogue data remains readable.
- Refresh operates on all saved provider registrations for the authenticated user, not only the displayed category/page. Rows synced less than about five minutes ago are reused. Older rows are reconciled against provider subscriptions (up to 20 pages of 100); at most ten extra subscription-ID lookups recover entries omitted from provider lists. Cached statistics are retained when refresh fails.
stalereports incomplete refresh andunavailablereports missing provider schema/configuration; these are not booking states or proof of live provider health.
Side effects
- Like the on-demand list, a table read can reconcile provider registrations and write cached status, personal URLs, viewing counters and sync/edit timestamps. It does not create a registration, mark completion or send an independent notification.
See it in Fieldmotion
CRM1
No direct CRM1 caller for Training_videosTable was found. The implemented screen is CRM2 Training; no direct mobile-app or m-service caller was found for these CRM routes.
CRM2
Open Training → Watch Training Videos. Topic, Sort By, title-header sorting and pagination produce Training_videosTable requests through widgetDisplayStyle. The table is read-only; Watch Video uses Training_watch for mapped recordings or directly opens the returned legacy recording_url.
Related endpoints
Training_list: underlying on-demand selection and all categories.Training_watch: obtain personal recording access.Training_progress: aggregate all currently published recordings, not just this table page.