When to use this endpoint

Discover session IDs from Upcoming Live Webinars, a bounded calendar window, or the on-demand recording catalogue, then load Training_get before presenting actions. This is a shared training catalogue enriched with the current user’s interest and booking state, not a tenant’s job/training-object list.

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

ParameterTypeRequiredDescription
viewStringNoTrimmed, case-sensitive upcoming (default), calendar, or on_demand. Blank/unknown values fail rather than using the default.
categoryStringNoTrimmed exact category ID, blank means all. Accepted: general, getting_started, jobs, assets_stock, advanced, scheduling, assets, stock, finance, forms, reports. Returned categories always includes the complete list, not only matching categories.
from / toUnix secondsFor calendarInteger-converted positive from and strictly later to; maximum span is 93×86400 seconds. Filters exclusive overlap: starts_at < to and ends_at > from. No parsing of civil dates or milliseconds. Ignored in other views.
sortStringOn-demand onlyExact newest (default), az or za; unrecognized values fail only in on_demand. Orders respectively by recording_published_at descending/ID descending, title/ID ascending, or title descending/ID ascending.
pageIntegerNoOn-demand only: present non-null page enables zero-based pagination; negative values clamp to zero and nonnumeric values integer-convert to zero. Omission means first 200 records, not a default ten-row page.
page_lengthIntegerWith pageOn-demand page length defaults to 10 when page is set, then clamps to 1–200. Ignored without page. Upcoming/calendar cannot be paged through these controls.

Example request

$result = FM_api('Training_list', [
	'view' => 'calendar',
	'category' => 'jobs',
	'from' => '1794355200',
	'to' => '1794614400',
]);

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": 1794477600,
      "ends_at": 1794481200,
      "timezone": "Europe/Dublin",
      "status": "scheduled",
      "capacity": null,
      "registration_closes_at": 0,
      "registration_open": true,
      "registration_url": "https://example.test/register",
      "interested": false,
      "registration_opened_at": 0,
      "join_available": false,
      "join_url": "",
      "registration_state": "none",
      "provider_mapped": false,
      "provider_available": false,
      "recording_provider_mapped": false,
      "recording_registration_state": "none",
      "recording_duration": 0,
      "watched_seconds": 0,
      "recording_available": false,
      "recording_url": "",
      "recording_published_at": 0
    }
  ],
  "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
}

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.

Returns data, the full categories array, and integer total before row limits/paging. No matches means data=[], total=0. Only on_demand adds stale/unavailable refresh flags; the example is a legacy URL-based calendar record before its join window.

Errors and unusual responses

ResponseCondition
{"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 view"}view is not upcoming/calendar/on_demand.
{"error":"invalid Training category"}Nonblank category is not in the category allowlist.
{"error":"invalid Training calendar range"}Calendar endpoints are invalid, reversed/equal, or more than 93×86400 seconds apart.
{"error":"invalid Training sort"}Unknown on-demand sort value.

Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.

Behaviour and edge cases

  • Every view requires the parent webinar to be published. Upcoming selects scheduled sessions whose ends_at is later than now, including sessions already in progress; it returns at most 200 ordered by starts_at then ID ascending. Calendar uses scheduled sessions overlapping the supplied range, including past sessions in that range, capped at 500 with the same ordering.
  • On-demand selects completed sessions already ended with published recordings, then requires a positive recording broadcast ID or a stored URL matching the SQL https:// prefix. With no provider schema only URL-based recordings qualify. This SQL prefilter is weaker than final URL validation: a malformed prefixed URL can be counted/listed but have recording_available=false.
  • The total can exceed the returned count. Upcoming/calendar have no offset cursor; use narrower calendar windows when hitting their fixed caps. On-demand pages are offset-based, not stable snapshots.
  • registration_open requires scheduled status, a future start, an unexpired/nonexistent registration cutoff, and either a provider broadcast mapping or valid registration URL. capacity is informational here; no local seat count/capacity enforcement occurs. A mapped row can therefore report registration_open=true even if its legacy registration_url is blank; use Training_register.
  • 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. stale reports incomplete refresh and unavailable reports missing provider schema/configuration; these are not booking states or proof of live provider health.

Side effects

  • Upcoming/calendar only read catalogue and cached personalization. On-demand may contact WebinarGeek and update global registration status, personal URLs, monotonic watch/replay seconds and sync/edit timestamps; it does not create subscriptions.

See it in Fieldmotion

CRM1

No direct CRM1 caller for Training_list was found. The current catalogue/booking screen is CRM2 Training; tests are not production UI callers. No direct mobile-app or m-service caller for these CRM routes was found.

CRM2

Open Training Home or Upcoming Live Webinars; filter Network requests for Training_list. Home and list view request upcoming; Calendar View sends account-timezone-derived Unix from/to values and category. Watch Training Videos uses the Training_videosTable wrapper instead.

Related endpoints