Resonance

The decisioning layer between an ad request and a cleared bid.

Resonance is Adbustr's own decisioning path: it enriches the request, decides whether the traffic is worth an auction, picks the demand that can actually serve it, runs the fan-out inside tmax, and clears the winner. Written in Go, co-located with the auction, driven by configuration rather than code.

Enrich · Filter · Select · Clear

The thesis

An exchange is judged on one interval: the few hundred milliseconds between an ad request arriving and a cleared price leaving. Everything that decides the outcome — what the request is, whether it deserves demand, which partners see it, how the winner is priced — happens inside that interval, in one process.

Resonance is that process. It is not a scoring service bolted onto someone else's stack: enrichment, quality filtering, demand selection, fan-out, clearing and settlement are stages of the same request path, and each stage records why it did what it did.

How it works

Six stages between the request and the rendered creative.

01Enrich

Request enrichment

Every request — a format handler call or a raw OpenRTB BidRequest posted to /rtb/{uid} — is resolved to the real client IP and enriched with GeoIP (ISO2 country, region, city, coordinates) and with browser, OS and device class derived from the user-agent. Enrichment degrades gracefully: a missing asset narrows targeting precision, it never drops the request.

02Filter

Request validation gate

Eligibility is decided before any demand partner is contacted. device.ip and device.ua are mandatory; the user-agent must resolve into the browser allow-list; IAB25, IAB25-3 and IAB26 are cut on mainstream endpoints. Each rejection is written to the funnel log with its reason code. Resonance does not run IVT scoring, proxy or datacenter detection, bot filters, environment checks, or automated validation of counterparty supply files — see the Anti-Fraud Policy.

03Build

Canonical bid request

The request is normalised into a single OpenRTB 2.5 BidRequest: exactly one imp, a site or app object, device, user, source.ext.schain (SupplyChain 1.0), badv, imp.secure, tmax, cur and at. Video impressions carry fixed mimes, protocols [2,3,5,6], api [1,2], 1–150 s duration and 10–9000 kbps bitrate bounds, so partners receive one predictable shape instead of per-publisher dialects.

04Select

Demand selection

Demand is chosen per request, not per integration: partner active and public flags, geo list, permitted device types, zone allow and block lists, and a synced buyeruid for partners that only serve matched users. ReqFilter sets what share of eligible requests each partner actually sees, and per-partner request adjustments are applied before the call leaves the process.

05Fan-out

Parallel fan-out

Selected partners are queried in parallel inside one window — tmax, 300 to 500 ms by default and configurable per publisher and zone, with a 500 ms per-partner timeout. Responses that arrive after the window are discarded rather than held. Every outcome is instrumented per partner: requests, bids, timeouts, errors, latency.

06Clear

Ranking, clearing, notices

Bids are normalised to the base currency before they are compared: Norm = price × currency_rate × BidRate. Non-200 responses, empty bodies, unparseable JSON, price ≤ 0 and anything below the partner bidfloor are discarded. The winner clears at second price by default (at=2); first price (at=1) is available per partner. The clearing price is bounded below by the partner bidfloor and above by the winner's own bid, and the price actually recorded for each request is exposed in the impression-level logs. Settlement substitutes the price macros, calls the winner's nurl server-side and fires lurl on every valid losing bid with its loss code.

Auction parameters

The contract, stated in the same terms your engineers will test it in.

Wire contract
OpenRTB 2.5, inbound and outbound
Impressions per request
Exactly one imp
Auction window (tmax)
300–500 ms, per publisher / zone
Partner timeout
500 ms · late responses discarded
Auction type
Second price (at=2) · first price (at=1)
Ranking
Norm = price × currency_rate × BidRate
Currency
USD base · rates from config snapshot
Bid discards
non-200 · empty · invalid JSON · price ≤ 0 · below floor
Notices
nurl (server-side) · burl (billable) · lurl (loss)
Price macros
AUCTION_PRICE · AUCTION_CURRENCY · AUCTION_ID · AUCTION_LOSS

Economic levers — bidfloor, BidRate, ReqFilter and revenue share with per-country overrides — are read from the database and served from the configuration snapshot. They are configuration, not constants in the binary.

Reason codes

Every no-bid has a name.

A silent 204 is the least useful answer an exchange can give. Resonance writes a per-request funnel — verdict and reason — into ClickHouse alongside impression and click events, so an integration issue is diagnosed from the log rather than guessed at over email. A GET to /rtb/{uid} is treated as a connectivity ping, not a bid request.

unknown_endpoint
Endpoint uid unknown, or the endpoint is switched off
no_ip_ua
device.ip or device.ua missing from the request
browser_not_allowed
User-agent does not map into the browser allow-list
non_mainstream
IAB25 / IAB25-3 / IAB26 on a mainstream endpoint
no_imp
Empty imp array
no_feeds
No live demand feed attached to the endpoint
fanout_off
Demand toggle off — request accepted and logged, demand untouched
HTTP 400
Body does not parse as JSON
HTTP 204
No bid, for any of the reasons above

Defensibility

Why it's defensible.

Resonance is not a wrapper around someone else's pipeline. The exchange core is our own Go service, and the decisioning path runs inside the same process as the auction — no network hop between the decision and the bid it governs.

Behaviour is driven by a configuration snapshot in Redis, projected from the database and hot-reloaded through a version and reload channel: floors, bid rates, throttling, revenue share, demand routing and the endpoint live toggle change without a rebuild and without a restart. The endpoint toggle is fail-safe off — when demand is switched away, requests are still accepted and logged.

Everything that path decides is observable: per-partner request, bid, timeout, error and latency metrics in Prometheus and Grafana, and a per-request funnel with verdict and reason in ClickHouse. What we publish about the exchange, we can read back out of it — see the Transparency Center.