DEV Community

Aleksander Sekowski
Aleksander Sekowski

Posted on

Native Ads 1.2 vs imp.native.request: The Asset Object Your Bidder Never Parses

On October 23, 2026, IAB Tech Lab extended public comment on AAMP 3.0 and OpenProposal through November 10. The October AAMP info sheet is explicit that negotiated agentic deals still land in familiar execution rails: OpenDirect, the Deals API, or OpenRTB 2.6. Nothing in that comment window retires in-feed native. The impression your buyer agent eventually activates can still be imp.native with a request field that is a string, not the asset tree you pasted into a spec review.

That split is why two competent teams argue past each other. One side quotes OpenRTB Dynamic Native Ads 1.2 and talks about assets, eventtrackers, and required image types. The other side quotes OpenRTB 2.6 and talks about imp.native.request as opaque transport. Both documents are ratified. Only one of them is what your exchange JSON parser sees on the first pass.

Side one: Native Ads 1.2 is a structured creative contract

Native 1.2 (final July 2017) is its own API specification. It versions independently of OpenRTB. The object practitioners care about looks like a normal JSON document:

  • ver (for example "1.2")
  • assets[] with stable ids, required flags, and typed children (title, img, data, video)
  • 1.2-only request features such as eventtrackers, privacy, and third-party assetsurl flows

An asset is not a loose bag of keys. A main image is requested under img with type: 3 (Main Image in the Native Image Asset Types list). Icon versus main versus data type 2 (description) are fill killers when the bid returns the wrong subtype, even if the headline length looks fine.

Native 1.2 also defines the response shape: link, assets with ids that match the request, and optional privacy URL. Exchanges express mismatch as loss reasons. Equativ code 1002 is literally "native response does not match the native request." That is not a generic creative rejection. It is the native contract failing after the auction already picked a seat.

If your workflow stops at "we built the 1.2 object in a spreadsheet," you have described the creative. You have not described what crossed the wire.

Side two: OpenRTB treats that contract as an embedded string

OpenRTB 2.x does not nest the Native 1.2 object as a first-class child of imp.native. The core spec requires imp.native.request to be a JSON-encoded string: the Native markup serialized, escaped, and placed inside the bid request JSON.

The bid request field reference states this plainly: when native is present, native.request is required and it is not a nested object. Many SDKs therefore bind request to string and stop. A bidder that unmarshals the outer OpenRTB document correctly can still never execute a second JSON.parse on request, which means required assets never enter targeting or validation logic.

The response side mirrors the request side. Standard practice puts the Native 1.2 response JSON back into bid.adm as another string, with OpenRTB 2.6 using mtype: 4 when the exchange expects native markup. Some stacks emit a direct object in an extension field; that is exchange-specific, not something you can assume from the Native spec alone.

OpenRTB 2.2 and later 2.x snapshots kept this transport model even as video gained AdCOM enums elsewhere. If you are diffing OpenRTB 2.2 against 2.6 for native, the string encoding is the constant, not the footnote.

Protobuf dialects add a parallel form (request_native as a structured message in proto-JSON). A path that converts between spec-JSON strings and proto objects without reconciling both copies is another native silent failure mode. The comparison that matters for fill is still Native 1.2 semantics versus whatever field name holds the string today.

What breaks when you treat them as one thing

Double encoding is the classic bug: the Native object is stringified twice, so the first parse yields another string instead of { "ver": "1.2", "assets": [...] }. Downstream code sees a valid OpenRTB impression and an empty asset list. RTBlint reports this as openrtb.native.request.double_encoded on the request path and a sibling rule on adm when the creative side does it.

Schema-valid wrong shape is the silent variant: imp.native.request is typed as object in a generated struct because "obviously native is JSON." The exchange accepts the payload. Your bidder reads {} or default values. Required title assets never block bad bids because they were never loaded.

Response-side JSON versus object breaks cross-validation: the request string asks for img type 3 at 1200x627, the adm string returns type 1 icon art, and the exchange loss reason is 1002 while your internal linter still says the bid JSON is well formed.

Agentic buying does not bypass any of this. AAMP and OpenProposal may negotiate packages in August or October drafts, but the agentic standards guide still ends in OpenRTB objects and VAST tags humans already debug. An MCP tool that returns "OK" on a task is not a substitute for opening the bid request and reading the native string.

How you catch it

An LLM implementing AdCP, ARTF, or AAMP can hallucinate a native asset id or skip the second parse and still return success from the agent RPC. The wire contract does not move. rtblint (cargo install rtblint, npm install rtblint-core, or the MCP server docs) is the deterministic check on the OpenRTB object: it walks imp.native.request, decodes Native 1.2 markup, pairs response adm against the request when you validate both sides, and flags double encoding before traffic hits a loss endpoint. It is independent of IAB Tech Lab and of AAO; it is not an official harness and the specs do not require it.

When the native contract includes a video asset, the creative inside adm is often VAST, not just JSON fields. Pull that markup out and run the VAST tag tester on the live URL or pasted XML for preview and tracking, then the VAST inspector if the response wraps hops before InLine. vastlint owns that creative payload the same way rtblint owns the auction object.

Run rtblint on sampled bid requests from your adapter, not on the pretty-printed Native object in your creative tool. The string in production is the source of truth.

Where to go next

Native 1.2 is the creative law. imp.native.request is how OpenRTB carries that law. Compare them on every path that claims to validate native, or you are auditing a document while the bidder never reads it.

Top comments (0)