OpenRTB integration reference
Field-level reference of both OpenRTB directions — the bid request Epom sends DSP partners and the response fields it reads back, the floor / PMP / creative-scan filters, the loss reasons and auction macros a buyer receives, and the /hb/bid endpoint Epom answers as a seller.
This is the field-level map for anyone plugging a DSP into Epom, or plugging Epom into a buyer, and for debugging why a partner sees no bid opportunity, a malformed request, or a bid that never wins.
Epom speaks OpenRTB in two directions, and they are separate implementations with different defaults:
| Direction | Epom’s role | Endpoint | Protocol |
|---|---|---|---|
| Outbound — Epom asks DSPs to bid | Exchange / SSP | The partner’s endpoint | Per partner, default 2.5 |
| Inbound — a buyer asks Epom to bid | Seller / bidder | POST /hb/bid on the ad-serving host | 2.6 |
Most of this page is the outbound direction, because that is where the field surface is large. The inbound direction is covered under Epom as a seller; the product side of it lives in Header bidding.
For the outbound direction: for each OpenRTB banner reached during ad selection, Epom builds one bid request per eligible banner, POSTs it to that banner’s DSP partner endpoint, and runs an auction over the returned bids. Partners are configured in DSP Partners; the demand-side banner in OpenRTB banners.
Note: two banners pointing at the same partner produce two requests, not one — the unit is the banner, not the partner. A banner whose DSP partner is deactivated is dropped before any request is built.
Note: the request is a JSON
POSTwithContent-Type: application/json; charset=UTF-8and anx-openrtb-versionheader naming the partner’s protocol version; the body is gzipped only when the partner has Gzip bid request on. Version, timeout, currency conversion and endpoint are per-partner settings — see the DSP Partners field table. Protocol version defaults to 2.5; native protocol to 1.1. Null fields and empty arrays are omitted from the JSON rather than sent.
Note: on a header-bidding call, a DSP partner is asked only when its Allow in header bidding setting is on, the platform-wide programmatic switch is on, and its pricing model is CPM. Otherwise its banners are skipped for that call.
Currency: USD, always
Epom is a single-currency exchange. The SSP currency is a hard-coded constant, and it appears on the wire in four places:
- the outgoing
curon every bid request defaults to["USD"]; imp.bidfloorandpmp.deals[].bidfloorcurare USD;- the
${AUCTION_CURRENCY}macro always substitutes the literalUSD, whatever else is configured; - Epom’s own bid response as a seller declares
cur: "USD".
There is no FX feed and no currency negotiation. The only escape is a pair of per-banner
network parameters — DSP Custom Currency and USD to DSP Custom Currency Rate — which you
set together on the OpenRTB banner. When both are present, Epom sends cur and imp.bidfloorcur
as your currency with the floor converted at your rate, and converts the returned bid.price back
to USD at the same rate.
Warning: that rate is a static number you typed, not a live exchange rate. It does not update, and nothing alerts you when it drifts. If the rate is missing or does not parse, Epom silently falls back to
1.0— meaning a EUR bid is banked as though it were USD.
Corner case: because Epom rewrites the response currency to
USDduring that conversion, the “reject a non-USD bid response” check that exists in the floor validator can never actually fire on the DSP path. Sendingcur: "EUR"in your bid response therefore does not produce an error — the price is taken at face value (or at your configured rate). Getting the rate parameters right is the only protection.
The bid floor
imp.bidfloor is the first non-zero value along this chain, evaluated for the request’s
country:
banner Bid Floor → placement Bid Floor → site Bid Floor → the banner’s own price → the campaign price → the site CPM threshold
A header-bidding request raises the result to the publisher’s floor when that is higher.
If every link is unset or zero, no floor is sent. An empty country code short-circuits the chain to zero. The two price links only contribute when the pricing type yields an eCPM — see CPM threshold for the pricing-type table.
Warning: the javadoc above this method in the source states a different order (placement before banner) and lists a seventh step, “open rtb country bid floor”, that does not exist. The order above is what the code does. Do not use the javadoc.
Two things sit outside the chain:
- A header-bidding caller’s floor raises ours. When the inbound request names its own floor, the value sent onward to DSPs is the higher of the two. This applies only to header-bidding requests; ordinary serving keeps the chain’s answer.
- There is no per-partner floor. Nothing on the DSP partner sets or adjusts a floor, and there is no way to quote different partners differently except by giving them different banners.
Note: nothing takes a margin off DSP spend. The winning bid price is banked as the raw settlement price. Epom’s margin is taken on the publisher side, as revenue share on the site or placement — the buyer’s price is never marked down. (A “fee” field exists in the floor model but is never populated by any code path; it is always zero.)
Request: top-level BidRequest
Eight fields ship on every request; four more are conditional.
| Field | What Epom sends | Always? |
|---|---|---|
id | Generated UUID per request | Yes |
imp[] | Exactly one impression | Yes |
site or app | app when the site’s inventory type is APP or the placement is an application placement; otherwise site. Sending neither or both is rejected before the request leaves. | Yes (one of) |
device | See device | Yes |
user | See user | Yes |
at (auction type) | 1 (first price) or 2 (second price) — see below | Yes |
tmax | The partner’s configured Timeout (ms), minus the time already spent on this auction batch | Yes |
cur | ["USD"], or the partner’s custom currency | Yes |
bcat | Blocked IAB categories — union of blocked categories on site + zone + placement | When non-empty |
source | pchain, plus schain — 2.5 puts it in source.ext.schain, 2.6 in source.schain | Only with the Add source schain network parameter |
test | 0 | DoubleImpact only |
ext | ext.sub = placementId | ExoClick only |
Corner case:
atis not always first price. It is2(second price) when the banner has Always 2nd price set and it is the only programmatic candidate, and also when several programmatic banners compete and the partner’sFORCE_FIRST_PRICEparameter is explicitly set tofalse. Note the inverted sense of that parameter: absent andtrueboth mean first price; only an explicitfalseopens the door to second price. See First vs second price.
Corner case: the
tmaxyou receive is the Timeout configured on the partner minus the time the current auction batch has already used, so it is usually smaller than the configured value. If 10 ms or less remains, the request is abandoned and never sent at all.
Not sent: badv, bapp, wseat, allimps, and regs — the regs object does not exist on
Epom’s outgoing model at all, so there is no regs.gdpr, regs.us_privacy, regs.gpp or coppa.
Request: imp by placement type
Every request carries exactly one impression object, typed to the placement.
Display (imp.banner)
| Field | Value |
|---|---|
banner.id | The placement id |
banner.w / banner.h | The banner’s own size, or the placement’s size when Use placement size is on |
banner.format[] | A single {w,h} format — 2.5 partners only |
banner.pos | The placement’s ad position |
banner.battr | Blocked creative attributes from the placement |
Corner case:
format[]is sent to 2.5 partners only, and never to 2.6 partners — which is the reverse of what the spec’s own direction of travel suggests, since 2.6 is the version that deprecatesbanner.w/hin favour offormat[]. A 2.6 partner receivesw/hand noformat.
Corner case:
banner.btypeis never populated. Onlybattrcarries the placement’s blocking.banner.mimes,banner.expdirandbanner.apiare likewise never sent.
Corner case: on pop placements, several partner types receive no banner object at all (it is set to null), and DoubleImpact receives an empty one.
Video (imp.video)
| Field | Value |
|---|---|
video.linearity | Linear / non-linear, from the banner’s In-Stream video type |
video.mimes[] | The banner’s Mime types list, or Epom’s default video mime set when empty |
video.minduration / video.maxduration | The placement’s min/max ad duration |
video.protocols[] | A fixed list — see below |
video.skip / video.skipmin / video.skipafter | From the placement’s skip settings |
video.playbackmethod[] / video.api[] | The placement’s RTB playback methods and API frameworks, when non-empty |
video.w / video.h | The placement ad-unit size, when set |
video.battr | Blocked video attributes from the placement |
video.minbitrate / video.maxbitrate | From the VAST request, when present |
video.pos / video.sequence / video.maxextended / video.boxingallowed | Always sent: 0 / 1 / 0 / 1 by default |
imp.ssai | 3 (stitched and tracked server-side) when the placement’s Server-side ad insertion (SSAI) setting names a stitcher; omitted otherwise — see Video placements |
video.protocols[] is a hard-coded six-element list that does not vary by placement, partner or
protocol version:
| Protocol | Id |
|---|---|
| VAST 2.0 | 2 |
| VAST 3.0 | 3 |
| VAST 2.0 wrapper | 5 |
| VAST 3.0 wrapper | 6 |
| VAST 4.0 | 7 |
| VAST 4.0 wrapper | 8 |
Corner case: VAST 1.0 (1, 4) and VAST 4.1 / 4.2 / 4.3 (11–16) are all modelled in Epom’s enum but are deliberately never advertised. A DSP that only bids on 4.2 will never see a matching protocol, and there is no setting that adds one.
Corner case:
boxingalloweddefaults to1and therefore ships on every video impression, not only when a VAST request supplies it. The VAST request only overrides the default.
Native (imp.native)
| Field | Value |
|---|---|
native.ver | The partner’s native protocol version (default 1.1) |
native.request | The native request spec built from the placement, optionally wrapped per the partner’s native request wrap setting |
native.battr | Blocked native attributes from the placement |
native.api and native.ext are never sent.
Shared imp fields (all types)
| Field | Value |
|---|---|
imp.id | The request’s transaction id — not the placement id |
imp.tagid | The placement key, or a custom OpenRTB tag id resolved from the placement (macros expanded) |
imp.bidfloor | The calculated floor — see The bid floor |
imp.bidfloorcur | USD, or the partner’s custom currency |
imp.instl | 1 for interstitial and rewarded placements, or when the partner forces interstitial |
imp.rwdd | 1 when the placement’s Application ad category is Rewarded video; omitted otherwise. A rewarded placement carries imp.instl = 1 as well, because it is full screen too — rwdd is the only field that tells the two apart, so a buyer that wants rewarded inventory (or wants to avoid it) reads this one |
imp.secure | 1 on HTTPS requests |
imp.pmp | Present only when the campaign has PMP deals |
imp.ext | Partner-specific (e.g. TrafficHunt subid, Epom WL DSP partnerRef); TwinRed always gets adm_on_resp: 1. On an always-redirect placement a pop is type: "pop" for Custom partners, and for TwinRed the impression is sent without an imp.banner object — see pops |
imp.displaymanager, imp.displaymanagerver, imp.exp and imp.clickbrowser do not exist on
Epom’s model and are never sent.
Request: site and app — page URL, domain and publisher
The request carries site for web traffic and app for app inventory (see the top-level table).
| Field | site (web) | app |
|---|---|---|
domain / name | Host of the page that called the tag: the page origin (the tag’s o parameter or the Origin header), else the Referer host, else the URL configured on the site | app.name is the site’s name; app.domain only when the SDK request passes one |
page | The full page URL — the tag’s requestUrl, else the Referer — when it belongs to that origin | — |
ref | The document referrer (the tag’s requestRef), when known | — |
id | A numeric hash of the domain; a custom parameter ortb.site.id (cp.ortb.site.id in the tag) replaces it | The site id |
bundle / storeurl / ver | — | The site’s Bundle setting; the SDK request’s bundle (or the WebView’s X-Requested-With package), storeurl and ver override it |
publisher.id | A generated publisher id per site — never the account | Same |
cat / sectioncat / pagecat | IAB categories of the site, the zone and the placement | Same |
privacypolicy | The site’s privacy-policy flag | — |
The buyer sees a Google domain (imasdk.googleapis.com) instead of the site
Inside Google’s IMA player the tag is requested from Google’s SDK frame. Epom drops imasdk.googleapis.com from the
origin and referrer and falls back to the site URL configured in Epom, so set the site’s URL. A
page that sends no Referer (a strict referrer policy, some players) also leaves page empty —
pass the page URL in the tag’s requestUrl parameter (data-page on the async tag), which
replaces the Referer, and requestRef for the document referrer.
Readable source id or zone name in the partner’s stats
imp.tagid carries the placement key by default. To show the buyer a readable id (for example sports-daily-top), set the placement’s Custom tag ID on
its RTB configuration card — see OpenRTB placement settings.
Is pre-roll, mid-roll or post-roll sent to the DSP?
No. Outbound video requests carry no video.startdelay, so a DSP cannot tell a pre-roll from a mid-roll or post-roll.
Request: device
| Field | Value | Notes |
|---|---|---|
ua | User agent | Always |
ip / ipv6 | Client IP | One or the other by address family |
js | Always 1 | Always |
connectiontype | Mapped to the OpenRTB connection-type enum | When known |
carrier | Mobile carrier | When known |
language | Browser language | When known |
os / osv | Operating system + version | When detected |
devicetype | PHONE / TABLET / CONNECTED_TV / PC | Derived from OS device format |
make / model / hwv | Vendor / family / model | From device detection |
ifa / dpidsha1 | Advertising IDs | The advertising ID (device.ifa) and its SHA1 platform-ID form (device.dpidsha1), from the request’s ifa parameter or the Android/iOS advertising-ID identification. An all-zero ID, an unsubstituted macro ([IFA]) or a placeholder such as null / unknown is treated as no ID |
lmt | Limit ad tracking, 1 / 0 | From the request’s lmt parameter; omitted when absent |
ext.ifa_type | Source of ifa — rida, tifa, aaid, sessionid, … | From the request’s ifa_type parameter, lower-cased; sent only alongside an ifa |
geo | See below | May be absent entirely |
ext.timezone / ext.timezoneCountry / ext.releaseYear | Non-standard extensions | Timezone name, its primary country (alpha-3), device release year |
device.geo carries country (ISO alpha-3), type, utcoffset, and lat/lon.
Corner case:
geo.lat/lonare not GPS-only. They are filled from IP geolocation on an ordinary request, withgeo.type = IP. Coordinates passed as request parameters are a second source and flipgeo.typetoGPS. Readgeo.typeif the distinction matters to you — a precise-looking lat/lon may be city-level IP data.
Corner case: the whole
geoobject is omitted when neither an alpha-3 country nor GPS parameters could be resolved.geo.region,metro,cityandzipare never sent.
Corner case: Epom does not populate
device.dnt, and sendsdevice.lmtonly when the ad request carried anlmtparameter (CTV tags behind a stitcher do). Otherwise a DSP filtering on them sees them absent (treated as unknown), not as0. Epom forwardslmt = 1as it is and does not withhold the ID: a device that limits tracking already zeroes or session-scopes it.
Corner case: screen width and height are not forwarded. Epom collects them (they reach the ad log and the creative-scan context) but drops them before the bid request, so
device.w/hnever appear.ppi,pxratio,suaandmccmncdo not exist on the model.
Request: user
| Field | Value | Notes |
|---|---|---|
id | Epom’s user id (UID) | Always |
buyeruid | The partner’s synced user id | Only when user sync is enabled on the partner and its sync cookie is present |
customdata | Partner-specific custom data | Keyed by the partner shortname |
keywords | The request’s keywords custom parameter | |
gender / yob / data[] | DMP segments | Only when DMP-to-bid-request enrichment is enabled |
ext.privateBrowsing | 1 (incognito) / 0 (normal) | Omitted when the state is unknown |
ext.xrw | The in-app WebView package name | From the X-Requested-With header |
ext.channel | The request channel | |
ext.category / ext.income | DMP-derived audience attributes | Only with DMP enrichment |
ext.<custom-param> | Any custom parameter on the request | Macros in the values are resolved before sending |
Note:
user.ext.privateBrowsingis a proprietary Epom extension, not an OpenRTB field. No standard field for private-browsing state exists in 2.5, 2.6 or 3.0, so Epom follows thednt/lmtconvention with an integer1/0and omits the key when the state is unknown. Do not expect other exchanges to send it.
Corner case: GDPR consent is not sent as a standard OpenRTB field. Epom carries the IAB consent string internally and uses it to gate its own serving and to fill consent macros in markup — but the outbound bid request has no
user.consentand noregs.ext.gdpr, because neither field exists on the outgoing model. The supported workaround is to pass the consent string as a custom parameter, which arrives asuser.ext.<your-param-name>; agree the name between the network and the DSP.
Request: pmp.deals
Present only when the winning banner’s campaign has PMP deals enabled and at least one deal
matches. Set on imp.pmp:
| Field | Value |
|---|---|
pmp.private_auction | 1 when the campaign marks the auction private, else 0 |
pmp.deals[].id | The deal id |
pmp.deals[].bidfloor | The deal’s floor |
pmp.deals[].bidfloorcur | USD |
pmp.deals[].wseat[] | The deal’s buyer (seat) id — always a single-element array |
pmp.deals[].at | The deal’s auction type |
Deals are matched by the request country first, then any global (country-agnostic) deals are
appended, de-duplicated. A private auction (private_auction = 1) with an empty deal list is
rejected before the request is sent — no HTTP call is made. A non-private auction with an empty
deal list simply drops the pmp object and sends the request without it.
Inventory masking
If the banner’s mask-inventory setting (or the site’s) applies, site.name becomes Site_<id>
and site.domain / site.page / site.ref are cleared. With field-hashing enabled, name and
domain are sent as MD5 hashes instead. Publishers are always sent as a generated publisher id,
never the raw account; the same id is used in pchain and schain.sid.
Note: even unmasked,
site.name,site.domainandsite.iddescribe the live page domain, derived from the ancestor-origin or referer — not the Epom site entity’s name.site.idis a hash of that domain. Do not expect it to match anything in the Epom UI. Onlyhttp(s)addresses count: an origin or referer such asabout:blank,data:…orandroid-app://…is ignored, and when neither is usable the site’s configured URL supplies the domain instead.
Response: fields Epom reads
Epom parses the standard seatbid[].bid[] structure. Anything not listed here is discarded on
parse — the deserializer ignores unknown fields.
| Response field | Used for |
|---|---|
price | Auction ranking, clearing price, billing |
adm | The served creative (HTML / VAST XML / native JSON) |
nurl | Win notice; for some partners the markup is delivered here |
lurl | Loss notice — see Loss reasons |
impid | Matched against the request’s impression; a bid that matches none is dropped |
crid | Creative-scan verdict key, troubleshooting, reporting |
adid | Fills the ${AUCTION_AD_ID} macro |
adomain[] | Carried onto the winning creative and republished as the adomain of Epom’s own bid when that creative is resold through header bidding — unless the advertiser declares its own Advertiser domain, which wins |
dealid | Validated against the request’s deals |
ext | ext.url and ext.impression_tracking_url, for Epom WL DSP pops |
seatbid[].seat | Deal wseat validation, loss records |
Response id / bidid / cur / customdata | Auction macros and currency handling |
Corner case: several fields you might expect to matter are parsed and then ignored:
cid,attr[]andiurlhave no consumer anywhere in the serving path, so a campaign id sent on the bid does not reach Epom’s reporting. Andw/hare not even modelled — creative size is taken from the request’s impression, never from your bid. Send them for spec compliance, but do not rely on Epom acting on them.
Warning: Epom accepts exactly one seatbid containing exactly one bid. A response with two seat objects, or one seat carrying two bids, is discarded whole as an invalid bid response — not trimmed to the best bid. If your bidder normally returns a bid per impression or several candidates per seat, it must be configured to return a single bid to Epom.
What happens to a returned bid
A bid passes through four stages before it can serve. They run in this order:
1. Response validation and floor. The response must be exactly one seatbid with one bid, and
the price must be at or above imp.bidfloor. A bid below the floor is dropped with
BID_WAS_BELOW_AUCTION_FLOOR.
Corner case: a bid carrying a
dealidskips the floor check entirely. Deal bids are held to the deal’s own floor instead, at stage 3. A deal priced below your open-auction floor still clears.
2. Private-auction pre-filter. When any impression in play is a private auction, seat bids whose deals are unsuitable are removed, and a partner left with nothing is dropped.
3. Creative scan. If the network scans the partner’s creatives
(creative scanning), the bid’s creative is checked
before the winner is chosen. A BLOCKED creative is removed from the auction, so the
runner-up wins instead — the impression still fills, just not with the blocked creative. Bids
removed here are recorded as CREATIVE_FILTERED_GENERAL_REASON_UNKNOWN.
4. Deal validation, inside the auction. This runs whenever any bid carries a dealid,
private auction or not:
- a bid whose
dealid+seatmatches no deal on the impression is dropped (INVALID_DEAL_ID); - a bid below its deal’s floor is dropped (
BID_WAS_BELOW_DEAL_FLOOR); - a Bid floor price deal has the bid’s price overwritten with the deal floor, whatever was bid;
- once any private-auction bid is present, every non-private bid is removed.
Corner case: a bid with no
dealidis not dropped by stage 4 — it competes normally unless a private-auction bid displaces it. The page previously implied otherwise.
Is there a size limit on a bid’s markup or video? Not in Epom: the ad server sets no cap on the bid response or on
adm, and a VAST inadmpoints at its video files by URL, so the file size never passes through Epom. A size limit quoted for a DSP’s video bids comes from the DSP, the player or the upload limit for your own video banners — not from the auction.
Native bids: which imptrackers and eventtrackers fire as impression beacons
For a native bid, Epom takes the impression trackers out of the native response in adm and
fires them as impression beacons of the served ad, next to its own:
| Native response version | Trackers fired at the impression |
|---|---|
| 1.0, 1.1 | Every URL in imptrackers |
| 1.2 | Every URL in imptrackers plus every eventtrackers entry with event: 1 (impression) and method: 1 (image pixel) that has a url — duplicates fired once |
Not fired: eventtrackers with method: 2 (JavaScript), entries for other events (viewability
2–4 and custom events), and jstracker. Each tracker URL is macro-processed first, so
${AUCTION_PRICE} and similar auction macros are filled. In a native template the beacons are
written as hidden 1×1 images by $$NATIVE_IMPRESSIONS$$, so they load when the ad renders — the
same moment Epom’s own impression beacon fires. They do not change how Epom counts: the
impression in Epom’s reports is Epom’s own beacon; the partner’s trackers feed the partner’s
numbers.
Auction and clearing
Epom runs the auction over all surviving bids across all partners for the impression. Second-price clearing applies only when the auction type resolved to second price and more than one bid survived; the clearing price is then:
min( max(deal floor + $0.01, second-highest bid + $0.01), winning bid )
— so it never exceeds what the winner actually bid, and never drops below a deal’s negotiated
minimum. With a single surviving bid the winner clears at its own price, capped toward the floor
plus a cent when the banner is set to Always 2nd price. One cent is 0.01 USD.
The clearing price is what feeds the served markup’s price macro and the impression’s revenue — not
necessarily the raw price the winner sent. See
First vs second price for the full model.
Corner case: the DSP partner’s Second price only checkbox (and the matching banner-level network parameter) does not change the clearing rule. It withdraws the banner from the auction when it would be the only bidder — the partner is saying “only bid me when there is competition”. A partner that mysteriously never wins on low-competition inventory usually has this set.
Loss reasons
Epom fills ${AUCTION_LOSS} in your lurl with the numeric OpenRTB loss-reason code. Nine of
the specification’s thirty codes are reachable:
| Code | Name | Why Epom sends it |
|---|---|---|
| 0 | BID_WON | You won |
| 3 | INVALID_BID_RESPONSE | Not exactly one seatbid with one bid, or the bid’s impid matched no impression |
| 4 | INVALID_DEAL_ID | dealid + seat matched no deal on the impression |
| 7 | MISSING_MARKUP | The winning bid produced no usable markup |
| 100 | BID_WAS_BELOW_AUCTION_FLOOR | Price under imp.bidfloor (non-deal bids only) |
| 101 | BID_WAS_BELOW_DEAL_FLOOR | Price under the matched deal’s floor |
| 102 | LOST_TO_HIGHER_BID | Ordinary auction loss |
| 103 | LOST_TO_A_BID_FOR_A_PMP_DEAL | Lost to a deal bid |
| 200 | CREATIVE_FILTERED_GENERAL_REASON_UNKNOWN | Removed by the creative scan |
Note:
200is the only signal you get for a creative-scan removal — the loss code does not say why the creative was removed.
Loss notices are sent only when the partner’s Call loss notification URL parameter is enabled.
Auction macros
Epom substitutes these in your nurl, lurl and adm before firing or serving them. An
unresolvable macro is replaced with an empty string, never left in place.
| Macro | Value |
|---|---|
${AUCTION_ID} | The bid request id |
${AUCTION_BID_ID} | The response bidid |
${AUCTION_IMP_ID} | The impid of the impression won |
${AUCTION_SEAT_ID} | The winning seat |
${AUCTION_AD_ID} | The bid’s adid |
${AUCTION_PRICE} | The clearing price, not your bid |
${AUCTION_PRICE:$$ENC$$} | The clearing price, encrypted per the partner’s encryption setting |
${AUCTION_CURRENCY} | Always the literal USD |
${AUCTION_CURRENCY:$$ENC$$} | Encrypted form of the same |
${AUCTION_LOSS} | The numeric loss reason — see above |
${AUCTION_MBR} | Market bid ratio, clearing price ÷ bid price as OpenRTB defines it: 1 under first price, below 1 when a second-price auction clears under the bid |
Note: the value is clearing ÷ bid, never the reciprocal, so it is at most 1. Do not invert it.
Epom as a seller: /hb/bid
Epom also answers OpenRTB bid requests, as a bidder in a Prebid auction. This is the header bidding path; the operational side — tags, slots, Prebid Server, reporting — is documented in Header bidding. The wire contract:
| Endpoint | POST /hb/bid on the ad-serving host. Any other method answers 405. |
| Protocol | OpenRTB 2.6 |
| Request | id and at least one imp required; site or app required. Up to 40 impressions per request (configurable per network by Epom). |
| Validation | Cheap field checks only, not full JSON-Schema. A rejection is a 400 with a JSON {"error": "<reason>"} body naming the reason. |
| No bid | 204 No Content — Epom does not send an OpenRTB no-bid object. |
| Response | One seatbid, seat epom, cur: "USD". |
Each returned bid carries id, impid, price, adm, crid, cid, w, h, mtype
(1 banner / 2 video / 4 native), exp, adomain when known, and dealid: "epom-direct" on
bought demand.
Note:
adomainis the Advertiser domain declared on the advertiser when the advertiser has one, otherwise theadomainthat came in on the DSP bid the creative was bought from. When neither exists the field is omitted — an absentadomainis deliberate, not a bug.
Corner case:
bid.exptells you how long the bid may sit in your cache, and it is derived from the impression beacon’s own validity minus a few seconds of travel time. Render later than that and the impression is never counted — the bid is still valid markup, but you will not be paid for it.
Warning: a
/hb/bidcall is a bid, not a served impression, but Epom does not yet treat it as one: selection still increments opportunity limits, request capping and pacing for bids that never win or render. Expect header-bidding traffic to consume capping and limit budget ahead of actual delivery, and read the header bidding reporting notes before reconciling numbers.
Corner case: requests for a placement that no active header-bidding tag lists are refused by default. That gate has three settings and its own diagnostics — see Header bidding.
Corner case: on a video slot,
bid.admis a VAST document Epom builds, and for a VPAID framework creative its<Duration>can legitimately be00:00:00. VAST requires a Duration on every Linear, but a VPAID creative’s real length is only known once the unit runs, so Epom emits a placeholder unless the framework declares a parameter literally nameddurationthat parses as a positive number of seconds. A real duration always carries milliseconds (00:00:15.000); the bare00:00:00is the placeholder. Do not reject the creative on it, and do not use it to size a player or a timeout.
Partner “network parameters”
Beyond the OpenRTB fields above, a partner integration uses named parameters drawn from a fixed set of 18. These are partner-onboarding values, filled on the DSP partner and/or the OpenRTB banner — not OpenRTB spec fields. Sixteen are the checkboxes of the partner’s Parameters card: Template Content Adapter, Broker Url, Win Notification URL, Traffic Tier, Mask Inventory, Second Price Only, Allow empty DSP UUID, DSP Custom Currency, USD to DSP Custom Currency Rate, Force First Price Auction Type, Call loss notification URL, Send winner price in loss notification, Hash Publisher Site Field’s Values, Add Source Chain to Bid Request, Custom Source Supply Chain ASI and Interstitial. The other two are the template ids the code reads, Template Native Adapter and the deprecated Template General. No other keys are accepted.
None of them is a fee, margin, commission or revenue share; no such parameter exists.
Coverage note
This page documents both directions of the standard path in full: every field Epom’s builder populates, every response field it consumes, the filter pipeline, the loss reasons, the auction macros, and the seller endpoint’s contract.
This page does not enumerate per-partner-type differences. Several of the 16 branded
partner types receive small partner-specific adjustments — a nulled or emptied banner object on
pops, partner-specific imp.ext keys, forced interstitial, test:0, or the markup read from a
partner-specific place in the response. They change when a partner’s integration is revised; ask
Epom support or your account manager what your specific partner type sends.
Two limits that are not gaps but firm behaviour, restated so they are not mistaken for omissions:
no dnt and no standard consent or regs on the outbound request — neither exists on
Epom’s outgoing model. device.lmt is not in that category: it is sent whenever the ad request
carried an lmt parameter — see Request: device.
Related
-
First vs second price — the clearing model in full
-
CPM threshold — the floor cascade and pricing types
-
Header bidding — the seller direction, end to end