OpenRTB Demand Integration
MailAdx acts as an SSP in the OpenRTB 2.5 ecosystem. When direct-sold campaigns don't fill a placement, the ad server runs a real-time auction against configured demand sources — within a 100ms deadline, with no blocking of the serving response.
How it works
The waterfall runs direct-sold line items first (highest-priority type wins). If no eligible line item fills the slot and the ad unit has RTB enabled, the ad server fans out an OpenRTB 2.5 bid request to all active exchanges simultaneously. The highest bid above the floor price wins. The creative HTML ( adm field) is returned directly to the publisher.
Auction timing
Global deadline: 100ms from the moment the direct waterfall returns no fill. Exchanges that don't respond within the deadline are excluded. The server never waits beyond the deadline — late responses are dropped.
Prerequisites
- MailAdx adserver deployed with
MA_RTB_ENABLED=trueenvironment variable. - At least one RTB exchange configured via Admin → RTB Exchanges.
- At least one ad unit with RTB enabled (off by default; opt-in per slot).
Step 1 — Add a demand source
Navigate to Dashboard → RTB Exchanges → Add Exchange (admin only).
For each exchange you need:
- Name — display name (e.g. "Google AdX", "AppNexus")
- Bid Endpoint URL — the DSP's OpenRTB 2.5 bid endpoint
- Timeout (ms) — default 150ms; responses after this are dropped
- Auth Header — optional, format
Authorization: Bearer token - Sandbox mode — tick this for test exchanges; tagged in reports
Or via API:
POST /api/v1/admin/rtb/exchanges
Authorization: Bearer {admin-jwt}
Content-Type: application/json
{
"name": "My DSP",
"endpointUrl": "https://bid.mydsp.com/openrtb/2.5",
"timeoutMs": 150,
"authHeader": "Authorization: Bearer sk-abc123",
"sandboxMode": false
}Step 2 — Enable RTB on an ad unit
RTB is disabled by default on every ad unit. Enable it per slot:
PUT /api/v1/ssp/ad-units/{adUnitId}/rtb/config
Authorization: Bearer {publisher-jwt}
Content-Type: application/json
{
"rtbEnabled": true,
"rtbFloorCpm": 0.50
}rtbFloorCpm overrides the slot's regular floor CPM for RTB bids only. Set null to inherit the slot's regular floor price.
Bid request format
MailAdx sends an OpenRTB 2.5 bid request to each exchange:
POST https://bid.exchange.com/openrtb/2.5
Content-Type: application/json
{
"id": "e4f2a9b1-...", // unique per request (UUID)
"imp": [{
"id": "1",
"banner": {},
"tagid": "ad-unit-uuid", // AdUnit ID from MailAdx
"bidfloor": 0.50, // floor CPM in USD
"bidfloorcur": "USD"
}],
"site": {
"publisher": { "id": "publisher-uuid" }
},
"user": {
"id": "sha256-of-email" // privacy-safe hash, null if anonymous
},
"device": {
"devicetype": 4, // 2=desktop, 4=mobile, 5=tablet
"geo": { "country": "IN" } // ISO 3166-1 alpha-2
},
"at": 2, // second-price auction
"tmax": 150 // global timeout in ms
}Bid response format
Your DSP should respond with a standard OpenRTB 2.5 bid response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "e4f2a9b1-...",
"cur": "USD",
"seatbid": [{
"seat": "your-dsp-id",
"bid": [{
"id": "bid-001",
"impid": "1",
"price": 2.35, // CPM bid in USD
"adm": "<img src='...' width='600' height='200'>", // HTML creative
"nurl": "https://win.yourdsp.com/notify?...", // win notification URL
"adomain": ["advertiser.com"],
"crid": "creative-id-456",
"w": 600,
"h": 200
}]
}]
}Return HTTP 204 to indicate a no-bid (no seatbid with price). The ad server handles empty seatbid arrays and missing price fields gracefully (treated as no-bid).
Creative format for email
Because MailAdx delivers to email (not web), creative markup in adm must be email-safe:
- Use
<img>tags — no JavaScript, no iframes - Absolute URLs only (no relative paths)
- Images should be hosted on CDN with HTTPS
- Recommended: single
<img>at the declared width × height
Win notifications
MailAdx stores the nurl from the winning bid. The win notification is fired asynchronously (fire-and-forget GET to the nurl URL) when the impression is counted. This is used by DSPs to confirm billing and record the clearing price.
Sandbox testing
MailAdx ships a built-in mock DSP endpoint for end-to-end testing without a real demand partner. Use bidCpm to control the bid price:
# Configure sandbox exchange (one-time setup):
POST /api/v1/admin/rtb/exchanges
{ "name": "MailAdX Sandbox DSP",
"endpointUrl": "https://mailadx.com/api/v1/rtb/sandbox/dsp/bid",
"timeoutMs": 150,
"sandboxMode": true }
# Test the mock DSP directly:
POST https://mailadx.com/api/v1/rtb/sandbox/dsp/bid?bidCpm=2.50
Content-Type: application/json
{ "id": "test", "imp": [{"id":"1","banner":{},"tagid":"test"}], "at":2, "tmax":150 }
# Inspect last bid request sent by adserver:
GET https://mailadx.com/api/v1/rtb/sandbox/dsp/last-request
# Health check:
GET https://mailadx.com/api/v1/rtb/sandbox/dsp/pingMonitoring auction performance
RTB reporting is available per publisher via the reporting API:
# Overall summary (last 14 days):
GET /api/v1/rtb/reports/summary?publisherId={id}&days=14
→ { totalRequests, totalBids, totalWins, fillRate, avgWinCpm, revenueUsd }
# Per exchange breakdown:
GET /api/v1/rtb/reports/by-exchange?publisherId={id}&days=14
→ [{ exchangeName, bids, wins, noBids, fillRate, avgWinCpm }]
# Per ad unit breakdown:
GET /api/v1/rtb/reports/by-adunit?publisherId={id}&days=14
→ [{ adUnitId, totalAuctions, wins, fillRate }]Key metrics to watch
- fillRate < 20% — most bids below floor; lower
rtbFloorCpmor add more exchanges - noBids high — DSP not interested in your inventory; review targeting data in bid request
- timeout rate high — DSP slow; increase
timeoutMsor contact demand partner
Exchange management API reference
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/admin/rtb/exchanges | List all exchanges |
| POST | /api/v1/admin/rtb/exchanges | Create exchange |
| PUT | /api/v1/admin/rtb/exchanges/{id} | Update exchange |
| DELETE | /api/v1/admin/rtb/exchanges/{id} | Delete exchange |
| POST | /api/v1/admin/rtb/exchanges/{id}/pause | Pause exchange |
| POST | /api/v1/admin/rtb/exchanges/{id}/activate | Activate exchange |
| GET | /api/v1/ssp/ad-units/{id}/rtb/config | Get RTB config for ad unit |
| PUT | /api/v1/ssp/ad-units/{id}/rtb/config | Set rtbEnabled + rtbFloorCpm |
All exchange management endpoints require NETWORK_ADMIN role. Ad unit config endpoints are accessible to publishers for their own units.