Training sessions and recordings
Register for a live webinar with verified provider identity
/a/Training_register
Register for a live webinar with verified provider identity
When to use this endpoint
Use a two-stage workflow: first call without submit to show the current provider fields and exact consent statements, then submit the entered values/accepted labels. Read Training_get or refresh My Booked Webinars afterwards; interestUpdate is not a substitute for provider registration.
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 |
|---|---|---|---|
session_id | Integer | Yes | Integer-converted shared session ID. The provider workflow returns not-found for zero/missing IDs after login/provider-readiness checks; it has no separate missing-parameter error. |
submit | PHP truthy flag | No | Omitted/blank/0/"0" requests fields/defaults without creating a new subscription. Any PHP-nonempty value, including string "false", submits. Use explicit 1 for submission. Recording access can reuse an existing confirmed subscription even without submit. |
values | JSON object text or native array | For submission | Map current provider field names to scalar values. Invalid/non-array JSON becomes an empty map. Server account email/external identity cannot be overridden. firstname falls back to the account first name; send other requested fields explicitly. Unknown value keys are ignored; provider-defined unsupported fields require the provider registration page. |
consents | JSON object text or native array | For submission when required | Map every returned consent label to integer 1, string "1" or true. Other truthy values are not accepted. Invalid/non-array JSON becomes an empty map. Accepted statement text comes from the stored session configuration, not submitted text. |
Example request
$result = FM_api('Training_register', [
'session_id' => '1042',
'submit' => '1',
'values' => json_encode(['firstname' => 'Alex', 'surname' => 'Taylor', 'extra_field_3' => 'Beginner']),
'consents' => json_encode(['Privacy' => 1]),
]);
Example response
{
"ok": 1,
"registration_state": "confirmed",
"watch_url": ""
}
Without submit the response is a field-definition/defaults envelope such as {"fields":[{"name":"firstname","label":"First name","mandatory":true,"input_type":"text_field"},{"name":"email","label":"Email","mandatory":true,"input_type":"text_field"},{"name":"extra_field_3","label":"Experience","mandatory":true,"extra_field":true,"input_type":"dropdown","field_options":[{"label":"Beginner"},{"label":"Expert"}]}],"consents":{"Privacy":"I agree to the privacy policy."},"defaults":{"firstname":"Alex","surname":"Taylor","email":"alex@example.test"},"registration_state":"none"}. fields is the live provider array and consents is its separately stored label/statement map; neither is a session object.
Submission returns ok=1 with registration_state=confirmed, pending or cancelled, based on returned provider evidence; confirmed requires a positive subscription ID and literal provider email_verified=true, while unsubscribed evidence takes precedence as cancelled. Pending recovery can additionally include message and omit watch_url. The live route’s watch_url is always empty: it must not bypass the protected join window.
No provider subscription ID or tenant/user identity is returned in the public success envelope. This endpoint can trigger provider communication; opening a form is not itself a completed registration.
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":"WebinarGeek is not connected yet. Please try again later."} | Provider schema or server configuration is unavailable; checked before session lookup. |
{"error":"Training session not found"} | No such session under a published parent webinar. |
{"error":"This session is not connected to WebinarGeek yet."} | The relevant live/recording broadcast mapping is zero. |
{"error":"This broadcast is not available"} | Provider broadcast is cancelled or uses active just-in-time scheduling. |
{"error":"Unable to load registration fields. Please try again later."} | Provider webinar request failed or did not supply a registration_fields array. |
{"error":"Complete all required registration fields."} | A current mandatory provider field is blank. |
{"error":"Select a valid registration option."} | A nonblank dropdown value is not a current provider option label. |
{"error":"Please accept the required consent statements."} | Any configured consent label lacks explicit true/1/"1". |
{"error":"A valid account email and first name are required."} | Submission cannot resolve a valid authenticated email/first name. |
{"error":"Invalid registration field"} | A recognized submitted provider field has a non-scalar value. |
{"error":"Registration field is too long"} | A recognized trimmed provider field exceeds 2,000 bytes. |
{"error":"This registration field needs the provider registration page."} | Provider schema contains a field the adapter cannot map to a supported standard/extra field. |
{"error":"Registration is already being processed. Please wait."} | The request did not acquire the ten-minute per-registration claim. |
{"error":"Unable to save registration. Please try again later."} | No local registration row was recovered, or the pre-POST pending write failed. |
{"error":"Unable to verify this registration"} | Existing saved subscription evidence identifies another tenant/user reference or broadcast. |
{"error":"Registration is awaiting verification. Please try again later."} | Returned subscription ID/reference/broadcast is missing or inconsistent. |
{"error":"Registration was sent. Please refresh to verify its status."} | Provider subscription was found/sent, but its local cache write failed. Do not infer that replay is safe. |
{"error":"Registration for this Training session has closed"} | Session is not scheduled, has started, or a nonzero registration cutoff has elapsed. |
{"error":"The webinar schedule has changed. Please contact support."} | Provider broadcast date differs from the stored start, or provider says the broadcast has ended. |
Legacy application errors may use HTTP 200; inspect the decoded error field. Provider transport errors are sanitized. Common responses contain error plus status and uncertain: status 429 reports “WebinarGeek is busy. Please try again later.”; 401/403 reports “WebinarGeek is not available. Please contact support.”; 400/422 reports “WebinarGeek could not accept this registration. Check the required fields or use the provider registration page.” Other failures report “Unable to contact WebinarGeek. Please try again later.” Never replay a registration/cancellation merely because uncertain=true. A reconciliation failure may also return “Invalid WebinarGeek response” or “WebinarGeek results are too large. Please contact support.”
Behaviour and edge cases
- 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.
- Provider identity defaults come from the active authenticated tenant-user row, not request client_id/user_id/email/external_id. The account name is split into first name and remaining surname; time_zone comes from the session timezone or Europe/Dublin. The authenticated email and tenant/user-specific external reference are immutable; recognized name/address fields can use submitted values. The same person is scoped separately in another tenant.
- Fetch current provider fields before submitting; the endpoint refetches them on every action. Values are trimmed scalar text, limited to 2,000 bytes per recognized provider field. Mandatory fields cannot be blank, dropdown values must equal a listed label, and every stored required consent must be explicitly accepted. Email always uses the account email, regardless of values.email; a valid account email and first name are required. Field schemas/defaults are examples, not a fixed contract.
- Both preflight and submit require registration_open now; an already-booked user cannot use this route to bypass an elapsed cutoff or fetch a live joining link. Future scheduled sessions with provider mappings can register without a legacy registration_url. capacity is not locally enforced; provider acceptance controls the subscription.
- Submission upserts a local row keyed by tenant/user/broadcast and takes a 600-second database claim. An active claim returns “Registration is already being processed. Please wait.” Reconciliation looks up existing provider subscriptions before posting, including a saved subscription-ID fallback; repeated requests should reuse verified evidence instead of duplicating registration.
- Before a new provider POST, the local row is marked pending. A timeout/5xx or malformed successful reply can mean that the provider accepted the request; uncertainty is returned without blind resubmission. A pending row with no recoverable subscription returns ok=1/pending and a check-email/contact-support message. Local success never overrides provider email-verification evidence.
- No Core_log/tenant deletion audit, local interest upsert or direct Training reminder is created here. The local claim release is token-checked in finally; a crashed request can leave the claim until its lease expires. A confirmed booking and a legacy interested flag remain separate.
Side effects
- Preflight contacts the provider for the broadcast and current webinar fields and reads local identity/registration data. It does not create a subscription. Submit can create/update global registration rows, claim/release timestamps, pending/failure/status evidence and cached personal URLs/viewing statistics.
- A new provider subscription POST uses skip_confirmation_mail=false and explicit consent text, so provider confirmation/email behavior may follow. The CRM handler does not independently send a duplicate Core_mail message. Registration, cache writes and external calls do not form one distributed transaction.
See it in Fieldmotion
CRM1
No direct CRM1 caller for Training_register was found. The current implementation is CRM2 Training; no direct mobile-app or m-service caller was found for these CRM routes.
CRM2
Training → Upcoming Live Webinars → Register Now opens a Training_register preflight. Completing the dialog sends submit=1 and JSON-encoded values/consents. Email is read-only in the UI. The screen shows Awaiting confirmation for pending results, then reloads.
Related endpoints
Training_get: read guarded personal join access.Training_mySessions: refresh booked=1 and inspect pending/confirmed rows.Training_cancelRegistration: unsubscribe an existing future live booking.Training_interestUpdate: legacy indication of interest, not provider confirmation.