JavaScript API
ResultFly campaigns can send key interaction events from the embedded experience to the host website or application. This allows developers to connect ResultFly to analytics systems, CRM platforms, CDPs, tag managers, and custom business logic.
This JavaScript API is not the canonical CDP ingest surface. If you need to push customer facts into ResultFly as governed profile or event data, use the server-to-server CDP ingest APIs instead.
Typical use cases include:
- sending campaign interaction events to analytics platforms
- tracking page views, starts, completions, and question answers
- capturing form submissions generated inside campaigns
- tracking rewards or coupons received by users
- measuring campaign funnels and completion rates
- triggering actions in external systems
ResultFly provides a public JavaScript API designed to expose stable business-level events from embedded campaigns.
Integration Overview
A ResultFly campaign can be embedded into a client website or application. Once embedded, the campaign sends public events to the host environment using one or more supported delivery methods.
These events can be consumed by:
- analytics tools such as GA4, Segment, or Amplitude
- tag managers such as Google Tag Manager
- CRM or CDP integrations
- custom front-end logic
- internal event pipelines
All public events follow a stable versioned contract.
LMS Compatibility
ResultFly JavaScript API works well in LMS environments, especially when campaigns are embedded as iframes inside course pages, lesson modules, or learning portals.
For LMS integrations, postMessage is typically the most reliable delivery method because many LMS platforms restrict direct access to host-page JavaScript while still allowing iframe-to-parent communication.
Typical LMS use cases include:
- tracking learner interaction starts and completions
- recording question answers for reporting
- marking lesson engagement or completion based on
finished - forwarding ResultFly events into SCORM, xAPI, or proprietary LMS tracking layers
When integrating with an LMS, verify that the platform allows:
- embedded third-party iframe content
- cross-window messaging with
postMessage - the ResultFly campaign origin in any relevant allowlist or content security policy
Quick Start
The simplest integration is embedding the campaign and subscribing to ResultFly events.
<div id="resultfly-embed"></div>
<script src="https://cdn.resultfly.com/embed.js"></script>
<script>
window.addEventListener("resultfly:event", function (e) {
const evt = e.detail;
console.log("ResultFly event:", evt);
if (evt.type === "started") {
console.log("User started interaction");
}
if (evt.type === "finished") {
console.log("Interaction finished:", evt.data?.outcome);
}
if (evt.type === "reward_received") {
console.log("Reward received:", evt.data?.reward_title);
}
});
</script>Event Delivery Methods
ResultFly supports multiple ways to deliver events to the host page.
1. JavaScript Event Bridge (recommended)
Events are emitted as browser events:
resultfly:eventExample:
window.addEventListener("resultfly:event", (e) => {
const event = e.detail;
});This method works well with:
- modern front-end frameworks
- analytics SDKs
- tag managers
- custom event pipelines
2. Callback Integration (optional)
For simple environments a global callback can be used.
window.onResultFlyEvent = function (event) {
console.log("ResultFly event:", event);
};If this function exists, ResultFly calls it for each public event.
3. iframe postMessage Integration
If the campaign is embedded as a pure iframe without the ResultFly host script running on the page, events are delivered via postMessage.
Example:
<iframe
src="https://campaign.resultfly.com/demo"
width="400"
height="700">
</iframe>
<script>
window.addEventListener("message", function (event) {
if (!event.data) return;
if (event.data.source !== "resultfly") return;
const evt = event.data.event;
console.log("ResultFly event:", evt);
});
</script>ResultFly always includes source: "resultfly" in postMessage events to simplify filtering.
Event Model
All ResultFly public events follow a unified schema.
type ResultFlyEvent = {
type:
| "ready"
| "page_viewed"
| "started"
| "finished"
| "answered"
| "submitted"
| "reward_received"
| "error"
version: "1.0"
timestamp: string
campaign_id: string
session_id?: string
page_id?: string
component_id?: string
component_type?: string
data?: Record<string, unknown>
}Notes:
session_idis present for interaction-level events and may be omitted for bridge lifecycle events such asready.page_ididentifies the currently visible campaign page.component_idandcomponent_typeidentify the component that produced the event.datacontains event-specific fields only.
Event Categories
ResultFly public events are grouped into three categories.
Lifecycle Events
| Event | Description |
|---|---|
ready | Campaign embed loaded and JavaScript API is ready |
page_viewed | A campaign page became visible |
started | User started interacting with a game or interactive component |
finished | Interaction completed |
error | Campaign encountered a runtime or delivery error |
Interaction Events
| Event | Description |
|---|---|
answered | User answered a quiz or question component |
submitted | User submitted a form inside the campaign |
Reward Events
| Event | Description |
|---|---|
reward_received | User received a reward or coupon |
Event Reference
ready
Triggered when the campaign embed finishes loading and the JavaScript API is available.
{
"type": "ready",
"version": "1.0",
"timestamp": "2026-03-13T10:00:00.000Z",
"campaign_id": "cmp_demo_001",
"data": {
"sdk_version": "1.0.0",
"delivery_methods": ["dom_event", "callback", "postmessage"]
}
}page_viewed
Triggered whenever a campaign page becomes visible.
This event fires:
- when the campaign first loads
- when navigating between pages inside the campaign
{
"type": "page_viewed",
"version": "1.0",
"timestamp": "2026-03-13T10:00:01.000Z",
"campaign_id": "cmp_demo_001",
"session_id": "sess_abc123",
"page_id": "welcome_page",
"data": {
"previous_page_id": null
}
}started
Triggered when the user begins interacting with a game or interactive component.
A campaign page may contain multiple interactive elements, and each interaction can generate its own started event.
{
"type": "started",
"version": "1.0",
"timestamp": "2026-03-13T10:00:05.000Z",
"campaign_id": "cmp_demo_001",
"session_id": "trivia_quiz_abc123",
"component_id": "quiz_1",
"component_type": "trivia_quiz",
"data": {
"game_type": "trivia_quiz",
"started_at": "2026-03-13T10:00:05.000Z",
"timer": {
"enabled": true,
"limit_ms": 60000
}
}
}finished
Triggered when a user finishes interacting with a game or interactive component.
This is the primary completion event in the JavaScript API and represents the final outcome of the interaction.
{
"type": "finished",
"version": "1.0",
"timestamp": "2026-03-13T10:01:12.000Z",
"campaign_id": "cmp_demo_001",
"session_id": "trivia_quiz_abc123",
"component_id": "quiz_1",
"component_type": "trivia_quiz",
"data": {
"outcome": "success",
"end_reason": "objective_reached",
"duration_ms": 67000,
"timer": {
"enabled": true,
"limit_ms": 60000,
"elapsed_ms": 67000,
"expired": false
},
"metrics": {
"score": 8,
"correct_answers": 8,
"completion_percent": 100
}
}
}outcome values
| Value | Meaning |
|---|---|
success | User successfully completed the interaction |
fail | User failed the objective |
quit | User exited before completion |
error | Interaction failed due to an error |
end_reason values
| Value | Meaning |
|---|---|
objective_reached | Interaction objective completed |
timer_expired | Time limit reached |
time_survived | Survival-based goal completed |
out_of_moves | User ran out of moves |
manual_exit | User exited interaction |
collision | Interaction ended by collision |
error | Runtime failure during play |
unknown | Fallback value if cause unknown |
answered
Triggered when a user answers a question inside supported quiz or question components.
{
"type": "answered",
"version": "1.0",
"timestamp": "2026-03-13T10:00:35.000Z",
"campaign_id": "cmp_demo_001",
"session_id": "trivia_quiz_abc123",
"component_id": "quiz_1",
"component_type": "trivia_quiz",
"data": {
"question_id": "q_1",
"answer_id": "a_2",
"correct": true
}
}Notes:
correctis included when the component supports correctness evaluation.- For non-scored question components,
correctmay be omitted.
submitted
Triggered when a user submits a form inside the campaign.
{
"type": "submitted",
"version": "1.0",
"timestamp": "2026-03-13T10:01:20.000Z",
"campaign_id": "cmp_demo_001",
"session_id": "sess_abc123",
"component_id": "lead_form_1",
"component_type": "form",
"data": {
"field_keys": ["email", "phone", "consent"]
}
}Notes:
- Public JavaScript events do not include raw field values by default.
- The event is intended for integration, tracking, and workflow triggers.
- To send lead values to a CRM or backend, use Lead Form Webhooks.
reward_received
Triggered when the user receives a reward.
This event is emitted only after reward allocation is completed and the reward is available to the user.
{
"type": "reward_received",
"version": "1.0",
"timestamp": "2026-03-13T10:01:25.000Z",
"campaign_id": "cmp_demo_001",
"session_id": "fortune_wheel_xyz789",
"component_id": "wheel_1",
"component_type": "fortune_wheel",
"data": {
"reward_id": "rw_123",
"reward_type": "coupon",
"reward_title": "10% OFF",
"coupon_code": "SAVE10"
}
}error
Triggered when the campaign encounters a runtime or delivery error relevant to the host integration.
{
"type": "error",
"version": "1.0",
"timestamp": "2026-03-13T10:01:30.000Z",
"campaign_id": "cmp_demo_001",
"session_id": "fortune_wheel_xyz789",
"component_id": "wheel_1",
"component_type": "fortune_wheel",
"data": {
"code": "reward_pool_empty",
"message": "No rewards available"
}
}Analytics Integration Example
Example using dataLayer for Google Tag Manager.
window.addEventListener("resultfly:event", function (e) {
const evt = e.detail;
window.dataLayer = window.dataLayer || [];
if (evt.type === "page_viewed") {
window.dataLayer.push({
event: "resultfly_page_viewed",
campaign_id: evt.campaign_id,
session_id: evt.session_id,
page_id: evt.page_id
});
}
if (evt.type === "started") {
window.dataLayer.push({
event: "resultfly_started",
campaign_id: evt.campaign_id,
session_id: evt.session_id,
component_id: evt.component_id,
component_type: evt.component_type
});
}
if (evt.type === "finished") {
window.dataLayer.push({
event: "resultfly_finished",
campaign_id: evt.campaign_id,
session_id: evt.session_id,
component_id: evt.component_id,
component_type: evt.component_type,
outcome: evt.data?.outcome,
end_reason: evt.data?.end_reason
});
}
if (evt.type === "reward_received") {
window.dataLayer.push({
event: "resultfly_reward_received",
campaign_id: evt.campaign_id,
session_id: evt.session_id,
reward_id: evt.data?.reward_id,
reward_type: evt.data?.reward_type
});
}
});Callback Example
window.onResultFlyEvent = function (evt) {
if (evt.type === "submitted") {
console.log("Form submitted:", evt.component_id);
}
if (evt.type === "reward_received") {
console.log("Reward:", evt.data?.reward_title);
}
};postMessage Example
const RESULTFLY_ORIGIN = "https://campaign.resultfly.com";
window.addEventListener("message", function (event) {
if (event.origin !== RESULTFLY_ORIGIN) return;
if (event.data?.source !== "resultfly") return;
const evt = event.data.event;
if (evt.type === "finished") {
console.log("Finished with outcome:", evt.data?.outcome);
}
});Security Notes
When using postMessage, validate the message origin.
const RESULTFLY_ORIGIN = "https://campaign.resultfly.com";
window.addEventListener("message", function (event) {
if (event.origin !== RESULTFLY_ORIGIN) return;
if (event.data?.source !== "resultfly") return;
const evt = event.data.event;
});Versioning
ResultFly uses a versioned public JavaScript event contract.
- Current version: 1.0
- New fields may be added inside
data - Existing documented fields will not change without introducing a new version
Integrations should rely only on documented event names and documented fields.
Support
For integration assistance contact: