Skip to content

optable-targeting new features: non-blocking early network call + hid + id5 - #4601

Open
justadreamer wants to merge 74 commits into
prebid:masterfrom
Optable:optable-targeting-features
Open

justadreamer wants to merge 74 commits into
prebid:masterfrom
Optable:optable-targeting-features

Conversation

@justadreamer

@justadreamer justadreamer commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

Type of changes

  • module update

What's the context?

Two related improvements to the optable-targeting module: the Optable API call no longer blocks the auction pipeline, and the Targeting API request now carries the parameters needed for mobile in-app resolution (hid, bundle/ver, ID5).

Latency: non-blocking early network call

The API call starts at raw-auction-request and runs in parallel with the rest of the auction. The result is consumed per bidder at bidder-request, where enrichment is actually injected, so the pipeline never waits on the network.

Stored requests and stored imps are merged after raw-auction-request, so for requests whose bidders or site/app live there (f.e. Prebid Mobile SDK traffic) the call is started by processed-auction-request on the merged request instead. Bidders are read from both imp.ext.prebid.bidder and the old-style imp.ext.<bidder>.

Traffic controls

  • enrichment-percentage / bidder-enrichment-percentages: sampling rate (0-100) applied per bidder. A bidder with no entry falls back to the account default; 0 disables it entirely.
  • enrich-web / enrich-app: select which traffic sources are enriched.

Sampling applies to the split plan only. The legacy processed-auction-request hook enriches the whole bid request before bidders split out, so it cannot sample per bidder.

Targeting API request parameters

  • hid-prefixes (new config entry): comma-separated id prefixes to emit as hid=, in the given order (e.g. "e,p,i6,a,g,c"). Independent of id-prefix-order, and selective rather than merely ordering: only the listed prefixes are sent as hid.
  • bundle / ver: emitted from bidRequest.app when present.
  • id5_signature in: read from user.ext.optable, forwarded as a Targeting API query attribute alongside email/phone/zip/vid.
  • id5_signature out: returned in ext.prebid.passthrough.optable.id5_signature. PBS cannot persist the signature between requests, so it round-trips through the client, which caches it and replays it on the next auction.

Backwards compatibility

No breaking changes. The processed-auction-request hook stays in the plan. When the raw-auction-request and bidder-request hooks are present it no longer blocks and only starts the call for stored-request traffic; otherwise it keeps the previous synchronous behavior.

Configuration (recommended)

hooks:
  modules:
    optable-targeting:
      enrichment-percentage: 100
      bidder-enrichment-percentages:
        appnexus: 75
        pubmatic: 0
      enrich-web: true
      enrich-app: true
      id-prefix-order: "e,v,c"
      hid-prefixes: "a,c,i6"

  execution-plan:
    endpoints:
      "/openrtb2/auction":
        stages:
          raw-auction-request:
            groups:
              - timeout: 50
                hook-sequence:
                  - module-code: "optable-targeting"
                    hook-impl-code: "optable-targeting-raw-auction-request-hook"
          processed-auction-request:
            groups:
              - timeout: 50
                hook-sequence:
                  - module-code: "optable-targeting"
                    hook-impl-code: "optable-targeting-processed-auction-request-hook"
          bidder-request:
            groups:
              - timeout: 500
                hook-sequence:
                  - module-code: "optable-targeting"
                    hook-impl-code: "optable-targeting-bidder-request-hook"
          auction-response:
            groups:
              - timeout: 10
                hook-sequence:
                  - module-code: "optable-targeting"
                    hook-impl-code: "optable-targeting-auction-response-hook"

The bidder-request group timeout bounds the wait on the API call, so it must accommodate round-trip latency to the Optable edge. The raw-auction-request and processed-auction-request timeouts only cover dispatch.

To migrate from the legacy plan, keep the processed-auction-request hook and add the raw-auction-request and bidder-request hooks.

Test plan

  • Unit tests
  • Semi-automated integration tests against the live Optable edge, covering enrichment with eids, per-bidder sampling (including a bidder pinned at 0%), ad server targeting keywords, ID5 signature passthrough, and the legacy execution plan

Quality check

  • Are your changes following our code style guidelines?
  • Are there any breaking changes in your code? No
  • Does your test coverage exceed 90%?
  • Are there any erroneous console logs, debuggers or leftover code in your changes? No

cc: @jrosendahl-opt @softcoder594

softcoder and others added 30 commits August 13, 2026 22:38
return early from the hook if (hasRawAuctionRequestHook && hasBidderRequestHook) - the new mode
random is 0-99, percentage is 0-100.  enrich when random < (strictly less than) percentage.  so if we specify percentage 0 we never enrich, if we specify percentage 100 we always enrich.
…ntage property is specified and it's value is 100%
The account config node is deserialized with the prebid mapper, which uses
SNAKE_CASE, so the unannotated fields bound to enrich_web and enrich_app while
the documented account keys are enrich-web and enrich-app. Per-account
overrides of both were silently dropped and the global value kept.
…change

Core changed ExecutionPlan.endpoints key type from Endpoint to
HookHttpEndpoint. Map.get accepts Object, so the stale lookup kept
compiling but always returned null against the EnumMap: the module read
a bidder-request hook timeout of 0, so the targeting API call inherited
only the raw-auction-request stage budget (50ms) instead of the
configured 500ms and timed out on every auction.
@softcoder594
softcoder594 force-pushed the optable-targeting-features branch from 862374e to f7a664f Compare September 30, 2026 17:46
justadreamer and others added 11 commits September 30, 2026 21:07
An account can have the module hooks in its execution plan without a
module config of its own. ConfigResolver passed the null config to the
JSON merger, which threw and failed every hook of the module.
The fallback looked for imp.ext.bidder, which is not a format the core
knows. Old-style params sit directly under imp.ext and the core moves
them to imp.ext.prebid.bidder for every imp that has no bidders there,
so the sampler now applies the same rule per imp instead of falling back
only when no imp at all has imp.ext.prebid.bidder.
An account config with "enrichment-percentage": null or
"bidder-enrichment-percentages": null overrides the defaults of the
properties class, and the sampler then failed on unboxing.
Bidders from stored imps are never enriched when other imps name bidders inline. If one imp lists its bidders and another only references a stored imp, the raw hook doesn't defer. This affects mixed Prebid Mobile and web setups.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant