Embed SDK

Embed SDK

The Embed SDK lets publishers embed a RewardedMedia video wall directly on their own pages via a lightweight JavaScript snippet. The video wall loads in a secure iframe — no CSS or JS conflicts with your page.

Setup

  1. In your promotion settings, go to the Embed SDK tab and add each origin (e.g. https://yoursite.com) that is allowed to embed the promotion.
  2. Copy the embed code snippet from the same tab into your publisher page.
  3. Implement a signing URL endpoint on your server (see below), or pass static mid/ts/sig params.

Quick Start (with Signing URL)

The recommended approach is to provide a signingUrl — a URL on your server that returns fresh signing parameters. The SDK calls this endpoint automatically on load and whenever reload() is called, so you never hardcode credentials in your frontend.

<div id="rewarded-container"></div>
<script src="https://rewardedmedia.com/js/rewarded-sdk.js"></script>
<script>
  var rm = new RewardedMedia({
    slug: 'your-promotion-slug',
    container: '#rewarded-container',
    signingUrl: '/api/rewarded-sign',  // your endpoint
    hideCompletionModal: true,         // you control the completion UX
    onReady: function() {
      console.log('Video wall is ready');
    },
    onComplete: function(data) {
      console.log('Earned:', data.points_earned, 'Member:', data.member_id);
      // Show your own success UI, credit the user, etc.

      // Optionally start a new activity:
      // rm.reload();
    },
    onError: function(err) {
      console.error('SDK error:', err.message);
    }
  });
</script>

Quick Start (with Static Params)

If you prefer to generate signing params server-side and inject them into the page template, you can pass them directly:

<div id="rewarded-container"></div>
<script src="https://rewardedmedia.com/js/rewarded-sdk.js"></script>
<script>
  var rm = new RewardedMedia({
    slug: 'your-promotion-slug',
    container: '#rewarded-container',
    mid: 'USER_123',         // server-rendered
    ts: '1709500000',        // server-rendered
    sig: 'hmac_signature',   // server-rendered
    onComplete: function(data) {
      console.log('Earned:', data.points_earned);
    }
  });
</script>

Constructor Options

Option Type Required Description
slug string Yes Your promotion's URL slug
container string | Element Yes CSS selector or DOM element to render the iframe in
signingUrl string No* URL on your server that returns { mid, ts, sig } as JSON. Called on init and on every reload(). * Either signingUrl or static mid/ts/sig should be provided.
mid string No* Member ID — your user's unique identifier (static alternative to signingUrl)
ts string No* Unix timestamp in seconds (static alternative to signingUrl)
sig string No* HMAC signature (static alternative to signingUrl)
hideCompletionModal boolean No When true, suppresses the built-in congrats modal after completion. The onComplete callback still fires so you can show your own UI. Default: false
onReady function No Called when the video wall has initialized. Receives { slug }
onComplete function No Called when the user completes the promotion. Receives { points_earned, member_id, slug }
onResize function No Called when the iframe content height changes. Receives { height }
onError function No Called if the signing URL request fails. Receives { error, message }

Methods

Method Description
reload() Start a new activity. If signingUrl is configured, fetches fresh signing params first. Otherwise reloads with the original static params. Useful for letting the user do another round after completion.
destroy() Remove the iframe and clean up event listeners

Signing URL Endpoint

When you provide a signingUrl, the SDK makes a GET request to that URL (with cookies/credentials for same-origin) and expects a JSON response:

{
  "mid": "USER_123",
  "ts": "1709500000",
  "sig": "a1b2c3d4e5f6..."
}

Your endpoint should authenticate the current user, generate a fresh timestamp, and compute the HMAC signature. This is called once on initialization and again each time reload() is invoked.

Example Signing Endpoints

const crypto = require('crypto');
const express = require('express');
const app = express();

const REWARDED_SECRET = process.env.REWARDED_SECRET;

app.get('/api/rewarded-sign', (req, res) => {
  // Authenticate the user from your session/JWT
  const user = req.user;
  if (!user) return res.status(401).json({ error: 'unauthorized' });

  const mid = user.id.toString();
  const ts = Math.floor(Date.now() / 1000).toString();
  const message = mid + '~' + REWARDED_SECRET + '~' + ts;
  const sig = crypto
    .createHmac('sha256', REWARDED_SECRET)
    .update(message)
    .digest('hex');

  res.json({ mid, ts, sig });
});
<?php
// GET /api/rewarded-sign
session_start();

$secret = getenv('REWARDED_SECRET');
$user_id = $_SESSION['user_id'] ?? null;
if (!$user_id) {
    http_response_code(401);
    echo json_encode(['error' => 'unauthorized']);
    exit;
}

$mid = (string) $user_id;
$ts = (string) time();
$message = $mid . '~' . $secret . '~' . $ts;
$sig = hash_hmac('sha256', $message, $secret);

header('Content-Type: application/json');
echo json_encode(['mid' => $mid, 'ts' => $ts, 'sig' => $sig]);
import hmac, hashlib, time, os, json
from flask import Flask, session, jsonify, abort

app = Flask(__name__)
REWARDED_SECRET = os.environ['REWARDED_SECRET']

@app.route('/api/rewarded-sign')
def rewarded_sign():
    user_id = session.get('user_id')
    if not user_id:
        abort(401)

    mid = str(user_id)
    ts = str(int(time.time()))
    message = f'{mid}~{REWARDED_SECRET}~{ts}'
    sig = hmac.new(
        REWARDED_SECRET.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()

    return jsonify(mid=mid, ts=ts, sig=sig)
# app/controllers/api/rewarded_controller.rb
class Api::RewardedController < ApplicationController
  before_action :authenticate_user!

  def sign
    mid = current_user.id.to_s
    ts = Time.now.to_i.to_s
    secret = ENV['REWARDED_SECRET']
    message = "#{mid}~#{secret}~#{ts}"
    sig = OpenSSL::HMAC.hexdigest('sha256', secret, message)

    render json: { mid: mid, ts: ts, sig: sig }
  end
end

# config/routes.rb
get '/api/rewarded-sign', to: 'api/rewarded#sign'

Full Integration Example

This example shows a typical publisher integration: the signing URL provides credentials, the completion modal is hidden so the publisher controls the UX, and reload() lets the user do another round.

<div id="rewarded-container" style="max-width: 640px; margin: 0 auto;"></div>
<div id="reward-banner" style="display: none; padding: 16px; background: #d4edda; text-align: center;">
  <span id="reward-message"></span>
  <button id="play-again" style="margin-left: 12px;">Play Again</button>
</div>

<script src="https://rewardedmedia.com/js/rewarded-sdk.js"></script>
<script>
  var rm = new RewardedMedia({
    slug: 'my-promotion',
    container: '#rewarded-container',
    signingUrl: '/api/rewarded-sign',
    hideCompletionModal: true,
    onComplete: function(data) {
      // Show your own completion UI
      document.getElementById('reward-message').textContent =
        'You earned ' + data.points_earned + ' points!';
      document.getElementById('reward-banner').style.display = 'block';
    },
    onError: function(err) {
      alert('Could not load the activity: ' + err.message);
    }
  });

  // "Play Again" reloads with fresh signing params
  document.getElementById('play-again').addEventListener('click', function() {
    document.getElementById('reward-banner').style.display = 'none';
    rm.reload();
  });
</script>

Sizing & Styling

  • The iframe automatically resizes its height to fit the video wall content.
  • Width is always 100% of the container element.
  • For best results, set a max-width on the container (e.g. max-width: 640px).
  • The minimum height is 400px.

Event Lifecycle

Event When Data
onReady Iframe loaded, signing verified (if applicable) { slug }
onComplete User finished all required videos (+ survey if configured) { slug, member_id, points_earned }
onResize Iframe content height changed { height }
onError Signing URL fetch failed { error, message }

Security

  • Only origins added to the promotion's allowed origins list can embed the video wall.
  • The iframe uses sandbox="allow-scripts allow-same-origin allow-popups allow-forms" to restrict capabilities.
  • The SDK validates the origin of all postMessage events.
  • Reward delivery is server-to-server via webhooks — the client-side onComplete callback is for UI updates only and should not be trusted for crediting users.
  • When using signingUrl, signing params never appear in your page source — they are fetched at runtime via an authenticated request to your own server.