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
- 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. - Copy the embed code snippet from the same tab into your publisher page.
- Implement a signing URL endpoint on your server (see below), or pass static
mid/ts/sigparams.
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-widthon 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
onCompletecallback 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.