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 JavaScriptstringsignature- cryptographic signature (ED25519) of the challenge you've passed during ad request, encoded asbase64string. 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:
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:
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:
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-challengeattribute of the script - if it's empty, it will try to look for
window._asg_challengevalue - 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.
-
Loading and impression coincide for banners, so we emit both events. ↩↩
-
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. ↩
-
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. ↩
-
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. ↩