Ads#
The Storyteller SDK supports displaying ads that can be created in the Storyteller CMS (First Party Ads), as well as Ads from Google Ad Manager via an SDK extension developed by Storyteller and Ads from other sources via custom implementation provided by the integrator.
Which source of ads is used can be configured on your behalf by a member of the Storyteller Delivery Team.
Storyteller First Party Ads#
If your tenant is configured to use Storyteller First Party Ads, which can be managed in the Storyteller CMS, then no changes to the Storyteller integration code are necessary. The Ads code is managed entirely within the Storyteller SDK.
Storyteller GAM Ads#
To use Ads from Google Ad Manager in Storyteller, first reach out to your Storyteller contact and they will assist you with setting up Google Ad Manager to traffic ads to Storyteller.
Required Platform Configuration#
IMPORTANT: Before using the Storyteller GAM Ads package, you must complete the following platform-specific configuration steps:
iOS Configuration (Required)#
Update your Info.plist file following Google's instructions: AdMob iOS Quick Start - Update Info.plist
Android Configuration (Required)#
Update your AndroidManifest.xml file following Google's instructions: AdMob Android Quick Start - Import Mobile Ads SDK
Package Installation and Setup#
Once the platform configuration is complete, you can use the Storyteller GAM Ads package to fetch ads from Google Ad Manager.
To initialize GAM package, call setupGAMModule method:
import StorytellerGamSdk, {
setupGAMModule,
getAdUnitForRequest,
setAdUnitResult,
type AdRequestPayload,
} from '@getstoryteller/react-native-storyteller-sdk-gam';
// Initialize the GAM module with your configuration
setupGAMModule({
customNativeTemplateId: {
stories: 'YOUR_STORIES_TEMPLATE_ID',
clips: 'YOUR_CLIPS_TEMPLATE_ID',
},
bottomBannerAdUnit: '/YOUR_NETWORK_CODE/clips-bottom-banner-ad-unit',
customKvps: {
custom_key: 'custom_value',
},
});
Configuration parameters are explained below:
customNativeTemplateId- If you have worked with the Storyteller Delivery team to setup Custom Native Ads, you will need to supply their IDs here. If you are only using Stories (but not Clips) it is only necessary to supply one property of this structbottomBannerAdUnit- Optional GAM ad unit ID used for Clips bottom banner ads. Provide this when your tenant or collection is configured to show Clips bottom banner ads.customKvps- The Storyteller GAM SDK passes a default set of KVPs to GAM to allow targeting based on the content of the Stories/Clips the user is viewing. If you have any additional parameters that you need to be able to target by, these should be passed here. Note that the SDK will not inherit any KVPs being set in the rest of your app.
Storyteller AdMob Ads#
The GAM package also exposes AdMob setup for apps that use AdMob instead of Google Ad Manager. Only one Google ads module should be active at a time; call either setupGAMModule or setupAdMobModule.
import { setupAdMobModule } from '@getstoryteller/react-native-storyteller-sdk-gam';
setupAdMobModule({
nativeAdUnit: 'ca-app-pub-xxx/native',
bannerAdUnit: 'ca-app-pub-xxx/banner-fallback',
bottomBannerAdUnit: 'ca-app-pub-xxx/clips-bottom-banner',
enableBannerAdPriority: false,
customKvps: {
custom_key: 'custom_value',
},
});
Configuration parameters are explained below:
nativeAdUnit- Required AdMob ad unit ID used for native ads.bannerAdUnit- Optional AdMob banner ad unit ID used as a fallback for full-screen placements.bottomBannerAdUnit- Optional AdMob banner ad unit ID used for Clips bottom banner ads.enableBannerAdPriority- Optional boolean. Whentrue, banner ads are attempted before native ads whenbannerAdUnitis configured.customKvps- Optional key-value pairs passed to AdMob as network extras when ad tracking is enabled.
Types exported by the GAM package (import for type safety):
import type {
AdRequestPayload,
StoriesAdRequest,
ClipsAdRequest,
ItemInfo,
Category,
SetupGAMConfiguration,
SetupAdMobConfiguration,
} from '@getstoryteller/react-native-storyteller-sdk-gam';
Necessary part of the GAM Ads setup is implementing a listener that sets the ID of the Ad Unit in Google Ad Manager that will be used to serve the Storyteller Ads for the specific Ad request. Each ad request includes a unique requestId that must be returned with your response.
Basic Example#
import { useEffect } from 'react';
import type { EventSubscription } from 'react-native';
import StorytellerGamSdk, {
getAdUnitForRequest,
setAdUnitResult,
type AdRequestPayload,
} from '@getstoryteller/react-native-storyteller-sdk-gam';
const STORIES_AD_UNIT = '/YOUR-CONFIGURATION';
const CLIPS_AD_UNIT = '/YOUR-CONFIGURATION';
function MyComponent() {
useEffect(() => {
const subscription: EventSubscription = getAdUnitForRequest(
({ requestId, adRequest }: { requestId: string; adRequest: AdRequestPayload }) => {
// Determine which ad unit to use based on the request type
let adUnitId = '';
if (adRequest.stories) {
adUnitId = STORIES_AD_UNIT;
} else if (adRequest.clips) {
adUnitId = CLIPS_AD_UNIT;
}
// Return the ad unit ID with the matching requestId
setAdUnitResult({ requestId, adUnitId });
}
);
return () => subscription.remove();
}, []);
// ... rest of your component
}
const { GET_AD_UNIT_FOR_REQUEST } = StorytellerGAMAds.getConstants();
constructor(props: DemoAppProps) {
const storytellerEvent = new NativeEventEmitter(StorytellerGAMAds);
storytellerEvent.addListener(GET_AD_UNIT_FOR_REQUEST, this._onAdUnitRequested);
}
_onAdUnitRequested = (event: AdUnitRequest) => {
if(event.adRequest.stories) {
StorytellerGAMAds.setAdUnitResult({ adUnitId: '[storiesAdUnitId]' });
} else if (event.adRequest.clips) {
StorytellerGAMAds.setAdUnitResult({ adUnitId: '[clipsAdUnitId]' });
}
};
Advanced Example with Request Details#
You can access detailed information about the ad request to make more sophisticated decisions:
import { useEffect } from 'react';
import type { EventSubscription } from 'react-native';
import StorytellerGamSdk, {
getAdUnitForRequest,
setAdUnitResult,
type AdRequestPayload,
} from '@getstoryteller/react-native-storyteller-sdk-gam';
function MyComponent() {
useEffect(() => {
const subscription: EventSubscription = getAdUnitForRequest(
({ requestId, adRequest }: { requestId: string; adRequest: AdRequestPayload }) => {
let adUnitId = '';
if (adRequest.stories) {
const { placement, categories, story } = adRequest.stories;
console.log('Stories ad request:', {
placement,
categories,
storyCategories: story.categories,
adIndex: adRequest.stories.adIndex,
});
// Use different ad units based on placement or categories
adUnitId = '/YOUR_NETWORK_CODE/stories-native-ad-unit';
} else if (adRequest.clips) {
const { collection, clip } = adRequest.clips;
console.log('Clips ad request:', {
collection,
clipCategories: clip.categories,
nextClipCategories: adRequest.clips.nextClip?.categories,
adIndex: adRequest.clips.adIndex,
});
// Use different ad units based on collection or categories
adUnitId = '/YOUR_NETWORK_CODE/clips-native-ad-unit';
}
setAdUnitResult({ requestId, adUnitId });
}
);
return () => subscription.remove();
}, []);
// ... rest of your component
}
AdRequestPayload#
The AdRequestPayload object contains metadata about the ad request, including either stories or clips information.
// See package exports for exact type definitions
// Shown here for clarity only
type Category = {
name: string;
externalId: string;
};
type ItemInfo = {
categories: Category[];
};
type AdRequestPayload = {
stories?: {
placement: string;
categories: string[];
story: ItemInfo;
adIndex: number;
};
clips?: {
collection: string;
clip: ItemInfo;
nextClip?: ItemInfo;
adIndex: number;
};
};
Stories requests include:
- placement: string — uniquely identifies where Stories are shown
- categories: string[] — categories assigned to the stories list
- story: ItemInfo — metadata about the story after which the ad will be placed
- adIndex: number — ad position supplied by the native integration
Clips requests include:
- collection: string — collection identifier
- clip: ItemInfo — metadata about the clip for which the ad is requested
- nextClip: ItemInfo | undefined — metadata for the next clip when supplied by the native integration
- adIndex: number — ad position supplied by the native integration
Storyteller VAST Ads#
The VAST package exposes the native Storyteller VAST integration for apps that need VAST-backed full-screen Story or Clip ads. Install it alongside the core SDK:
npm install @getstoryteller/react-native-storyteller-sdk
npm install @getstoryteller/react-native-storyteller-sdk-vast
The VAST package exports its configuration, callback, diagnostics, URL-format, and request payload types:
import type {
SetupVASTConfiguration,
SetupGAMVASTConfiguration,
SetVASTRequestParametersResult,
VASTDiagnosticsEvent,
VASTURLFormat,
AdRequestPayload,
StoriesAdRequest,
ClipsAdRequest,
ItemInfo,
Category,
} from '@getstoryteller/react-native-storyteller-sdk-vast';
VASTURLFormat is 'pathSegment' | 'queryString'. SetupVASTConfiguration contains baseUrl, optional requestParameters, urlFormat, enableDebugLogging, and usesRequestParametersCallback. SetupGAMVASTConfiguration contains adUnit, descriptionUrl, and optional contentUrl, customParams, tagParameters, and enableDebugLogging.
Generic VAST#
Use setupVASTModule when your app already has a VAST tag endpoint and can provide request parameters directly.
import StorytellerVastSdk from '@getstoryteller/react-native-storyteller-sdk-vast';
StorytellerVastSdk.setupVASTModule({
baseUrl: 'https://pubads.g.doubleclick.net/gampad/ads',
urlFormat: 'queryString',
requestParameters: {
iu: '/21775744923/external/single_preroll_skippable',
sz: '640x480',
gdfp_req: '1',
output: 'vast',
unviewed_position_start: '1',
env: 'vp',
},
});
Configuration parameters:
baseUrl- Required HTTPS VAST tag endpoint.requestParameters- Optional static key-value parameters appended to each VAST request.urlFormat- Optional serialization mode. UsequeryStringfor normal?key=valuequery parameters orpathSegmentfor path-segment serialization. Defaults topathSegment.enableDebugLogging- Optional Android-only verbose VAST logging flag.usesRequestParametersCallback- Optional. Whentrue, native code emitsgetVASTRequestParametersForRequestand waits up to one second forsetVASTRequestParametersResult; if the response is late, the request continues without dynamic parameters.
The final VAST tag URL must be HTTPS and no longer than 2,048 characters after parameters are applied.
When usesRequestParametersCallback is enabled, subscribe before presenting Storyteller content and return a result with the same requestId:
import StorytellerVastSdk from '@getstoryteller/react-native-storyteller-sdk-vast';
const subscription = StorytellerVastSdk.getVASTRequestParametersForRequest(
({ requestId, adRequest }) => {
StorytellerVastSdk.setVASTRequestParametersResult({
requestId,
requestParameters: {
content_type: adRequest.stories ? 'story' : 'clip',
},
});
}
);
// Later:
subscription.remove();
SetVASTRequestParametersResult contains requestId and optional requestParameters. Responses received after the native timeout are ignored for that request.
GAM VAST#
Use setupGAMVASTModule when you want the SDK to build Google Ad Manager VAST tag URLs without integrating Google Mobile Ads or IMA.
import StorytellerVastSdk from '@getstoryteller/react-native-storyteller-sdk-vast';
StorytellerVastSdk.setupGAMVASTModule({
adUnit: '/21775744923/external/single_preroll_skippable',
descriptionUrl: 'https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags',
tagParameters: {
sz: '640x480',
unviewed_position_start: '1',
},
});
Configuration parameters:
adUnit- Required GAM ad unit path serialized asiu.descriptionUrl- Required HTTPS page or content URL serialized asdescription_url.contentUrl- Optional HTTPS content URL serialized asurl.customParams- Optional custom targeting encoded intocust_params.tagParameters- Optional top-level GAM VAST tag parameters applied after SDK defaults.enableDebugLogging- Optional Android-only verbose VAST logging flag.
VAST Diagnostics#
The VAST package emits diagnostics separately from user activity analytics. Subscribe while testing or when collecting support data:
import StorytellerVastSdk from '@getstoryteller/react-native-storyteller-sdk-vast';
const subscription = StorytellerVastSdk.onVASTDiagnostics(event => {
console.log('VAST diagnostic', event.type, event);
});
// Later:
subscription.remove();
Event type values include requestStarted, requestCompleted, requestFailed, parseFailed, wrapperResolved, mappingFailed, and mediaSelectionFailed. Some fields are platform-specific; handle optional fields defensively.
VASTDiagnosticsEvent exports the complete optional diagnostics payload: slotId, baseUrl, urlFormat, parameterCount, totalMs, vastVersion, adCount, wrapperDepth, errorType, errorCode, httpStatus, cause, availableMimes, candidateCount, and message.
VAST modules support full-screen Story and Clip ads. Clips bottom banner ads are not part of the VAST protocol surface and are not supported by the VAST package.
Custom Ads#
If your tenant is configured for client-supplied ads, subscribe to StorytellerSdk.getAdsForList and complete each native request with either completeAdRequest(ad) or failAdRequest(error).
import { useEffect } from 'react';
import StorytellerSdk, { type StorytellerAd } from '@getstoryteller/react-native-storyteller-sdk';
function MyComponent() {
useEffect(() => {
const subscription = StorytellerSdk.getAdsForList(async (adRequest) => {
try {
const ad: StorytellerAd = await loadAdForRequest(adRequest);
StorytellerSdk.completeAdRequest(ad);
} catch (error) {
StorytellerSdk.failAdRequest(error instanceof Error ? error.message : 'No ad available');
}
});
return () => subscription.remove();
}, []);
}
StorytellerAd#
For type-safe action destinations, import the exported StorytellerAdActionType union or the runtime StorytellerAdActionKind enum:
import {
StorytellerAdActionKind,
type StorytellerAd,
type StorytellerAdActionType,
} from '@getstoryteller/react-native-storyteller-sdk';
const actionType: StorytellerAdActionType = 'web';
const runtimeActionType = StorytellerAdActionKind.web;
The StorytellerAd object which the SDK expects to be returned contains the following properties:
id: string- a unique ID for the Ad in question.advertiserName: string- the name of the advertiser - used in place of the Story title on the Ad Page.advertiserDescription?: string- optional advertiser description. This is consumed by iOS; the linked Android SDK image/video ad factories do not expose this field.advertiserLogoURL?: string- optional advertiser logo URL. This is consumed by iOS; the linked Android SDK image/video ad factories do not expose this field.image?: string- the image to display for the ad.video?: string- the video to display for the ad. Note that if both an image and a video are supplied, then thevideois preferred.playcardUrl?: string- the image to display as a placeholder if a video asset is still loading. It could be set, for example, as the first frame of the video. This property is unused for image ads.duration?: number- how long the ad should be displayed for. Note that for videos, this parameter will be ignored and the ad will be displayed for the length of the video. If this parameter isnullfor animagepage, it will default to 5s.trackingPixels: StorytellerAdTrackingPixel[]- an array of 1x1 ad tracking pixels which will be triggered when an ad is loaded and throughout its playback - properties detailed below.action?: StorytellerAdAction- optional property which describes what should happen when a user presses an action button on the ad. Omit this property when no action is required.adFormat: string- format identifier. This is required by iOS; the linked Android SDK image/video ad factories always create custom native ads.responseIdentifier?: string- optional response identifier. This is consumed by iOS; the linked Android SDK image/video ad factories do not expose this field.
The StorytellerAdTrackingPixel object contains the following properties:
eventType: string- string describing the type of the tracking pixel - possible values are "impression", "videoStart", "firstQuartile", "midpoint", "thirdQuartile", "videoComplete", "videoPause", "videoResume".url: string- the tracking pixel URL.
The StorytellerAdAction object contains the following properties:
type: StorytellerAdActionType- action destination. Possible values are:web(will direct the user to an in-app browser);inApp(will direct the user to a location within the integrating app);store(will direct the user to the App Store);externalApp(will direct the user to another App if the user has it installed)urlOrStoreId: string- string describing the destination. Forwebtypes, this should be a valid HTTPS URL. ForinApptypes, this should be a valid deeplink into the integrating app - when a user presses an action button on an ad we will calluserNavigatedToAppon the component and pass this URL for your app to direct the user to the correct destination. Forstoretypes, this should be the URL of the App you wish to link to on the App Store (e.g. https://apps.apple.com/us/app/testflight/id899247664)text?: string- string describing text on the bottom of the Story or Clip that suggests the available action. Default value isLearn More.
Ad Encoding#
Any video ads passed to the Storyteller SDK should be encoded according to the following specs:
- Width: 540
- Height: 960
- Bitrate: 1500 kbps
- MaxBitrate - 1500 kbps
- FrameRate - 30 fps
- Audio: AAC 128 kbps bitrate, 48k sampling