diff --git a/modules/deepintentBidAdapter.js b/modules/deepintentBidAdapter.js
new file mode 100644
index 00000000000..27aa4e2c8a6
--- /dev/null
+++ b/modules/deepintentBidAdapter.js
@@ -0,0 +1,142 @@
+import {registerBidder} from '../src/adapters/bidderFactory';
+import {BANNER} from '../src/mediaTypes';
+import * as utils from '../src/utils';
+const BIDDER_CODE = 'deepintent';
+const BIDDER_ENDPOINT = 'https://prebid.deepintent.com/prebid';
+const USER_SYNC_URL = 'https://beacon.deepintent.com/usersync.html';
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [BANNER],
+ aliases: [],
+
+ // tagId is mandatory param
+ isBidRequestValid: bid => {
+ let valid = false;
+ if (bid && bid.params && bid.params.tagId) {
+ if (typeof bid.params.tagId === 'string' || bid.params.tagId instanceof String) {
+ valid = true;
+ }
+ }
+ return valid;
+ },
+ interpretResponse: function(bidResponse, request) {
+ let responses = [];
+ if (bidResponse && bidResponse.body) {
+ let bids = bidResponse.body.seatbid && bidResponse.body.seatbid[0] ? bidResponse.body.seatbid[0].bid : [];
+ responses = bids.map(bid => formatResponse(bid))
+ }
+ return responses;
+ },
+ buildRequests: function (validBidRequests, bidderRequest) {
+ const openRtbBidRequest = {
+ id: utils.generateUUID(),
+ at: 1,
+ imp: validBidRequests.map(bid => buildImpression(bid)),
+ site: buildSite(bidderRequest),
+ device: buildDevice(),
+ source: {
+ fd: 0,
+ ext: {
+ type: 2
+ }
+ }
+ };
+
+ return {
+ method: 'POST',
+ url: BIDDER_ENDPOINT,
+ data: JSON.stringify(openRtbBidRequest),
+ options: {
+ contentType: 'application/json'
+ }
+ };
+ },
+ /**
+ * Register User Sync.
+ */
+ getUserSyncs: syncOptions => {
+ if (syncOptions.iframeEnabled) {
+ return [{
+ type: 'iframe',
+ url: USER_SYNC_URL
+ }];
+ }
+ }
+
+};
+
+function formatResponse(bid) {
+ return {
+ requestId: bid && bid.impid ? bid.impid : undefined,
+ cpm: bid && bid.price ? bid.price : 0.0,
+ width: bid && bid.w ? bid.w : 0,
+ height: bid && bid.h ? bid.h : 0,
+ ad: bid && bid.adm ? bid.adm : '',
+ creativeId: bid && bid.crid ? bid.crid : undefined,
+ netRevenue: false,
+ currency: bid && bid.cur ? bid.cur : 'USD',
+ ttl: 300,
+ dealId: bid && bid.dealId ? bid.dealId : undefined
+ }
+}
+
+function buildImpression(bid) {
+ return {
+ id: bid.bidId,
+ tagid: bid.params.tagId || '',
+ secure: window.location.protocol === 'https' ? 1 : 0,
+ banner: buildBanner(bid),
+ ext: bid.params.custom ? bid.params.custom : {}
+ };
+}
+
+function buildBanner(bid) {
+ if (utils.deepAccess(bid, 'mediaTypes.banner')) {
+ // Get Sizes from MediaTypes Object, Will always take first size, will be overrided by params for exact w,h
+ if (utils.deepAccess(bid, 'mediaTypes.banner.sizes') && !bid.params.height && !bid.params.width) {
+ let sizes = utils.deepAccess(bid, 'mediaTypes.banner.sizes');
+ if (utils.isArray(sizes) && sizes.length > 0) {
+ return {
+ h: sizes[0][1],
+ w: sizes[0][0]
+ }
+ }
+ } else {
+ return {
+ h: bid.params.height,
+ w: bid.params.width
+ }
+ }
+ }
+}
+
+function buildSite(bidderRequest) {
+ let site = {};
+ if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) {
+ site.page = bidderRequest.refererInfo.referer;
+ site.domain = getDomain(bidderRequest.refererInfo.referer);
+ }
+ return site;
+}
+
+function getDomain(referer) {
+ if (referer) {
+ let domainA = document.createElement('a');
+ domainA.href = referer;
+ return domainA.hostname;
+ }
+}
+
+function buildDevice() {
+ return {
+ ua: navigator.userAgent,
+ js: 1,
+ dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack === '1') ? 1 : 0,
+ h: screen.height,
+ w: screen.width,
+ language: navigator.language
+ }
+}
+
+registerBidder(spec);
diff --git a/modules/deepintentBidAdapter.md b/modules/deepintentBidAdapter.md
new file mode 100644
index 00000000000..7f5afbd233a
--- /dev/null
+++ b/modules/deepintentBidAdapter.md
@@ -0,0 +1,53 @@
+# Overview
+
+```
+Module Name: Deepintent Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: prebid@deepintent.com
+```
+
+# Description
+
+Deepintent currently supports the BANNER type ads through prebid js
+
+Module that connects to Deepintent's demand sources.
+
+# Banner Test Request
+```
+ var adUnits = [
+ {
+ code: 'di_adUnit1',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]], // a display size, only first one will be picked up since multiple ad sizes are not supported yet
+ }
+ }
+ bids: [
+ {
+ bidder: 'deepintent',
+ params: {
+ tagId: '1300', // Required parameter
+ w: 300, // Width and Height here will override sizes in mediatype
+ h: 250,
+ custom: { // Custom parameters in form of key value pairs
+ user_min_age: 18
+ }
+ }
+ }
+ ]
+ }
+ ];
+```
+
+###Recommended User Sync Configuration
+
+```javascript
+pbjs.setConfig({
+ userSync: {
+ iframeEnabled: true,
+ enabledBidders: ['deepintent'],
+ syncDelay: 3000
+ }});
+
+
+```
diff --git a/test/spec/modules/deepintentBidAdapter_spec.js b/test/spec/modules/deepintentBidAdapter_spec.js
new file mode 100644
index 00000000000..353a815eb8c
--- /dev/null
+++ b/test/spec/modules/deepintentBidAdapter_spec.js
@@ -0,0 +1,162 @@
+import {expect} from 'chai';
+import {spec} from 'modules/deepintentBidAdapter';
+import * as utils from '../../../src/utils';
+
+describe('Deepintent adapter', function () {
+ let request;
+ let bannerResponse;
+
+ beforeEach(function () {
+ request = [
+ {
+ bidder: 'deepintent',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
+ params: {
+ tagId: '100013',
+ w: 728,
+ h: 90,
+ custom: {
+ user_gender: 'female',
+ user_max_age: 25
+ }
+ }
+ }
+ ];
+ bannerResponse = {
+ 'body': {
+ 'id': '303e1fae-9677-41e2-9a92-15a23445363f',
+ 'seatbid': [{
+ 'bid': [{
+ 'id': '11447bb1-a266-470d-b0d7-8810f5b1b75f',
+ 'impid': 'a7e92b9b-d9db-4de8-9c3f-f90737335445',
+ 'price': 0.6,
+ 'adid': '10001',
+ 'adm': "\r\n",
+ 'adomain': ['deepintent.com'],
+ 'cid': '103389',
+ 'crid': '13665',
+ 'w': 300,
+ 'h': 250,
+ 'dealId': 'dee_12312stdszzsx'
+ }],
+ 'seat': '10000'
+ }],
+ 'bidid': '0b08b09f-aaa1-4c14-b1c8-7debb1a7c1cd'
+ }
+ }
+ });
+
+ describe('validations', function () {
+ it('validBid : tagId is passed', function () {
+ let bid = {
+ bidder: 'deepintent',
+ params: {
+ tagId: '1232'
+ }
+ },
+ isValid = spec.isBidRequestValid(bid);
+ expect(isValid).to.equals(true);
+ });
+ it('invalidBid : tagId is not passed', function () {
+ let bid = {
+ bidder: 'deepintent',
+ params: {
+ h: 200,
+ w: 300
+ }
+ },
+ isValid = spec.isBidRequestValid(bid);
+ expect(isValid).to.equals(false);
+ });
+ it('invalidBid : tagId is not a string', function () {
+ let bid = {
+ bidder: 'deepintent',
+ params: {
+ tagId: 12345
+ }
+ },
+ isValid = spec.isBidRequestValid(bid);
+ expect(isValid).to.equals(false);
+ });
+ });
+ describe('request check', function () {
+ it('unmutaable bid request check', function () {
+ let oRequest = utils.deepClone(request),
+ bidRequest = spec.buildRequests(request);
+ expect(request).to.deep.equal(oRequest);
+ });
+ it('bidder connection check', function () {
+ let bRequest = spec.buildRequests(request);
+ expect(bRequest.url).to.equal('https://prebid.deepintent.com/prebid');
+ expect(bRequest.method).to.equal('POST');
+ expect(bRequest.options.contentType).to.equal('application/json');
+ });
+ it('bid request check : Device', function () {
+ let bRequest = spec.buildRequests(request);
+ let data = JSON.parse(bRequest.data);
+ expect(data.device.ua).to.be.a('string');
+ expect(data.device.js).to.equal(1);
+ expect(data.device.dnt).to.be.a('number');
+ expect(data.device.h).to.be.a('number');
+ expect(data.device.w).to.be.a('number');
+ });
+ it('bid request check : Impression', function () {
+ let bRequest = spec.buildRequests(request);
+ let data = JSON.parse(bRequest.data);
+ expect(data.at).to.equal(1); // auction type
+ expect(data.imp[0].id).to.equal(request[0].bidId);
+ expect(data.imp[0].tagid).to.equal('100013');
+ });
+ it('bid request check : ad size', function () {
+ let bRequest = spec.buildRequests(request);
+ let data = JSON.parse(bRequest.data);
+ expect(data.imp[0].banner).to.be.a('object');
+ expect(data.imp[0].banner.w).to.equal(300);
+ expect(data.imp[0].banner.h).to.equal(250);
+ });
+ it('bid request check : custom params', function () {
+ let bRequest = spec.buildRequests(request);
+ let data = JSON.parse(bRequest.data);
+ expect(data.imp[0].ext).to.be.a('object');
+ expect(data.imp[0].ext.user_gender).to.equal('female');
+ expect(data.imp[0].ext.user_max_age).to.equal(25);
+ });
+ it('bid request check: source params', function () {
+ let bRequest = spec.buildRequests(request);
+ let data = JSON.parse(bRequest.data);
+ expect(data.source.fd).to.equal(0);
+ expect(data.source.ext.type).to.equal(2);
+ })
+ });
+ describe('user sync check', function () {
+ it('user sync url check', function () {
+ let syncOptions = {
+ iframeEnabled: true
+ };
+ let userSync = spec.getUserSyncs(syncOptions);
+ expect(userSync).to.be.an('array').with.length.above(0);
+ expect(userSync[0].type).to.equal('iframe');
+ expect(userSync[0].url).to.equal('https://beacon.deepintent.com/usersync.html');
+ });
+ });
+ describe('response check', function () {
+ it('bid response check: valid bid response', function () {
+ let bRequest = spec.buildRequests(request);
+ let data = JSON.parse(bRequest.data);
+ let bResponse = spec.interpretResponse(bannerResponse, request);
+ expect(bResponse).to.be.an('array').with.length.above(0);
+ expect(bResponse[0].requestId).to.equal(bannerResponse.body.seatbid[0].bid[0].impid);
+ expect(bResponse[0].width).to.equal(bannerResponse.body.seatbid[0].bid[0].w);
+ expect(bResponse[0].height).to.equal(bannerResponse.body.seatbid[0].bid[0].h);
+ expect(bResponse[0].currency).to.equal('USD');
+ expect(bResponse[0].netRevenue).to.equal(false);
+ expect(bResponse[0].ttl).to.equal(300);
+ expect(bResponse[0].creativeId).to.equal(bannerResponse.body.seatbid[0].bid[0].crid);
+ expect(bResponse[0].dealId).to.equal(bannerResponse.body.seatbid[0].bid[0].dealId);
+ });
+ })
+});