Skip to main content
Version: 4.2.0

Integration Guide

Complete guide to integrating the BidMachine Plus Unity plugin.

BidMachine Plus Unity Plugin

BidMachine Plus exposes two demand paths through one API:

ModeDescription
AdNetworkBidMachine runs as a header-bidding demand source — on its own, or plugged into a third-party mediation
MediationBidMachine acts as the mediation platform itself, running the auction across your demand

The same ad unit classes — BannerAd, InterstitialAd, RewardedAd — work in both modes. The active mode is selected at initialization.

This page covers plugin installation, initialization, and ad loading. Two installation paths are available: automated via AI coding agents, or manual setup via Unity Package Manager.

Automated Integration with AI Coding Agents

The BidMachine Plus SDK Agents bundle ships a portable integration skill for Claude Code, Codex, and Gemini. Install it for your runtime:

/plugin marketplace add bidmachine/bidmachine-sdk-agents
/plugin install bidmachine-sdk-agents@bidmachine

Then ask your agent to add BidMachine Plus as a First Look in front of your existing mediation — for example:

Prompt
Add BidMachine Plus as a First Look in front of my existing ad mediation in this Unity game.

The bundled skill drives the integration: it installs the plugin, initializes BidMachine in Mediation mode, and wires BidMachine Plus as the primary source for interstitial and rewarded — requested first on a timeout — with fallback to your existing mediation (MAX, AdMob, LevelPlay, or another) on a no-fill, preserving your current setup.

First Look covers interstitial and rewarded only. For banners, use the manual setup below — the plugin supports them, but they aren't part of the First Look flow.

Manual Installation

Requires Unity 2021.3+, Android SDK 23+, iOS 13.0+, and Xcode 16.4+.

Unity Package Manager

BidMachine Plus is distributed through the BidMachine scoped registry. Register it, then install com.bidmachine.plus. The External Dependency Manager is pulled in automatically as a package dependency — no separate import is needed.

  1. Open Edit → Project Settings → Package Manager.
  2. Under Scoped Registries click + and fill in:
    • Name: BidMachine Registry
    • URL: https://npm.bidmachine.com
    • Scope(s): com.bidmachine
  3. Click Apply.
  4. Open Window → Package Manager, switch the source to My Registries, select BidMachine Plus, and click Install.

Android Configuration

In Player Settings → Publishing Settings, enable Custom Main Gradle Template and Custom Gradle Settings Template.

In Assets → External Dependency Manager → Android Resolver → Settings, enable Patch mainTemplate.gradle, Copy and patch settingsTemplate.gradle from 2022.2, and Use Jetifier. Then run Android Resolver → Resolve.

Required permissions in AndroidManifest.xml:

Plugins/Android/AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

Optional permissions:

Plugins/Android/AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />

iOS Configuration

In Assets → External Dependency Manager → iOS Resolver → Settings, disable Link frameworks statically.

Add to the exported Xcode project's Info.plist:

Info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

<key>SKAdNetworkItems</key>
<array>
<!-- full SKAdNetwork list is published with the SDK -->
</array>

<key>NSUserTrackingUsageDescription</key>
<string>$(APP_NAME) needs your advertising identifier to deliver personalized ads.</string>

Initialization

Call BidMachine.Initialize once at app startup. Pass the desired mode to select the demand path.

BidMachine.GetInstance returns the SDK instance — keep a reference; you'll pass it to every ad unit constructor.

BidMachine Plus initializes in one of two modes, selected by the IntegrationMode you pass to the builder:

AdNetwork Mode

BidMachine runs as a header-bidding ad network. When it plugs into a third-party mediation, name that platform with WithMediator so it can compete in the waterfall.

var sdk = BidMachine.GetInstance("YOUR_APP_KEY");

var config = new InitializationConfigBuilder(IntegrationMode.AdNetwork)
.WithMediator(MediationProvider.AdMob) // the mediation BidMachine plugs into — or MediationProvider.LevelPlay, MediationProvider.Max, a custom string
.WithLoggingEnabled(true) // remove before release
.WithTestModeEnabled(true) // remove before release
.Build();

sdk.Initialized += (sender, args) =>
{
if (args.Error != null) Debug.LogError($"init failed: {args.Error.Message}");
};
sdk.Initialize(config);

Mediation Mode

