Skip to main content
An XP rule can only price an event type that one of your installed modules declares. This page catalogs the engagement events declared by the 4D Engagement module and the payload each expects, so your backend knows what to send. How events get in is covered in Events; what can stop an award is covered in XP. The machine-readable source of truth is the module version manifest your installation is pinned to: GET /v1/installations gives you the module_id and pinned version_id, GET /v1/modules/{moduleId}/versions returns each version’s manifest, and the emits[] entry for your event type carries the JSON Schema for its payload. An installation stays on its pinned version until you adopt a newer one, so read the pinned manifest rather than assuming the newest catalog version.

The catalog

“Pays once per item” is the always-on same-content guard described in XP: the first paid event claims that item (content_id, quiz_id, video_id) for the member, and a later event for the same item is declined as content_already_earned - even if it arrives under a fresh event_id. The repeatable types have no per-item identity to claim, so the brakes there are the per-action daily and lifetime award limits your organization configures in the console.

Payload contracts

Send these as the payload of your event. The schemas are closed: an undeclared field is rejected. Every event needs a member identity - your member_external_id at the envelope level or a platform user_id in the payload; an event with neither is recorded but earns nothing.

engagement.content.viewed

engagement.quiz.completed

engagement.video.completed

Completion is your claim; the measurement fields use Google Analytics 4’s parameter names so a video player integration can forward what it already emits.

engagement.profile.completed

A profile milestone, not a field-change event. No required payload fields.

engagement.venue.attended

Check-in evidence is yours: the platform validates the field shapes, stores them and passes them through, but does not verify a geofence, QR value or beacon signal.

engagement.reply.posted

One event per reply, so a member can have many.

Choose your event ids deliberately

event_id is the deduplication anchor: a repeat send of the same id credits nothing a second time. Derive it from the identity of the moment, not at random, and reuse it on retries:
  • Profile completed - organization_id + member_id. One milestone per member.
  • Venue attended - organization_id + member_id + venue_id + fixture_id, or the UTC calendar date of occurred_at when there is no fixture. One check-in per member, venue and occasion.
  • Video completed - organization_id + member_id + video_id. The platform’s same-content guard backs this up regardless of event ids.
  • Reply posted - organization_id + reply_id. A different reply must get a different id.
Any collision-resistant deterministic string works, as long as the same identity always produces the same id and your organization id is part of what you hash. Finally, pick one event type per asset. engagement.content.viewed and engagement.video.completed are different types: if you emit both for the same asset and both are priced, both pay. The platform does not infer that they describe the same content.