OnAim Integration Guide

1. Introduction

OnAim is a gamification as a service platform that enables casinos, betting operators, and other digital businesses to create event-driven player promotions — without custom development for each campaign.

Your system sends player activity events (such as Bet, Win, Registration, Deposit, or custom-defined events) to OnAim in real time.

OnAim processes these events according to your promotion’s configuration — awarding coins, filling progress bars, updating leaderboards, unlocking prizes, and more.

Players access promotions through a Promotion Landing Page, which you embed into your platform via an iframe, allowing a seamless in-platform experience.

To ensure smooth integration:

  • Player identification must be secure and verified.
  • Events must include the required fields.
  • Specific backend endpoints must be available for player info and coin withdrawals.

Note: OnAim operates as a single-tenant platform. Each partner has a dedicated, isolated environment for event ingestion, data processing, and player interactions.

---

2. Event Integration

OnAim’s event processing system supports multiple integration channels depending on your infrastructure.

2.1 Supported Integration Methods

Method Description Recommended Use
REST API Send events to OnAim via HTTPS POST requests. Simpler integrations, low to medium traffic
RabbitMQ Publish events to a configured RabbitMQ exchange. Asynchronous event-driven systems
Kafka (AVRO or JSON) Send serialized events to a Kafka topic provided by OnAim. High-volume, distributed systems
Note: These values are indicative and may vary based on deployment and load. Each tenant can request customized throughput limits.

2.2 Dynamic Event Schema

OnAim accepts fully dynamic event structures.

Only the following three fields are mandatory:

{
  "customerId": "string",
  "eventType": "string",
  "timeStamp": "string (ISO 8601, UTC recommended)"
}

All other fields are free and flexible — you can include any additional data relevant to your use case.

These fields can be:

  • Shared properties, such as `deviceType`, `promotionId`, `country`, or `sessionId`
  • Event-specific properties, such as `amount`, `gameName`, or `RefereeId`

UTC Time: Timestamps should be provided in UTC. Non-UTC values are accepted but can result in misaligned reporting and aggregation windows.

If an event is missing any of the three required fields (`customerId`, `eventType`, `timeStamp`):

  • The event will be rejected and logged in the ingestion service.
  • Logs will contain the tenant ID, malformed payload, and timestamp for traceability.

There is no restriction on additional data — OnAim automatically flattens all properties for processing. You can extend event structure anytime without redeploying backend code.

2.3 Example Event

{
    "customerId": "P_123456",
    "eventType": "Bet",
    "timeStamp": "2025-05-23T10:16:58.980Z",
    "deviceType": "Mobile",
    "gameName": "Roulette",
    "gameType": "Table",
    "amount": 100,
    "currency": "USD"
}
  • `customerId`, `eventType`, and `timeStamp` are mandatory.
  • `deviceType` and `promotionId` are shared fields.
  • `gameName`, `gameType`, `amount`, and `currency` are custom properties.
  • The structure is flat — all properties are accessible in the Admin Panel once described in the schema.

2.4 Schema Description for Admin Panel

While OnAim accepts any dynamic event, the Admin Panel can only display and filter fields that are defined in your schema definition.

This schema can be defined via:

  • UI configuration in the OnAim Admin Panel

The schema describes all fields and expected types. Once registered, OnAim automatically generates UI elements (filters, dropdowns, numeric inputs) for building conditions and analytics.

Example Schema Description:
{
  "customerId": "string",
  "eventType": "Bet | Win | Registration | Deposit | CustomEvent",
  "timeStamp": "2025-05-23T10:16:58.980Z",
  "deviceType": "Mobile | Desktop | Tablet | Other",
  "customProperties": {
    "Bet": {
      "gameName": "string",
      "gameType": "string",
      "amount": 100,
      "currency": "USD"
    },
    "Win": {
      "gameName": "string",
      "value": 250,
      "coinId": "FreeSpin"
    },
    "Registration": {
      "RefereeId": "string",
      "RegistrationDate": "2025-05-23T10:16:58.980Z"
    }
  }
}

