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 (partner admin session) 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

Send duration_seconds as a whole number of seconds. A fractional value such as 12.5 fails the platform’s check on the event, and the event earns nothing.

engagement.quiz.completed

Send score as a whole number. A fractional score fails the same check, and the event earns nothing.

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.

Discover what each event pays

The manifest tells you what an event must look like. What it pays is your organization’s XP rules, which are set in the console. Your backend can read the current pricing with GET /v1/xp/event-definitions, so it doesn’t have to copy amounts out of the console. The answer has one rule set per event type. A rule set has an optional default and an ordered list of cases. Cases are checked in the order returned, and the first case whose condition matches pays. If no case matches, the default pays, and with no default the set pays nothing. Exactly one member of a set pays per event. Rules written outside the console’s sets appear under adds_on_top. Each one that matches adds its award to the set’s, so when that list isn’t empty the set is not the whole price. Retired rules and old versions are left out. A boost can still multiply the result, and the earning controls can still decline it. Creating definitions needs a partner admin session; an org key can only read them.

API reference

List XP event definitions

GET /v1/xp/event-definitions authenticates with your organization API key as a bearer header (see Authentication). It takes no parameters and returns your organization’s active pricing.
200 OK:
An organization with no active rules returns { "rule_sets": [] }.