BidMachine acts as the mediation platform itself, running the auction across your demand. Pass IntegrationMode.Mediation — there's no third-party mediator to declare:

var config = new InitializationConfigBuilder(IntegrationMode.Mediation)
.WithLoggingEnabled(true) // remove before release
.WithTestModeEnabled(true) // remove before release
.Build();

var sdk = BidMachine.GetInstance("YOUR_APP_KEY");
sdk.Initialized += (sender, args) => { /* ready */ };
sdk.Initialize(config);

In both modes the ad unit classes expose NotifyWin() and NotifyLoss(winnerEcpm, networkName) for reporting the outcome of a waterfall round — see Mediation.

Running the auction on your own server instead? See S2S Bidding for the bid-token flow and loading with a bidPayload.

Ad Integration

All ad lifecycle events deliver an AdInfo describing the winning ad (via EventArgs.AdInfo):

PropertyTypeDescription
PlacementIdstringPlacement ID from the BidMachine dashboard
PricedoubleeCPM ÷ 1000 (e.g. 0.005 = $5 CPM)
PrecisionRevenuePrecisionOne of Estimated, Exact, Unknown
RawDataIReadOnlyDictionary<string, string>Network metadata. Known keys: networkName, dsp, ecpm

Read the winning network via e.AdInfo.RawData["networkName"], and the per-impression revenue via e.AdInfo.Price.

e.AdInfo.Precision describes how reliable that price is:

ValueMeaning
ExactReal-time auction price — trust for reporting
EstimatedHistorical-data estimate — treat as approximate
UnknownConfidence cannot be determined

All ad events fire on the Unity main thread, so you can update UI and call MonoBehaviour APIs from them directly.

Interstitial Ads

Full-screen ads. Create once, load, then call Show when ready. Reload from Closed to keep an ad ready for the next show.

AdsController.cs
public class AdsController : MonoBehaviour
{
private InterstitialAd interstitialAd;

private void CreateInterstitialAd()
{
interstitialAd = new InterstitialAd(sdk, placementId: "YOUR_PLACEMENT_ID");

interstitialAd.Loaded += (s, e) => Debug.Log($"Interstitial loaded from {e.AdInfo.RawData["networkName"]}");
interstitialAd.LoadFailed += (s, e) => Debug.LogError($"Interstitial load failed: {e.Error.Message}");
interstitialAd.Shown += (s, e) => Debug.Log("Interstitial displayed");
interstitialAd.ShowFailed += (s, e) => Debug.LogError($"Interstitial show failed: {e.Error.Message}");
interstitialAd.Clicked += (s, e) => Debug.Log("Interstitial clicked");
interstitialAd.Closed += (s, e) => interstitialAd.Load(); // reload for next show
interstitialAd.Expired += (s, e) => Debug.LogWarning("Interstitial expired before show");
interstitialAd.RevenuePaid += (s, e) => Debug.Log($"Interstitial revenue: {e.AdInfo.Price} from {e.AdInfo.RawData["networkName"]}");

interstitialAd.Load();
}

private void ShowInterstitialAd()
{
if (interstitialAd.IsLoaded)
{
interstitialAd.Show();
}
else
{
Debug.LogWarning("Interstitial ad not ready yet");
}
}

private void OnDestroy()
{
interstitialAd?.Dispose();
}
}
CallbackDescription
LoadedAd loaded and ready to show
LoadFailedAd failed to load
ShownAd is displayed full-screen
ShowFailedAd failed to display
ClickedUser tapped the ad
ClosedUser dismissed the ad
ExpiredAd expired before being shown
RevenuePaidBillable impression recorded

Rewarded Ads

Same lifecycle as interstitial, plus a UserQualifiedForReward callback when the user completes the ad and earns a reward.

