Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
66101f3
Add Start.io User ID submodule with tests and documentation
IlliaMil Feb 3, 2026
6ff92f8
Merge branch 'refs/heads/master' into DEP-11967-Cookie-Sync-Prebid.js…
IlliaMil Feb 3, 2026
7db3472
Update Start.io User ID module to ensure callbacks and AJAX requests …
IlliaMil Feb 3, 2026
8bbf781
Remove storage-related functionality from Start.io ID submodule and a…
IlliaMil Feb 3, 2026
4f2aee9
Add iframe-based user syncing to Start.io Bid Adapter with consent pa…
IlliaMil Feb 5, 2026
d6e0c98
Simplify Start.io ID module by removing storage-related parameters an…
IlliaMil Feb 5, 2026
e7774d7
Update Start.io modules to use new endpoint URL and improve user sync…
IlliaMil Feb 9, 2026
8e9a6bd
Fix documentation
Feb 10, 2026
90386aa
Enhance Start.io ID module with storage management, caching, and impr…
IlliaMil Feb 10, 2026
827bac2
Merge remote-tracking branch 'origin/DEP-11967-Cookie-Sync-Prebid.js-…
IlliaMil Feb 10, 2026
9adb313
Remove window exposure of startioAdapterSpec for browser testing purp…
IlliaMil Feb 10, 2026
4b004ba
Update Start.io Bid Adapter to clarify prebid params for iframe-based…
IlliaMil Feb 10, 2026
41e3fff
Update Start.io Bid Adapter to clarify prebid params for iframe-based…
IlliaMil Feb 10, 2026
127201c
id fixed to uid and updated
IlliaMil Feb 10, 2026
b9babdf
docs updated
IlliaMil Feb 10, 2026
9331767
Make `storeId` and `fetchIdFromServer` functions configurable with `e…
IlliaMil Feb 10, 2026
3c22f00
debugger removed
IlliaMil Feb 10, 2026
a8fcb95
test
IlliaMil Feb 10, 2026
f7248ee
Extend default cookie expiration for `storeId` from 9 to 90 days.
IlliaMil Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion modules/startioBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js';
import { logError, isFn, isPlainObject } from '../src/utils.js';
import { logError, isFn, isPlainObject, formatQS } from '../src/utils.js';
import { ortbConverter } from '../libraries/ortbConverter/converter.js'
import { ortb25Translator } from '../libraries/ortb2.5Translator/translator.js';
import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js';

const BIDDER_CODE = 'startio';
const METHOD = 'POST';
const GVLID = 1216;
const ENDPOINT_URL = `https://pbc-rtb.startappnetwork.com/1.3/2.5/getbid?account=pbc`;
const IFRAME_URL = 'https://cs.startappnetwork.com/sync?p=1002';