Schema Description Rules

Rule Description
Required fields Only customerId, eventType, and timeStamp are mandatory.
Shared fields Common fields (e.g., deviceType, promotionId, country) should be defined at the root level.
Event-specific fields Must be described inside customProperties under their event type key.
Data type or example value Specify either a data type (string, number, date) or example value ("USD", 100).
Optional fields You can omit properties; they’ll still be processed but won’t appear in Admin filters.
Extensibility The schema can be modified or extended anytime without affecting existing data.
Flattened structure All fields (shared + event-specific) become flat for analytics and filtering.

---

3. Promotion Page Integration (Iframe + OTT Authentication)

Players interact with promotions through a Promotion Landing Page hosted by OnAim. You can embed this page inside your platform using an iframe.

3.1 Base URL


https://[onaim-landing].io/?promotionId=752&lang=ka&landingPageId=574

3.2 Secure Player Identification (OTT Token)

Each player is identified using a One-Time Token (OTT) generated by your backend.


https://[onaim-landing].io/?promotionId=752&lang=ka&landingPageId=574&ott=80b6a17cc251

OTT Requirements

Property Description
Unique per session Must be generated for one player session only.
Server-side generation Must be created and signed by your backend.
Secure transmission Must be passed over HTTPS only.
Example Iframe:
<iframe src="https://[onaim-landing].io/?promotionId=752&lang=ka&landingPageId=574&ott=80b6a17cc251" width="100%" height="800" frameborder="0" allowfullscreen></iframe>

---

4. Player Info Endpoint

The Player Info Endpoint is used by OnAim to fetch information about a player whenever the promotion landing page or event processing requires it.

There is no strict requirement to use the OTT-based flow. The endpoint can be implemented in any form that your system supports, as long as OnAim can uniquely identify the player and retrieve accurate player details.

For example:

  • You can use the previously described OTT flow for higher security.
  • Or, you can expose a direct authenticated endpoint that takes any identifying token, or session key to resolve the player.

The only requirement is that your endpoint must reliably return the correct player information based on the identifier provided by OnAim.

Example Request


GET https://[yourdomain].com/onaim/player?token={ANY_UNIQUE_IDENTIFIER}

Example Response

{
  "PlayerId": "P_123456" , 
  "UserName": "JohnDoe",
  "Email": "[email protected]",
  "Country": "GE",
}

4.3 Field Mapping

Map OnAim’s required fields to your backend’s property names and provide the mapping as shown below:
{
  "FieldMappings": {
    "default": {
      "PlayerId": "YOUR_PLAYER_ID_PROPERTY", 
      "UserName": "YOUR_PLAYER_NAME_PROPERTY"
    }
  }
}

---

5. Coin Withdrawal Endpoint

When players redeem withdrawal coins (coins that represent real or virtual currency), OnAim triggers your withdrawal endpoint to process the payout.

All withdrawal options — endpoint URL, HTTP method, headers, query parameters, and body mappings — are fully configured in the OnAim Admin Panel UI.

5.1 Example Configuration

{
  "endpointUrl": "https://api.yourdomain.com/onaim/withdraw",
  "method": "POST",
  "headers": {
    "Authorization": "Bearer {API_KEY}"
  },
  "body": {
    "playerId": "{PlayerId}",
    "amount": "{Amount}", 
    "currency": "{Currency}", 
    "transactionId": "{TransactionId}"
    }
}

5.2 Response Requirements & Behavior

Condition Expected Description
Success HTTP 200 OK Withdrawal confirmed successfully.
Failure HTTP 4xx / 5xx Transaction marked as failed; visible in admin logs.
Timeout N/A If the request exceeds timeout (default: 5s), OnAim retries up to 3 times with exponential backoff if idempotency is supported.
Duplicate prevention N/A If your system supports idempotency keys using transactionId, OnAim safely retries without creating duplicates. Otherwise, OnAim will perform only one attempt to avoid double payouts.

5.3 Retry and Rollback Policy

  • If idempotency is implemented on your side (using `transactionId` as a unique key):
  • OnAim may safely retry failed or timed-out requests.
  • Retries will not create duplicate transactions.
  • You may also roll back or reconcile failed attempts safely.
  • If no idempotency mechanism exists:
  • OnAim will send the withdrawal request once.
  • If it fails or times out, the transaction will be marked as failed in Admin.
  • Manual review or reprocessing will be required.
Recommendation: Always implement an idempotent withdrawal endpoint that can detect duplicate `transactionId` values. This enables safe automatic retries and prevents payout duplication.

---

6. Security & Best Practices

  • Always use HTTPS for all communications.
  • Generate OTTs server-side only.
  • Limit OTT validity to 1 minute or less.
  • Always provide timestamps in UTC for consistent analytics.
  • Validate every event payload for the three required fields.
  • Use idempotency keys to prevent duplicates in withdrawals and event ingestion.
  • Implement acknowledgment and retry mechanisms for message broker integrations.
  • Regularly update your schema definition to expose new event fields.
  • Consider adding monitoring dashboards to visualize event flow and error rates.

7. Promotion Page Embedding (Frontend)

Sections 1–6 cover the backend integration (events, player info, withdrawals). This section covers the frontend side: how the Promotion Landing Page is embedded into your site and how it communicates with your page at runtime (login, deposit, balance, resize). It expands on the basic iframe embed in Section 3.

There are two embed shapes, both served from the same renderer. The iframe is the currently recommended integration; the Web Component widget is also supported.

Shape What it is When to use
iframe (recommended) The landing page loaded as a URL inside an <iframe>. Communicates via window.postMessage. Default embed. Broadest host compatibility and cross-origin isolation.
Web Component widget A <onaim-landing-page> custom element loaded from a single JS bundle. Style-isolated (Shadow DOM). Communicates via element methods + DOM events. Embedding directly in your own page: no iframe, no scrollbars, sizes to content automatically.

7.1 iframe embedding (recommended)

Embed the landing page as a URL inside an <iframe>, using the base URL and OTT described in Section 3. At runtime the iframe and your page exchange postMessage events (see 7.3 and 7.4).

<iframe
  id="onaim-landing"
  src="https://[onaim-landing].io/?promotionId=752&lang=ka&landingPageId=574&ott=80b6a17cc251"
  style="width:100%; border:none;">
</iframe>

The iframe posts its content height so you can resize it (7.5). Do not give it a fixed or viewport-relative height in that mode — pass &embedMode=inner in the URL if you want to control the height yourself and disable auto-height posting.

7.2 Web Component widget

Add the widget bundle and drop the element wherever the promotion should appear. OnAim provides the bundle URL and your promotion-id / landing-page-id values — hosting and environment are managed by OnAim, so no additional configuration is required on your side.

<!-- widget bundle (URL provided by OnAim) -->
<script type="module" src="https://<LANDING_HOST>/widget/onaim-landing-page.js"></script>

<!-- the element -->
<onaim-landing-page
  promotion-id="752"
  landing-page-id="574"
  language="ka"
  otp="80b6a17cc251">
</onaim-landing-page>

The widget fetches its own configuration, renders inside its Shadow DOM, and grows to fit its content — no height messaging is required.

Widget attributes

Attribute Required Description
promotion-id Yes Promotion/campaign id (integer).
landing-page-id Yes Landing-page id (integer).
language No Language code, e.g. en, ka.
otp No Player One-Time Token for personalized/authenticated content (see Section 3.2).
enable-signalr No Real-time updates. Enabled unless set to "false".
config No Inline configuration JSON (an array of components, or { components, fonts }). Testing/preview only — normally the widget fetches its own configuration.

Methods and events

Call methods directly on the element, and listen for its DOM events:

const el = document.querySelector('onaim-landing-page');

el.setLanguage('en');               // switch language at runtime
// el.setConfig([...]);             // replace config (preview/testing only)

el.addEventListener('ready', () => console.log('landing ready'));
el.addEventListener('error', (e) => console.error('landing error', e.detail));
Note: in widget mode, player actions such as login / register / deposit are handled inside your page by the renderer's per-deployment configuration, not by the postMessage codes below. The codes in 7.3 apply to the iframe embed.

7.3 Messages FROM the landing page (iframe → host)

Add a single message listener on the parent window. Command messages carry both a numeric code and a string action (match on either); the message field is a human-readable label and must not be used for control flow.

code action Extra Meaning / host action
1001 login Player requested sign-in. Show your login UI; after a successful login, reload the iframe with a fresh OTT.
1002 register Player requested registration. Show your sign-up UI.
1003 height value (px) Content height changed. Resize the iframe (7.5).
1011 openTab value (URL) Open an external link in a new tab.
1012 deposit value (method or null) Player triggered a deposit. Open your deposit/cashier flow.
1031 scrollTo value (Y px) (Legacy, optional) Auto-height embeds: scroll the page so this Y offset aligns to the top of the viewport, to bring a popup into view.

State messages use type (no code):

type Extra Meaning
balance-update amount (number) In-game balance changed. Refresh your balance display.
no-more-balance Player is out of balance. Prompt a deposit.
tournament-play Tournament-over "Play" CTA. Route the player to the next playable surface.
Native app embeds: inside the OnAim native app, the deposit (1012) and a link / redirect command (code 1010, with value = URL) are delivered through window.PromotionChannel instead of postMessage. Pure web hosts only need the postMessage codes above.

Complete listener:

const iframe = document.getElementById('onaim-landing');

window.addEventListener('message', (event) => {
  // Recommended: verify the sender in production.
  // if (event.origin !== 'https://<LANDING_HOST>') return;

  const { type, code, value, amount } = event.data || {};

  switch (code) {
    case 1001: showLoginModal(); break;                       // login
    case 1002: showRegisterModal(); break;                    // register
    case 1003: iframe.style.height = value + 'px'; break;     // height
    case 1011:                                                // openTab
      if (typeof value === 'string' && value.startsWith('https:')) {
        window.open(value, '_blank', 'noopener');
      }
      break;
    case 1012: openDepositFlow(value); break;                 // deposit: 'VISA'|'BANK'|'AMEX'|'APPLE'|null
    case 1031: window.scrollTo({ top: value, behavior: 'smooth' }); break; // scrollTo (legacy)
  }

  switch (type) {
    case 'balance-update':  updateBalance(amount); break;
    case 'no-more-balance': showDepositPrompt();   break;
    case 'tournament-play': routeToNextGame();     break;
  }
});
Security: treat every value URL as untrusted — allow only https: links and open them with noopener. In production, validate event.origin against your landing host before acting on a message.

7.4 Messages TO the landing page (host → iframe)

Post to the iframe's contentWindow:

const iframe = document.getElementById('onaim-landing');
iframe.contentWindow.postMessage({ type: 'SET_LANGUAGE', payload: 'en' }, '*');
type Payload Effect
SET_LANGUAGE string Switch the display language.
SET_LANGUAGES string[] Set the list of available languages.
SET_CONFIG config object/array Replace the rendered component configuration.
SET_GLOBAL_VARIABLES object (CSS variables) ⚠️ Not currently applied in iframe mode. For widget theming, use the element's setGlobalVariables() method instead.

7.5 Height auto-adjustment (iframe)

Unless you pass embedMode=inner, the iframe measures its content and posts a height command (code 1003) whenever it changes (initial load, dynamic content, layout shifts). Apply it so there is no inner scrollbar:

window.addEventListener('message', (event) => {
  const { code, value } = event.data || {};
  if (code === 1003) {
    document.getElementById('onaim-landing').style.height = value + 'px';
  }
});

