---
title: "Header bidding on a site that runs its own Prebid.js"
description: "Add Epom as one more bidder to a publisher’s own Prebid.js — create the tag in Bidder integration, copy one bids entry per ad unit off the Code face, and what the publisher does with it, including routing it through their Prebid Server."
type: "howto"
audience: [network-manager, admin]
keywords: [prebid.js, own prebid, bidder integration, bidder mode, epom_as, bidder code, alias, aliasBidder, bids entry, addAdUnits, mediaTypes, networkId, placementKey, vastXml, allowVastXmlOnly, prebid cache, outstream, price floors, s2sConfig, hb_deal]
ui_path: "Account → Header Bidding"
---

Fetch the complete documentation index at: https://epom.com/ad-server/docs/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Header bidding on a site that runs its own Prebid.js

Use this route when the publisher already runs Prebid.js and adds Epom as one more bidder in their
own auction. The tag produces no script and no containers here: you create it in Bidder
integration, copy one `bids` entry per ad unit off its **Code** face, and the publisher puts each
into an ad unit they already have. The same entries work when their Prebid.js routes its bidders
through a Prebid Server.

## Who this is for

Network managers and admins who build the tag, and the publisher’s developer who edits their Prebid
configuration. The tag needs the access listed under [Permissions](https://epom.com/ad-server/docs/guides/inventory/header-bidding.md#permissions).

## Prerequisites

- Our `epom_as` adapter in their Prebid.js build — see the two conditions at the start of
  [When the site runs its own Prebid](#when-the-site-runs-its-own-prebid).
- A placement of a supported type on the site for each ad unit you bid on.
- A network id on the ad server; without it the **Code** face stays empty after a save.

## Steps

1. Create the tag in Bidder integration, one slot per ad unit — section *1. Create the tag* below.
2. Save, open **Code** → **Their Prebid.js, our adapter** and copy one entry per slot — section
   *2. Read the entries off the Code face*.
3. Hand each entry over to go into the matching ad unit’s `bids`, with the alias line when you bid
   under your own name — section *3. What the publisher does with it*.
4. If their page calls its bidders through a Prebid Server, see
   [Through their Prebid.js, into their Prebid Server](#through-their-prebidjs-into-their-prebid-server).

## When the site runs its own Prebid

The publisher already has ad units and already runs the auction; we become one more bidder inside
it. Nothing of ours goes on their page except one entry per ad unit. Two conditions, before
anything else: **our adapter has to be in their Prebid build** — the `epom_as` module is not yet
in a released Prebid.js build (it has been submitted to Prebid and is under review; upstream ships
only `epom_dsp`, which is the buy side and cannot sell your inventory), so ask Epom support or your account manager
for the module or a build that carries it; and **their placements have to be listed on a tag**, or every bid is refused — see
[When a placement answers bids](https://epom.com/ad-server/docs/guides/inventory/header-bidding-settings.md#when-a-placement-answers-bids).

### 1. Create the tag

1. **Account → Header Bidding → Add**. Pick the **Site**, the **Serving domain** and a name.
2. Set **What the tag brings** to *Nothing — the site runs its own Prebid and adds us as a bidder*.
   The timeout, refresh and Google settings disappear: their Prebid owns all of that. **Bidder
   code** takes their place; what to put there is under [Create a tag](https://epom.com/ad-server/docs/guides/inventory/header-bidding-settings.md#create-a-tag).
3. On **Slots**, add one slot per ad unit of theirs that should carry our bid: the **Placement**
   that answers it (its type decides what the ad unit may ask for — a Video Placement answers
   `mediaTypes.video`, a Native Placement `mediaTypes.native`, a Site or Non-standard Placement
   `mediaTypes.banner`), their ad unit `code` as the **Container name** (it marks nothing on the
   page in this mode; it only names the entry), and on a Site Placement the placement's own size
   under **Ad units** — required, though nothing you pick here reaches their page: the sizes are
   their `mediaTypes`. Leave **GAM ad unit** and **Fallback markup** empty. There is no **Partners**
   tab: other bidders on that ad unit are theirs to configure, and Save refuses a tag in this mode
   while any slot still names a partner — remove them before switching, because the tab that holds
   them is hidden once you have.
4. **Save**, with **Serve this tag** on. Their entries exist only after this save.

### 2. Read the entries off the Code face

Open **Code** and pick **Their Prebid.js, our adapter** on the switch at the top. Above the
entries a second switch picks the name we bid under: **As epom_as** — recognised by any Prebid that
carries our adapter, with nothing else to declare — or **As epom_{network}**, the network's own
code or the tag's **Bidder code** if you set one, which keeps our name out of their configuration
at the cost of one more thing they must not forget. Then one block per slot, headed by the
container name, with a copy button:

```js
// once per page, before the auction — only under an alias
pbjs.aliasBidder('epom_as', 'epom_n1234');

// into the bids of ad unit "article-leaderboard"
{
  bidder: 'epom_n1234',
  params: {
    networkId: 'n1234',
    host: 'ads.example.com',
    placementKey: 'a4f21c9e7b',
    channel: 'sports-uk',
    customParams: { "section": "sport" }
  }
}
```

`channel` and `customParams` appear only when the placement carries them under **Ad tags & API →
Settings** — see [Channel and custom parameters](https://epom.com/ad-server/docs/guides/inventory/header-bidding-settings.md#channel-and-custom-parameters). They are a
snapshot: change them on the placement afterwards and the publisher has to be given the new entry,
because in this mode their Prebid config is the copy that counts. The same holds for `host`, which
is quoted from the tag's **Serving domain**.

`networkId` names your network. Prebid.js still sends the bid to `host`, the network's own domain,
where its cookies live. The field is there so the same entry also works in a publisher's Prebid
Server, which may not take a bidder's address from a parameter and reaches the network by its id
instead. An adapter that predates the field ignores it.

- **It lists a slot only after the tag has been saved.** The blocks are built from the last saved
  tag, so an edit reaches them only after you save; a note above the blocks flags the unsaved
  changes. Save, then copy.
- **It can stay empty after a save.** If the face still says *Save the tag once to get a document
  per slot.* after you saved, and the naming switch offers only `epom_as`, this network has no
  network id set on the ad server. Epom sets it — ask Epom support or your account manager; nothing in the tag fixes it.
- **The alias line names `pbjs`.** If their Prebid runs under another global, they rename that one
  call; the entry itself carries no global.

![The Code face for a page that already runs Prebid.js: the per-slot bids entry and the naming switch](https://epom.com/docs-images/ad-server/header-bidding/hb-code-their-prebidjs-adapter.webp)

### 3. What the publisher does with it

**The entry goes into an ad unit they already have.** Not a new ad unit. Their `mediaTypes` stay as
they are, their other bidders stay as they are, and the entry is one more object in that unit's
`bids` array:

```js
pbjs.addAdUnits([{
  code: 'article-leaderboard',
  mediaTypes: { banner: { sizes: [[728, 90], [970, 250]] } },
  bids: [
    { bidder: 'appnexus', params: { placementId: 13144370 } },
    { bidder: 'epom_as', params: { networkId: 'n1234', host: 'ads.example.com', placementKey: 'a4f21c9e7b' } }
  ]
}]);
```

**Their auction stays the only auction.** A second `addAdUnits` + `requestBids` on the same ad
unit codes — a second wrapper, a second Prebid build, our tag in Owned or Adopt mode beside theirs
— silently voids the first one's targeting: whichever run writes the GPT key-values last is the one
Google sees, and the other run's winner is never rendered.

**Every Epom ad unit on the page travels in one request** to `https://{host}/hb/bid`, so
roadblocks and one-campaign-per-page rules hold across the page. `host` is the serving domain as a
bare hostname — no `https://`, no path; the adapter refuses anything else before the request leaves
the page. The ad server prices the impressions in order and stops at their `bidderTimeout`, less a
little kept back for the round trip, so a short timeout on a page with many units leaves the last
impressions unbid.

**Sizes.** Their `mediaTypes.banner.sizes` are the sizes we may answer with, and the creative has to
match one of them exactly or no bid is made — you see each refusal on the placement's troubleshooting
log as a size the impression did not ask for. A placement pinned to one size needs
that size in their list; a placement that allows variable sizes needs every size its creatives come
in. Two cases are never size-matched: video, as under *Instream video* below, and any creative on a
Non-standard Placement, sized or not. A sizeless creative on a Non-standard Placement is answered at
the first size their unit lists; one with a size of its own is answered at that size.

**Instream video.** A Video Placement answers `mediaTypes.video` with `context: 'instream'`;
`playerSize` is a hint and never matched. The bid comes back as VAST in `vastXml`, and Prebid.js
drops a VAST bid before `bidsBackHandler` unless their config says what to do with it. Pick by where
the ad is going: a player that takes VAST as a string (IMA, JW) needs
`pbjs.setConfig({ cache: { allowVastXmlOnly: true } })`; a player that wants a URL needs
`cache: { useLocal: true }`; only a real Prebid Cache — `cache: { url: 'https://…/cache' }` —
produces the `hb_uuid` a Google Ad Manager video line item needs. We return VAST, not a player: an
`outstream` unit needs a renderer of their own, or Prebid discards the bid.

Recognise what a missing cache setting looks like, because nothing on our side reports it: the ad
server answered with a bid and the placement's troubleshooting log shows it, while the page's slot
reports no bid at all. Prebid dropped the bid before `bidsBackHandler` ran, and the only trace is an
error in the browser console. When a publisher says video never fills while the log says it bid,
that setting is the first thing to ask for.

**Native.** A Native Placement answers `mediaTypes.native`, in the OpenRTB or the legacy asset
form; the bid answers each asset under the id they asked it under. Rendering is theirs — an
`adTemplate` or `rendererUrl` on the ad unit, or their own code over `bid.native.ortb`.

**Two Epom networks on one page need two names.** Both entries are called under one code, but
their reports and the `hb_*_<bidder>` keys then show one Epom, and a Prebid Server keeps only one
of them. Give each network a **Bidder code** of its own; both aliases are declared once per page,
before the auction.

**Consent** works as described under [Consent](https://epom.com/ad-server/docs/guides/inventory/header-bidding-settings.md#consent): their `consentManagement` module's
string rides the request, and a page without one still bids. **Floors** from their Price Floors
module, or `params.bidFloor`, are honoured before any markup is built — USD only. **Capping** rides
a cookie on the serving domain: the adapter sends the request with credentials, so the identity our
beacons set reaches the next bid; a browser that withholds third-party cookies leaves per-user
capping without an id on that page, and the bid is still made.

## Through their Prebid.js, into their Prebid Server

The commonest server-side case has no slot id in it at all. A page already running Prebid.js can
route some or all of its bidders through a Prebid Server instead of calling them from the browser,
and for that publisher we are one more name in a list they already keep:

```js
pbjs.setConfig({
  s2sConfig: [{
    accountId: 'the-id-their-server-issued',
    bidders: ['epom_as'],
    enabled: true,
    endpoint: 'https://their-prebid-server.example.com/openrtb2/auction',
  }],
});
```

Their ad unit does not change, and neither does the entry we print on the *Their Prebid.js, our
adapter* face — the same `bids[]` line, with the same `networkId`, `host` and `placementKey`. Prebid.js carries
those params to their server as `imp.ext.prebid.bidder.epom_as`, which is the same place every
other server face puts them, so there is nothing to store and nothing to keep in step. What the
publisher needs from us is those values; what they need from their own host is the `accountId`
and an `epom_as` their server recognises.

The slot still has to be sold by an active tag here, exactly as in the browser case — a request
routed through a server carries no mark saying so, and the ad server cannot tell one from the other.

## Corner cases and gotchas

> **Corner case:** **An alias has to be declared, once per page, before the auction.** Under any name other than
> `epom_as` the adapter entry opens with `pbjs.aliasBidder('epom_as', …)`; leave that line out and
> Prebid drops the bidder in silence. An empty **Bidder code** is not `epom_as` — it is `epom_` plus
> the network id, so the alias line is there unless you typed `epom_as`.

> **Corner case:** **A long alias truncates the Google key.** Prebid cuts targeting key names at 20 characters, so
> `hb_deal_` plus a bidder code longer than 12 characters reaches Ad Manager shortened —
> `hb_deal_epom_dailynews` becomes `hb_deal_epom_dailyne`. Build the line item on the shortened
> key, or keep the code short.

## Per-role notes

- **Network manager** — builds the tag and copies the entries; the publisher’s Prebid
  configuration is theirs to edit.
- **Publisher** — pastes the entries into their own ad units; see
  [Header bidding: what a publisher installs](https://epom.com/ad-server/docs/guides/inventory/header-bidding-for-publishers.md).
- **Can View share** — sees the site’s tags but cannot save or delete them; see the
  [per-role notes](https://epom.com/ad-server/docs/guides/inventory/header-bidding.md#per-role-notes) on the main page.

## Related

- [Header bidding](https://epom.com/ad-server/docs/guides/inventory/header-bidding.md)
- [Header bidding tag settings and slots](https://epom.com/ad-server/docs/guides/inventory/header-bidding-settings.md)
- [Header bidding with Google Ad Manager](https://epom.com/ad-server/docs/guides/inventory/header-bidding-gam.md)
- [Header bidding through a Prebid Server](https://epom.com/ad-server/docs/guides/inventory/header-bidding-prebid-server.md)
- [Header bidding in an app with Prebid Mobile](https://epom.com/ad-server/docs/guides/inventory/header-bidding-prebid-mobile.md)
- [Header bidding reports and targeting](https://epom.com/ad-server/docs/guides/inventory/header-bidding-reporting.md)
- [Header bidding: what a publisher installs](https://epom.com/ad-server/docs/guides/inventory/header-bidding-for-publishers.md)
- [OpenRTB integration reference](https://epom.com/ad-server/docs/reference/openrtb-integration-reference.md)