# Standalone Integration

This guide shows how to use n360ortb without an ad server by rendering ads directly from the bid response.

## Overview

In standalone mode, you:

1. Request bids using `fetchBids()`
2. Receive the winning bids as an array
3. Render ads using `n360ortb.renderAd()`

This approach is ideal for simple implementations or custom ad rendering solutions.

## Complete Example

```html
<!DOCTYPE html>
<html>
<head>
  <title>n360ortb Standalone Example</title>

  <!-- n360ortb loader -->
  <script>
  !function(){if(!window.n360ortb){window.n360ortb={init:function(){e("init",arguments)},fetchBids:function(){e("fetchBids",arguments)},setDisplayBids:function(){},targetingKeys:function(){return[]},que:[]};var n=document.createElement("script");n.async=!0,n.src="https://lib.nexx360.io/nexx360ortb/api.js";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(n,t)}function e(n,t){window.n360ortb.que.push([n,t])}}();
  </script>

  <script>
    // Initialize
    n360ortb.init({
      currency: 'EUR'
    });

    // Fetch bids and render
    n360ortb.fetchBids({
      slots: [
        {
          tagId: 'your-tag-id',
          slotID: 'ad-container',
          sizes: [[300, 250]]
        }
      ]
    }, function(bids) {
      // bids is an array of StoredBid objects
      if (bids.length > 0) {
        // Render each bid into its target container
        bids.forEach(function(bid) {
          n360ortb.renderAd(bid, bid.slotID);
        });
      } else {
        console.log('No bids received');
      }
    });
  </script>
</head>
<body>
  <h1>My Page</h1>

  <div id="ad-container" style="width: 300px; height: 250px;">
    <!-- Ad will render here -->
  </div>
</body>
</html>
```

## Understanding the Bid Response

The `fetchBids()` callback receives an array of `StoredBid` objects:

```javascript
[
  {
    slotID: 'ad-container',    // Target element ID
    bidId: 'abc123-def456',    // Unique bid identifier
    price: 2.50,               // Bid price
    priceBucket: '2.50',       // Price bucket for targeting
    size: '300x250',           // Size string
    adm: '<script>...</script>',  // Ad markup to render
    crid: 'creative-789',      // Creative ID
    ssp: 'appnexus',           // SSP identifier
    targeting: {...},          // Targeting key-values
    timestamp: 1706000000000,
    rawBid: {...}              // Full OpenRTB bid object
  }
]
```

The `adm` property contains the complete ad markup (HTML/JavaScript) that should be rendered.

## The renderAd() Function

`n360ortb.renderAd(bid, targetElement)` handles rendering with automatic iframe context detection:

| Context                 | Behavior                                          |
| ----------------------- | ------------------------------------------------- |
| **Main page**           | Creates an iframe and renders using `srcdoc`      |
| **Friendly iframe**     | Resizes parent iframe, then renders               |
| **SafeFrame**           | Calls `$sf.ext.expand()`, then `document.write()` |
| **Cross-origin iframe** | Sends `postMessage` resize request, then renders  |

```javascript
// Render into element by ID
n360ortb.renderAd(bid, 'ad-container');

// Render into element reference
n360ortb.renderAd(bid, document.getElementById('ad-container'));

// Render via document.write (for use inside iframe/creative)
n360ortb.renderAd(bid);
```

## Handling Multiple Slots

```javascript
n360ortb.fetchBids({
  slots: [
    {
      tagId: 'leaderboard-tag',
      slotID: 'leaderboard',
      sizes: [[728, 90], [970, 250]]
    },
    {
      tagId: 'sidebar-tag',
      slotID: 'sidebar',
      sizes: [[300, 250], [300, 600]]
    },
    {
      tagId: 'footer-tag',
      slotID: 'footer',
      sizes: [[728, 90]]
    }
  ]
}, function(bids) {
  // Render all received bids
  bids.forEach(function(bid) {
    n360ortb.renderAd(bid, bid.slotID);
  });
});
```

## Handling No Bids

When no bid is returned for a slot, you can display fallback content:

```javascript
n360ortb.fetchBids({
  slots: [
    { tagId: 'my-tag', slotID: 'ad-slot', sizes: [[300, 250]] }
  ]
}, function(bids) {
  if (bids.length > 0) {
    n360ortb.renderAd(bids[0], 'ad-slot');
  } else {
    // Show fallback
    showFallback('ad-slot');
  }
});

function showFallback(slotID) {
  var container = document.getElementById(slotID);
  container.innerHTML = '<img src="/fallback-banner.jpg" alt="Advertisement">';
}
```

## How renderAd() Works

The built-in `n360ortb.renderAd()` function automatically:

1. **Creates a secure iframe** - Ads are isolated in their own document context
2. **Uses `srcdoc`** - Modern, secure way to inject HTML into iframes
3. **Handles resizing** - Automatically resizes based on bid dimensions
4. **Detects context** - Adapts behavior for SafeFrame, friendly iframe, or cross-origin scenarios

{% hint style="success" %}
Using the built-in `renderAd()` is recommended over custom implementations as it handles edge cases and different iframe contexts automatically.
{% endhint %}

## Refreshing Ads

To refresh ads after a period of time:

```javascript
function refreshAds() {
  // Fetch new bids and render (renderAd clears the container automatically)
  n360ortb.fetchBids({
    slots: [
      { tagId: 'your-tag-id', slotID: 'ad-container', sizes: [[300, 250]] }
    ]
  }, function(bids) {
    if (bids.length > 0) {
      n360ortb.renderAd(bids[0], 'ad-container');
    }
  });
}

// Refresh every 30 seconds
setInterval(refreshAds, 30000);
```

{% hint style="warning" %}
Be mindful of refresh rates. Excessive ad refreshing can negatively impact user experience and may violate SSP policies.
{% endhint %}

## Next Steps

* [API Reference](https://developer.nexx360.io/direct-integration-n360ortb/api-reference) - Complete API documentation
* [GAM Integration](https://developer.nexx360.io/direct-integration-n360ortb/gam-integration) - For integration with Google Ad Manager
* [Privacy & Consent](https://developer.nexx360.io/direct-integration-n360ortb/privacy-consent) - Handle GDPR and other privacy requirements