AdsController.cs
public class AdsController : MonoBehaviour
{
private RewardedAd rewardedAd;

private void CreateRewardedAd()
{
rewardedAd = new RewardedAd(sdk, placementId: "YOUR_PLACEMENT_ID");

rewardedAd.Loaded += (s, e) => Debug.Log($"Rewarded loaded from {e.AdInfo.RawData["networkName"]}");
rewardedAd.UserQualifiedForReward += (s, e) =>
{
Debug.Log($"Reward earned: {e.Reward?.Amount} {e.Reward?.Label}");
// grant reward to the user
};
rewardedAd.LoadFailed += (s, e) => Debug.LogError($"Rewarded load failed: {e.Error.Message}");
rewardedAd.Shown += (s, e) => { };
rewardedAd.ShowFailed += (s, e) => { };
rewardedAd.Clicked += (s, e) => { };
rewardedAd.Closed += (s, e) => rewardedAd.Load(); // reload for next show
rewardedAd.Expired += (s, e) => { };
rewardedAd.RevenuePaid += (s, e) => Debug.Log($"Rewarded revenue: {e.AdInfo.Price} from {e.AdInfo.RawData["networkName"]}");

rewardedAd.Load();
}

private void ShowRewardedAd()
{
if (rewardedAd.IsLoaded)
{
rewardedAd.Show();
}
else
{
Debug.LogWarning("Rewarded ad not ready yet");
}
}

private void OnDestroy()
{
rewardedAd?.Dispose();
}
}
CallbackDescription
LoadedAd loaded and ready to show
UserQualifiedForRewardUser completed the ad, grant reward
LoadFailedAd failed to load
ShownAd is displayed full-screen
ShowFailedAd failed to display
ClickedUser tapped the ad
ClosedUser dismissed the ad
ExpiredAd expired before being shown
RevenuePaidBillable impression recorded

The UserQualifiedForReward args expose Reward (may be null if reward metadata is not supplied), which carries Label (string) and Amount (int).

BannerAd displays as an overlay anchored to a screen edge. Load it, then call Show(position) from the Loaded handler; call Hide() to remove it. It is single-shot — it does not auto-refresh.

SizeDimensionsUse case
BannerAdSize.Banner320 × 50Standard banner
BannerAdSize.Leaderboard728 × 90Tablets
BannerAdSize.MRec300 × 250Medium rectangle
BannerAdSize.Adaptive(width, maxHeight)customFluid layout
tip

Use BannerAdSize.GetMaxAdaptiveHeight(width) to calculate maxHeight for adaptive banners.

Call Show(position) on a loaded BannerAd to anchor it to one of the positions below. Call Hide() to remove it from the screen.

PositionConstant
TopBannerPosition.HorizontalTop
BottomBannerPosition.HorizontalBottom
LeftBannerPosition.VerticalLeft
RightBannerPosition.VerticalRight
var banner = new BannerAd(sdk, placementId: "YOUR_PLACEMENT_ID", size: BannerAdSize.Banner);

banner.Loaded += (s, e) => banner.Show(BannerPosition.HorizontalBottom); // default position
banner.LoadFailed += (s, e) => Debug.LogError($"Banner load failed: {e.Error.Message}");
banner.Shown += (s, e) => { };
banner.ShowFailed += (s, e) => { };
banner.Clicked += (s, e) => { };
banner.Expired += (s, e) => { };
banner.RevenuePaid += (s, e) => { };

banner.Load();

// Later:
banner.Hide(); // remove the overlay; Show(position) can re-display it
banner.Dispose();
EventDescription
LoadedAd loaded and ready to show
LoadFailedAd failed to load
ShownBanner visible on screen, impression tracked
ShowFailedAd failed to display
ClickedUser tapped the ad
ExpiredAd expired before being shown
RevenuePaidBillable impression recorded

Publisher Extras

Attach arbitrary key → value pairs forwarded with the bid request as publisher extras. The API exists at two levels with the same shape — choose the one that matches the scope you need.

ScopeWhere to callApplies to
SDK-wideBidMachine.AddExtraEvery auction from this instance
Per ad unit<AdUnit>.AddExtraOnly that placement's auctions

Passing null as the value removes the key. Per-ad-unit extras override SDK-wide values for the same key on that placement.

SDK-wide

var sdk = BidMachine.GetInstance("YOUR_APP_KEY");

sdk.AddExtra("user_segment", "whale");
sdk.AddExtra("ab_bucket", "control");

IReadOnlyDictionary<string, string> all = sdk.GetExtras();
sdk.AddExtra("ab_bucket", null); // remove

Per ad unit

Available on BannerAd, InterstitialAd, and RewardedAd:

interstitialAd.AddExtra("placement_context", "level_complete");
bannerAd.AddExtra("screen", "main_menu");

IReadOnlyDictionary<string, string> extras = interstitialAd.GetExtras();