---
title: "OpenRTB bid response, auction and macros"
description: "The bid response fields Epom reads and the ones it ignores, the four stages a returned bid passes before it can serve, second-price clearing, the nine loss reasons a buyer can receive and the auction macros filled in nurl, lurl and adm."
type: "reference"
audience: [network-manager, admin]
keywords: [OpenRTB, bid response, seatbid, adm, nurl, lurl, impid, crid, adomain, dealid, second price, clearing price, bid floor, deal floor, creative scan, loss reason, AUCTION_PRICE, AUCTION_LOSS, AUCTION_MBR, AUCTION_CURRENCY, auction macros, require a competing DSP, VAST in adm]
---

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.

# OpenRTB bid response, auction and macros

A detail page of the [OpenRTB integration reference](https://epom.com/ad-server/docs/reference/openrtb-integration-reference.md), which
has the two directions, the currency rules and the bid floor chain. This page is what happens
once a DSP partner answers: the response fields Epom reads, the stages a bid passes before it can
serve, how the auction clears, the loss reasons and the auction macros. The request a partner
receives is on [OpenRTB bid request fields](https://epom.com/ad-server/docs/reference/openrtb-integration-reference-bid-request.md); the
inbound direction on [Epom as a seller: the /hb/bid endpoint](https://epom.com/ad-server/docs/reference/openrtb-integration-reference-seller.md).

## 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. The ad server calls it once your bid wins, with the [auction macros](#auction-macros) filled in, and ignores the response: the creative must come in `adm`. Only an `http(s)` URL on a public address is called. In [header bidding](https://epom.com/ad-server/docs/guides/inventory/header-bidding.md) it fires from the page instead, as an impression pixel added to the creative |
| `lurl` | Loss notice, called only when the partner's **Call loss notification URL** is on — see [Loss reasons](#loss-reasons). As with `nurl`, the response is ignored and only an `http(s)` URL on a public address is called |
| `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 |
| `cat[]` | The creative's IAB content categories — carried onto the winning creative and written as `<Category>` into the VAST 4.x response, so a server-side ad insertion stitcher reselling the ad sees them |
| `adomain[]` | Carried onto the winning creative and republished as the `adomain` of Epom's own bid when that creative is resold through [header bidding](https://epom.com/ad-server/docs/guides/inventory/header-bidding.md) — 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[]` and `iurl` have no consumer anywhere in the serving path, so a campaign id sent on
> the bid does **not** reach Epom's reporting. And `w`/`h` are 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.

> **Note on VAST in `adm`:** the document is taken exactly as you send it. Percent sequences and `+`
> inside a `VASTAdTagURI`, a tracker URL or a click-through survive untouched, so a wrapper pointing
> at a URL-encoded tag still resolves, and a `%%MACRO%%` your own stack left behind no longer costs
> you the impression. Markup that is percent-encoded as a whole — a document that does not start with
> `<` — is still decoded first, for partners that send it that way.

> **Note:** when Epom sends a win or loss notice itself, it sends it only to an `http` or `https` URL
> whose host resolves to a public internet address. A notice addressed anywhere else — another scheme,
> a private network, loopback or link-local — is skipped the way an unreachable endpoint is, and the
> auction is unaffected.

> **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 `dealid` **skips 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](https://epom.com/ad-server/docs/use-cases/creative-policy-protection.md)), 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` + `seat` matches 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** `dealid` is not dropped by stage 4 — it competes normally
> unless a private-auction bid displaces it.

### 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 in `adm` points
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.

Which `imptrackers` and `eventtrackers` of a native bid fire as impression beacons is on the hub:
[Native bids: which imptrackers and eventtrackers fire as impression beacons](https://epom.com/ad-server/docs/reference/openrtb-integration-reference.md#native-bids-which-imptrackers-and-eventtrackers-fire-as-impression-beacons).

## 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](https://epom.com/ad-server/docs/concepts/auction-first-vs-second-price.md) for the full model.

> **Corner case:** the DSP partner's **Require a competing DSP** 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:** `200` is 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, in the currency of your bid |
| `${AUCTION_PRICE:$$ENC$$}` | The clearing price, encrypted per the partner's encryption setting |
| `${AUCTION_CURRENCY}` | The currency of your bid: `USD`, or your currency |
| `${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**, both in your currency, 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.

## Related

- [OpenRTB integration reference](https://epom.com/ad-server/docs/reference/openrtb-integration-reference.md) — the two directions, currency and the bid floor
- [OpenRTB bid request fields](https://epom.com/ad-server/docs/reference/openrtb-integration-reference-bid-request.md)
- [Epom as a seller: the /hb/bid endpoint](https://epom.com/ad-server/docs/reference/openrtb-integration-reference-seller.md)
- [First vs second price](https://epom.com/ad-server/docs/concepts/auction-first-vs-second-price.md) — the clearing model in full
- [PMP deals](https://epom.com/ad-server/docs/guides/advertising/pmp-deals.md)
- [Configure DSP partners](https://epom.com/ad-server/docs/guides/admin/dsp-partners.md)