const converter = ortbConverter({
imp(buildImp, bidRequest, context) {
Expand Down Expand Up @@ -151,6 +153,23 @@ export const spec = {
},

onSetTargeting: (bid) => { },

getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) {
const syncs = [];

if (syncOptions.iframeEnabled) {
const consentParams = getUserSyncParams(gdprConsent, uspConsent, gppConsent);
const queryString = formatQS(consentParams);
const queryParam = queryString ? `&${queryString}` : '';

syncs.push({
type: 'iframe',
url: `${IFRAME_URL}${queryParam}`
});
}

return syncs;
}
};

registerBidder(spec);
28 changes: 24 additions & 4 deletions modules/startioBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var adUnits = [
bidder: 'startio',
params: {
// REQUIRED - Publisher Account ID
accountId: 'your-account-id',
publisherId: 'your-account-id',

// OPTIONAL - Enable test ads
testAdsEnabled: true
Expand Down Expand Up @@ -58,7 +58,7 @@ var videoAdUnits = [
{
bidder: 'startio',
params: {
accountId: 'your-account-id',
publisherId: 'your-account-id',
testAdsEnabled: true
}
}
Expand All @@ -85,7 +85,7 @@ var nativeAdUnits = [
{
bidder: 'startio',
params: {
accountId: 'your-account-id',
publisherId: 'your-account-id',
testAdsEnabled: true
}
}
Expand All @@ -94,8 +94,28 @@ var nativeAdUnits = [
];
```

### Prebid Params Enabling User Sync

To enable iframe-based user syncing for Start.io, include the `filterSettings` configuration in your `userSync` setup:

```javascript
pbjs.setConfig({
userSync: {
userIds: [{
name: 'startioId'
}],
filterSettings: {
iframe: {
bidders: ['startio'],
filter: 'include'
}
}
}
});
```

# Additional Notes
- The adapter processes requests via OpenRTB 2.5 standards.
- Ensure that the `accountId` parameter is set correctly for your integration.
- Ensure that the `publisherId` parameter is set correctly for your integration.
- Test ads can be enabled using `testAdsEnabled: true` during development.
- The adapter supports multiple ad formats, allowing publishers to serve banners, native ads and instream video ads seamlessly.
106 changes: 106 additions & 0 deletions modules/startioSystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* This module adds startio ID support to the User ID module
* The {@link module:modules/userId} module is required
* @module modules/startioSystem
* @requires module:modules/userId
*/
import { logError } from '../src/utils.js';
import { submodule } from '../src/hook.js';
import { ajax } from '../src/ajax.js';
import { getStorageManager } from '../src/storageManager.js';
import { MODULE_TYPE_UID } from '../src/activities/modules.js';

const MODULE_NAME = 'startioId';
const DEFAULT_ENDPOINT = 'https://cs.startappnetwork.com/get-uid-obj?p=1002';

const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME});

function getCachedId() {
let cachedId;

if (storage.cookiesAreEnabled()) {
cachedId = storage.getCookie(MODULE_NAME);
}

if (!cachedId && storage.hasLocalStorage()) {
const expirationStr = storage.getDataFromLocalStorage(`${MODULE_NAME}_exp`);
if (expirationStr) {
const expirationDate = new Date(expirationStr);
if (expirationDate > new Date()) {
cachedId = storage.getDataFromLocalStorage(MODULE_NAME);
}
}
}

return cachedId || null;
}

function storeId(id, expiresInDays) {
expiresInDays = expiresInDays || 90;
const expirationDate = new Date(Date.now() + expiresInDays * 24 * 60 * 60 * 1000).toUTCString();

if (storage.cookiesAreEnabled()) {
storage.setCookie(MODULE_NAME, id, expirationDate, 'None');
}

if (storage.hasLocalStorage()) {
storage.setDataInLocalStorage(`${MODULE_NAME}_exp`, expirationDate);
storage.setDataInLocalStorage(MODULE_NAME, id);
}
}

function fetchIdFromServer(callback, expiresInDays) {
const callbacks = {
success: response => {
let responseId;
try {
const responseObj = JSON.parse(response);
if (responseObj && responseObj.uid) {
responseId = responseObj.uid;
storeId(responseId, expiresInDays);
} else {
logError(`${MODULE_NAME}: Server response missing 'uid' field`);
}
} catch (error) {
logError(`${MODULE_NAME}: Error parsing server response`, error);
}
callback(responseId);
},
error: error => {
logError(`${MODULE_NAME}: ID fetch encountered an error`, error);
callback();
}
};
ajax(DEFAULT_ENDPOINT, callbacks, undefined, { method: 'GET' });
}

export const startioIdSubmodule = {
name: MODULE_NAME,
decode(value) {
return value && typeof value === 'string'
? { 'startioId': value }
: undefined;
},
getId(config, consentData, storedId) {
if (storedId) {
return { id: storedId };
}

const cachedId = getCachedId();
if (cachedId) {
return { id: cachedId };
}
const storageConfig = config && config.storage;
const expiresInDays = storageConfig && storageConfig.expires;
return { callback: (cb) => fetchIdFromServer(cb, expiresInDays) };
},

eids: {
'startioId': {
source: 'start.io',
atype: 3
},
}
};

submodule('userId', startioIdSubmodule);
35 changes: 35 additions & 0 deletions modules/startioSystem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Start.io User ID Submodule

The Start.io User ID submodule generates and persists a unique user identifier by fetching it from a publisher-supplied endpoint. The ID is stored in both cookies and local storage for subsequent page loads and is made available to other Prebid.js modules via the standard `eids` interface.

For integration support, contact prebid@start.io.

### Prebid Params Enabling User Sync

To enable iframe-based user syncing for Start.io, include the `filterSettings` configuration in your `userSync` setup:

```javascript
pbjs.setConfig({
userSync: {
userIds: [{
name: 'startioId'
}],
filterSettings: {
iframe: {
bidders: ['startio'],
filter: 'include'
}
}
}
});
```

This configuration allows Start.io to sync user data via iframe, which is necessary for cross-domain user identification.

## Parameter Descriptions for the `userSync` Configuration Section

The below parameters apply only to the Start.io User ID integration.

| Param under userSync.userIds[] | Scope | Type | Description | Example |
| --- | --- | --- | --- | --- |
| name | Required | String | The name of this module. | `"startioId"` |
94 changes: 94 additions & 0 deletions test/spec/modules/startioBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,4 +370,98 @@ describe('Prebid Adapter: Startio', function () {
});
}
});

describe('getUserSyncs', function () {
it('should return an iframe sync when iframeEnabled is true', function () {
const syncs = spec.getUserSyncs({ iframeEnabled: true }, []);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].type).to.equal('iframe');
expect(syncs[0].url).to.be.a('string');
});

it('should return an empty array when iframeEnabled is false', function () {
const syncs = spec.getUserSyncs({ iframeEnabled: false }, []);

expect(syncs).to.have.lengthOf(0);
});

it('should return an empty array when syncOptions is empty', function () {
const syncs = spec.getUserSyncs({}, []);

expect(syncs).to.have.lengthOf(0);
});

it('should append GDPR consent params to the sync URL', function () {
const gdprConsent = {
gdprApplies: true,
consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='
};

const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.include('gdpr=1');
expect(syncs[0].url).to.include('gdpr_consent=BOJ/P2HOJ/P2HABABMAAAAAZ+A==');
});

it('should append gdpr=0 when gdprApplies is false', function () {
const gdprConsent = {
gdprApplies: false,
consentString: ''
};

const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent);

expect(syncs[0].url).to.include('gdpr=0');
});

it('should append USP consent param to the sync URL', function () {
const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, '1YNN');

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.include('us_privacy=1YNN');
});

it('should append GPP consent params to the sync URL', function () {
const gppConsent = {
gppString: 'DBABMA~BAAAAAAAAgA.QA',
applicableSections: [7, 8]
};

const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, undefined, gppConsent);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.include('gpp=DBABMA~BAAAAAAAAgA.QA');
expect(syncs[0].url).to.include('gpp_sid=7,8');
});

it('should append all consent params together when all are provided', function () {
const gdprConsent = {
gdprApplies: true,
consentString: 'testConsent'
};
const uspConsent = '1YNN';
const gppConsent = {
gppString: 'testGpp',
applicableSections: [2]
};

const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent, uspConsent, gppConsent);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.include('gdpr=1');
expect(syncs[0].url).to.include('gdpr_consent=testConsent');
expect(syncs[0].url).to.include('us_privacy=1YNN');
expect(syncs[0].url).to.include('gpp=testGpp');
expect(syncs[0].url).to.include('gpp_sid=2');
});

it('should not append query string when no consent params are provided', function () {
const syncs = spec.getUserSyncs({ iframeEnabled: true }, []);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.equal('https://cs.startappnetwork.com/sync?p=1002');
});
});
});
Loading
Loading