Skip to content

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',
  },
  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 struct
  • 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.

Types exported by the GAM package (import for type safety):

import type {
  AdRequestPayload,
  StoriesAdRequest,
  ClipsAdRequest,
  ItemInfo,
  Category,
} 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,
            storyId: story.id,
            storyCategories: story.categories,
          });

          // 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,
            clipId: clip.id,
            clipCategories: clip.categories,
          });

          // 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;
  displayTitle: string;
  type: string;
  placement?: { title: string; code: string };
};

type ItemInfo = {
  id: string;
  categories: Category[];
};

type AdRequestPayload = {
  stories?: {
    placement: string;
    categories: string[];
    story: ItemInfo;
  };
  clips?: {
    collection: string;
    clip: ItemInfo;
  };
};

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

Clips requests include:

  • collection: string — collection identifier
  • clip: ItemInfo — metadata about the clip for which the ad is requested

StorytellerAd#

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.
  • 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 the video is 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 is null for an image page, it will default to 5s.
  • trackingPixels: [ClientTrackingPixel] - 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. If no action is required, pass null for this property. This parameter is nullable and does not need to be set for custom implementations.

The ClientTrackingPixel 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: string - enumeration describing the destination of the action - 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)); share (will open share sheet); none (no action)
  • urlOrStoreId: string - string describing the destination. For web types, this should be a valid HTTPS URL. For inApp types, this should be a valid deeplink into the integrating app - when a user presses an action button on an ad we will call userNavigatedToApp on the component and pass this URL for your app to direct the user to the correct destination. For store types, 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 is Learn 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