A CSS transition: height 0.2s on the iframe keeps resizes smooth; debouncing on your side is not required.

Do not give the iframe a viewport-relative height (100vh, %) in auto-height mode: the content grows with the iframe, which grows with the content — an unbounded loop. Let the posted height drive the size, or use embedMode=inner and size it yourself.

7.6 iframe vs widget

Concern iframe (recommended) Web Component widget
Embed <iframe src> <onaim-landing-page> + bundle
Style isolation full document boundary Shadow DOM
Host → landing postMessage (SET_LANGUAGE, SET_CONFIG) element methods (setLanguage, setConfig)
Landing → host postMessage codes (7.3) DOM events (ready, error)
Height height 1003 → you resize (7.5) grows automatically
Targeting promotionId + landingPageId (URL) promotion-id + landing-page-id (attributes)
Player token ott URL parameter otp attribute
Theming not applied via postMessage setGlobalVariables() method
Player actions (login / register / deposit) postMessage codes (7.3) per-deployment client scripts

7.7 Sharing your scroll position with the iframe (host viewport, code 1020)

Recommended for the default auto-height iframe embed. By posting your page's scroll position and viewport size down to the iframe, you let the landing page see where the visible screen is. The landing uses this to place anything that should track the visible viewport rather than the document, including:

  • Centered dialogs / modals — open in the middle of the player's screen instead of the middle of the (tall) landing page.
  • Pinned / sticky components — stay fixed to the top or bottom of the screen while the player scrolls.
  • Any other viewport-relative overlay the landing renders.
Why it's needed: in auto-height mode the iframe is grown to its full content height (7.5), so the iframe itself never scrolls — your page does. Code inside the iframe cannot read your scroll position across origins, so on its own it can only position things relative to the whole (tall) document, not the player's visible screen. The host-viewport message tells it where that visible screen is.

This is optional and safe to skip: without it nothing breaks — dialogs and pinned components fall back to document-relative positioning (a dialog may sit off-screen on a long page; a pinned component scrolls with the page). Implement it to get correct viewport-relative placement in the auto-height embed. The Web Component widget and self-sized iframes (embedMode=inner) already have a real viewport, so they don't need it.

Post your viewport to the iframe

Send a host-viewport message to the iframe's contentWindow on load, on scroll, and on resize:

Field Type Value
action string "host-viewport"
code number 1020
scrollY number Current vertical scroll position of your page, in px (window.scrollY).
innerHeight number Height of the visible viewport, in px (window.innerHeight).
iframeTop number The iframe element's top edge in your document: rect.top + window.scrollY.

All three numeric fields are required and must be finite numbers — a malformed message is ignored and the last good value is kept.

const iframe = document.getElementById('onaim-landing');

function postHostViewport() {
  const rect = iframe.getBoundingClientRect();
  iframe.contentWindow.postMessage({
    action: 'host-viewport',
    code: 1020,
    scrollY: window.scrollY,
    innerHeight: window.innerHeight,
    iframeTop: rect.top + window.scrollY,   // iframe's top in the document
  }, '*');
}

// Post on scroll and resize (passive listeners keep scrolling smooth)…
window.addEventListener('scroll', postHostViewport, { passive: true });
window.addEventListener('resize', postHostViewport);

// …and once when the iframe is ready, so overlays are placed correctly before any scroll.
iframe.addEventListener('load', postHostViewport);

// Optional: the iframe may ask for the current viewport (action
// 'host-viewport-request', code 1021), e.g. when a dialog opens or a pinned
// component mounts. Answer by posting once.
window.addEventListener('message', (event) => {
  if (event.source === iframe.contentWindow && event.data?.code === 1021) {
    postHostViewport();
  }
});
Tip: you can reuse this for any cross-origin overlay alignment, not just pinned components — it tells the landing where your visible screen is. Keep posting on every scroll/resize; the landing throttles the updates to one per animation frame.