Skip to content

Conversation

@bjoberg-amzn
Copy link

Overview

APS (Amazon Publisher Services) bid adapter initial open source release.

Type of change

  • Bugfix

  • Feature

  • New bidder adapter

  • Updated bidder adapter

  • Code style update (formatting, local variables)

  • Refactoring (no functional changes, no api changes)

  • Build related changes

  • CI related changes

  • Does this change affect user-facing APIs or examples documented on http://prebid.org?

  • Other

Description of change

  • (feat) Banner ad support
  • (feat) Video ad support
  • (feat) iframe user sync support
  • (feat) Telemetry and analytics
  • (docs) Integration guide

Other information

@bjoberg-amzn
Copy link
Author

This pull request is pending final verification and approval from Amazon and Prebid. Please keep this comment unresolved until explicit confirmation is received to proceed with the merge.

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

Tread carefully! This PR adds 6 linter warnings (possibly disabled through directives):

  • modules/apsBidAdapter.js (+6 warnings)

@ChrisHuie ChrisHuie requested a review from dgirardi December 9, 2025 21:58
@bjoberg-amzn
Copy link
Author

Tread carefully! This PR adds 6 linter warnings (possibly disabled through directives):

  • modules/apsBidAdapter.js (+6 warnings)

Noted, will resolve these warnings prior to merge.

@patmmccann patmmccann self-requested a review December 11, 2025 00:01
@patmmccann patmmccann self-assigned this Dec 11, 2025
url: endpoint,
data: converter.toORTB({ bidRequests, bidderRequest }),
options: {
contentType: 'application/json',

Check warning

Code scanning / CodeQL

Application/json request type in bidder

application/json request type triggers preflight requests and may increase bidder timeouts
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to fix this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged, will fix.


// Validate and process impressions - fail fast on structural issues
if (!request.imp || !Array.isArray(request.imp)) {
throw new Error('Request must contain a valid impressions array');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't allow throws, please fix to logError

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, we will fix this.

function record(eventName, data) {
// Check if telemetry is enabled
if (config.readConfig('aps.telemetry') === false) {
return;
Copy link
Collaborator

@patmmccann patmmccann Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about your record utility falling back to a beacon? Did you decide you didn't want to fire errors or other events except in the small prevent of the time aps is there?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this initial release, APS has decided not to fire operational metrics when APS is not on page. We have made this decision to ensure consistent monitoring with our existing systems. We plan to revisit this in our subsequent releases.

}

request.ext = request.ext ?? {};
request.ext.account = config.readConfig('aps.accountID');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please allow this to exist on ad unit config as well

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, we will make this available.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon further team review, APS is updating the previous response. This feature will move to a future adapter version. A separate release ensures both integration paths are tested without risk to existing features like operational metrics. Apologies for the change and thank you for your patience.


Connects to Amazon Publisher Services (APS) for bids.

# Usage
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instructions to force a test bid from the endpoint are often very useful here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. We will add those instructions.

record('getUserSyncs');
try {
const userSyncs = serverResponses.flatMap(
(res) => res?.body?.ext?.userSyncs ?? []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an opportunity to use the consent objects here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this suggestion. The latest revision now includes client-side validation in addition to existing server-side checks.

(s.type === 'image' && syncOptions.pixelEnabled)
);
} catch (err) {
record('getUserSyncs/didError', { error: err });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like another opportunity to fallback from the record when telemetry is false

bid.ad = `<script src="${creativeUrl}"></script>
<script>
const accountID = '${accountID}';
window._aps = window._aps || new Map();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks odd to me because the creative is (usually) rendered in a separate iframe and I don't expect this to be useful there. If you're just interested in recording when the render happens, onAdRenderSucceeded runs immediately after this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APS uses an event-driven architecture. The record statement here is not used for analytics but instead to trigger a callback in the APS creative render script (creativeUrl in the code snippet).

if (!doesHWExist && doesFormatExist) {
const { w, h } = imp.banner.format[0];
if (typeof w !== 'number' || typeof h !== 'number') {
throw new Error(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring unsuitable format / imps is better than throwing, as you may still get something you can use somewhere else in the request. It would align better with how isBidRequestValid filters out only invalid requests - this sort of validation is meant to be done there, although it doesn't work on ortb imp objects which is probably why you're doing it here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, we will fix this. Thanks!!

@ChrisHuie ChrisHuie requested review from gmcgrath11 and robertrmartinez and removed request for jsnellbaker December 11, 2025 19:13
Comment on lines 142 to 160
if (!imp.banner) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey sorry to point this out again, but just checking this short circuit is intentional. I didn't spot any related test context and it tosses out video bids.
Example request:
image

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the feedback. The short circuit is intended so that we only add the w and h properties to banner objects. Additional test context is now included to clarify this scenario. The test I am referring to is: "when imp array contains banner object with invalid format..."

const debugQueryChar = endpoint.includes('?') ? '&' : '?';
const renderMethod = config.readConfig('aps.renderMethod');
if (renderMethod === 'fif') {
endpoint += debugQueryChar + 'amzn_debug_mode=fif&amzn_debug_mode=1';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this intended to send same query param twice with different values?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! This is our way to pass multiple values for our debug scenario. We are working towards simplifying our test approach in the future.

@patmmccann
Copy link
Collaborator

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +166 to +188
let vastUrl;
if (bid.mtype === 2) {
vastUrl = bid.adm;
// Making sure no adm value is passed down to prevent issues with some renderers
delete bid.adm;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve video adm to keep inline VAST playable

Video responses delete bid.adm before delegating to the ORTB converter, then set vastUrl from that removed value. When APS returns inline VAST in adm (the usual ORTB case when no nurl is provided), the converter can no longer populate vastXml, and the bid is returned with no valid video creative (a VAST XML string ends up in vastUrl). In such responses the video ad will fail to render; keep adm available to buildBidResponse so inline VAST is preserved.

Useful? React with 👍 / 👎.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼 - this was useful. We acknowledge the feedback and will resolve this in a subsequent release.

bjoberg-amzn added a commit to bjoberg-amzn/Prebid.js that referenced this pull request Dec 16, 2025
bjoberg-amzn added a commit to bjoberg-amzn/Prebid.js that referenced this pull request Dec 17, 2025
bjoberg-amzn added a commit to bjoberg-amzn/Prebid.js that referenced this pull request Dec 17, 2025
bjoberg-amzn added a commit to bjoberg-amzn/Prebid.js that referenced this pull request Dec 18, 2025
**Overview**
------------
APS (Amazon Publisher Services) bid adapter initial open source release.

**Changes**
-----------
- (feat) Banner ad support
- (feat) Video ad support
- (feat) iframe user sync support
- (feat) Telemetry and analytics
- (docs) Integration guide
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants