Skip to content

Events and signing

Our advertising scripts are now emitting JavaScript events, allowing you to perform actions when the visitor is ready to interact with an ad or have already interacted with it. Further more, these events may be cryptographically signed. It gives you proof that no third-party messed up your ad setup by pretending that the ad has been displayed.

Ad events

We emit events via standard JavaScript document.dispatchEvent call.

Each event name consists of asg prefix, followed by capitalized ad type (or Spot if you don't care about the specific type), and the event itself - Loaded for events marking an ad ready for iteraction, or Impression if the interaction that should be paid by ad network is completed.

This may sound complicated, so here's a table of all emittable events:

Ad type Event name When it is emitted
Any asgSpotLoaded The ad to be displayed in this spot has been selected,
and it is ready to be interacted with
asgSpotImpression The visitor has interacted with the ad,
and it should be paid by ad network
JS Banner asgBannerLoaded Banner code has been injected into the page 1
asgBannerImpression Banner code has been injected into the page1
asgBannerInViewport Banner container has become visible in the viewport
(for example, the visitor scrolled to the bottom of the page
with a footer banner).
Code Popunder2 asgPopunderLoaded When we have selected the ad to display
and the data for display has been sent to the browser.
asgPopunderImpression When the pages opens a new window.
We do not know when exactly this happens
for code popunders, so any open window counts here.
Popunder asgPopunderLoaded When we have selected the ad to display
and the data for display has been sent to the browser.
asgPopunderImpression Just before our scripts opens a new tab or window
with popunder's URL
Interstitial asgInterstitialLoaded When we have selected the ad to display
and the data for display has been sent to the browser.
asgInterstitialImpression When the interstitial overlay is open
IM asgImLoaded When we have selected the ad to display
and the data for display has been sent to the browser.
asgImImpression When the JS code from the IM creative has been injected into the page.
In-Page Push asgPushLoaded The response with data for display has arrived from our servers.
asgPushImpression JS-code of the push has been injected into the page.3
In-Video VAST4 asgInVideoLoaded We have recieved all we need to display VAST from our servers.
asgInVideoImpression The first frame of the VAST ad has been displayed.
Slider asgSliderLoaded Data for slider display has arrived from our servers.
asgSliderImpression The first frame of the video that is displayed by slider has been played.
Outstream asgOutstreamLoaded We've received data to display the Outstream ad.
asgOutstreamImpression Outstream has become visible to the visitor.

Event payload

Each event has a detail field, that contains two keys:

  • spotId - the ID of the spot that emitted the event, as a JavaScript string
  • signature - cryptographic signature (ED25519) of the challenge you've passed during ad request, encoded as base64 string. It will be an empty string if no challenge has been passed.

Event signature

If you give us a challenge (any string), we will sign it with our public key using ED25519 algorithm:

l+6zjyE2OjpFVezz5j4XXLLycvWTtED62HYR4IqVTKQ=

For example, for the string foo the signature will be Lf7jOfMMrRPk4DgFEzCufxHl3aZx/gU2VhNlLYXTEDH9fWP51rhc3XCteo4bDOAq0hlM5AKtuwmrgeK7cuTwAQ==

There are three ways to pass a challenge for us to sign. You may choose any, or even mix and match them.

meta tag

Add a meta tag into the head of your page with name attribute set to asg-challenge, and provide the value of the challenge inside the content attribute of this tag. For example:

<meta name="asg-challenge" content="foo">

Global JS value

Set challenge into window._asg_challenge variable. Do this before including our scripts - otherwise our code won't see it! For example:

window._asg_challenge = 'foo'

data-challenge attribute of the script tag

Provide the challenge inside data-challenge attribute of the script tag that injects our codes. For example:

<script type="text/javascript"
        data-tag="asg"
        src="//cdn.tapioni.com/asg_embed.js"
        data-spots="183163" 
        data-challenge="foo">
</script>

If you use several of these ways, the priority is this:

  • first, our script will look into data-challenge attribute of the script
  • if it's empty, it will try to look for window._asg_challenge value
  • if it is empty as well, meta name="asg-challenge" will be used.

If no challenge found, no challenge will be sent, and the signature in the events will be empty.

Validating signature

An example of validating the signature of the challenge in JavaScript is like this:

const PUBLIC_KEY_B64 = 'l+6zjyE2OjpFVezz5j4XXLLycvWTtED62HYR4IqVTKQ=';

// Helper function to convert base64 string to bytes
const base64ToBytes = (b64) => {
  const bin = atob(b64);
  const len = bin.length;
  const bytes = new Uint8Array(len);
  for (let i = 0; i < len; i += 1) {
    bytes[i] = bin.charCodeAt(i);
  }
  return bytes;
};

const verifySignature = async (message, signatureB64) => {
  try {
    if (!signatureB64) return { status: "missing" };
    const pubKeyBytes = base64ToBytes(PUBLIC_KEY_B64);
    const sigBytes = base64ToBytes(signatureB64);
    const key = await crypto.subtle.importKey(
      "raw",
      pubKeyBytes,
      { name: "Ed25519" },
      false,
      ["verify"],
    );
    const ok = await crypto.subtle.verify(
      "Ed25519",
      key,
      sigBytes,
      new TextEncoder().encode(message),
    );
    return { status: ok ? "valid" : "invalid" };
  } catch (error) {
    return { status: "error", error: error.message || String(error) };
  }
};

Example of usage:

console.log(
  await verifySignature(
    "foo",
    "Lf7jOfMMrRPk4DgFEzCufxHl3aZx/gU2VhNlLYXTEDH9fWP51rhc3XCteo4bDOAq0hlM5AKtuwmrgeK7cuTwAQ=="
  )
); // valid

console.log(
  await verifySignature(
    "bar",
    "Lf7jOfMMrRPk4DgFEzCufxHl3aZx/gU2VhNlLYXTEDH9fWP51rhc3XCteo4bDOAq0hlM5AKtuwmrgeK7cuTwAQ=="
  )
); // invalid

Tips for challenge

It is impossible to guess the signature for the given challenge. However, the signature will always be the same for the same challege. Please, provide different challenge each time you need to verify the signature.


  1. Loading and impression coincide for banners, so we emit both events. 

  2. These are popunders that inject ad network's code to open the popunder. A rare kind nowadays. Most likely you don't have them, but they are here for backward compatibility. 

  3. Third-party JS codes for Pushes may hide actual push until scroll or any other iteraction. We can't know beforehand. So we emit impression event right after their code has been injected into the page. 

  4. In-Video spots emit events only if you use our script to display them. No events will be emitted if you use direct VAST link to integrate with your player.