public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); // A conversion goal cannot be deleted, so even if this is a test // please choose an appropriate name accordingly. var offlineConversionGoalName = "My Offline Conversion Goal"; var conversionGoals = new ConversionGoal[] { new OfflineConversionGoal { // Determines how long after a click that you want to count offline conversions. ConversionWindowInMinutes = 43200, // If the count type is 'Unique' then only the first offline conversion will be counted. // By setting the count type to 'All', then all offline conversions for the same // MicrosoftClickId with different conversion times will be added cumulatively. CountType = ConversionGoalCountType.All, Name = offlineConversionGoalName, // The default conversion currency code and value. Each offline conversion can override it. Revenue = new ConversionGoalRevenue { CurrencyCode = null, Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = null }, }; OutputStatusMessage("-----\nAddConversionGoals:"); var addConversionGoalsResponse = await CampaignManagementExampleHelper.AddConversionGoalsAsync( conversionGoals : conversionGoals); var conversionGoalIds = addConversionGoalsResponse.ConversionGoalIds.ToArray(); BatchError[] conversionGoalErrors = addConversionGoalsResponse.PartialErrors.ToArray(); OutputStatusMessage("ConversionGoalIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(conversionGoalIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(conversionGoalErrors); List <long> goalIds = GetNonNullableIds(conversionGoalIds); var conversionGoalTypes = ConversionGoalType.OfflineConversion; OutputStatusMessage("-----\nGetConversionGoalsByIds:"); var getConversionGoalsResponse = (await CampaignManagementExampleHelper.GetConversionGoalsByIdsAsync( conversionGoalIds: goalIds, conversionGoalTypes: conversionGoalTypes)); var getConversionGoals = getConversionGoalsResponse.ConversionGoals; conversionGoalErrors = getConversionGoalsResponse.PartialErrors.ToArray(); OutputStatusMessage("ConversionGoals:"); CampaignManagementExampleHelper.OutputArrayOfConversionGoal(getConversionGoals); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(conversionGoalErrors); // Every time you create a new OfflineConversionGoal via either the Bing Ads web application or Campaign Management API, // the MSCLKIDAutoTaggingEnabled value of the corresponding AccountProperty is set to 'true' automatically. // We can confirm the setting now. var accountPropertyNames = new List <AccountPropertyName>(); accountPropertyNames.Add(AccountPropertyName.MSCLKIDAutoTaggingEnabled); OutputStatusMessage("-----\nGetAccountProperties:"); var getAccountPropertiesResponse = await CampaignManagementExampleHelper.GetAccountPropertiesAsync( accountPropertyNames : accountPropertyNames); var accountProperties = getAccountPropertiesResponse.AccountProperties; BatchError[] accountPropertiesErrors = getAccountPropertiesResponse.PartialErrors.ToArray(); OutputStatusMessage("AccountProperties:"); CampaignManagementExampleHelper.OutputArrayOfAccountProperty(accountProperties); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(accountPropertiesErrors); var offlineConversions = new[] { new OfflineConversion { // If you do not specify an offline conversion currency code, // then the 'CurrencyCode' element of the goal's 'ConversionGoalRevenue' is used. ConversionCurrencyCode = "USD", // The conversion name must match the 'Name' of the 'OfflineConversionGoal'. // If it does not match you won't observe any error, although the offline // conversion will not be counted. ConversionName = offlineConversionGoalName, // The date and time must be in UTC, should align to the date and time of the // recorded click (MicrosoftClickId), and cannot be in the future. ConversionTime = DateTime.UtcNow, // If you do not specify an offline conversion value, // then the 'Value' element of the goal's 'ConversionGoalRevenue' is used. ConversionValue = 10, MicrosoftClickId = "f894f652ea334e739002f7167ab8f8e3" } }; // After the OfflineConversionGoal is set up, wait two hours before submitting the offline conversions. // This example would not succeed in production because we created the goal very recently i.e., // please see above call to AddConversionGoalsAsync. OutputStatusMessage("-----\nApplyOfflineConversions:"); var applyOfflineConversionsResponse = await CampaignManagementExampleHelper.ApplyOfflineConversionsAsync( offlineConversions : offlineConversions); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(applyOfflineConversionsResponse.PartialErrors); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>(authorizationData, environment); // Before you can track conversions or target audiences using a remarketing list, // you need to create a UET tag in Bing Ads (web application or API) and then // add the UET tag tracking code to every page of your website. For more information, please see // Universal Event Tracking at https://docs.microsoft.com/en-us/bingads/guides/universal-event-tracking. // First you should call the GetUetTagsByIds operation to check whether a tag has already been created. // You can leave the TagIds element null or empty to request all UET tags available for the customer. var uetTags = (await CampaignManagementExampleHelper.GetUetTagsByIdsAsync(null)).UetTags; // If you do not already have a UET tag that can be used, or if you need another UET tag, // call the AddUetTags service operation to create a new UET tag. If the call is successful, // the tracking script that you should add to your website is included in a corresponding // UetTag within the response message. if (uetTags == null || uetTags.Count < 1) { var uetTag = new UetTag { Description = "My First Uet Tag", Name = "New Uet Tag", }; uetTags = (await CampaignManagementExampleHelper.AddUetTagsAsync(new[] { uetTag })).UetTags; } if (uetTags == null || uetTags.Count < 1) { OutputStatusMessage( string.Format("You do not have any UET tags registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } OutputStatusMessage("List of all UET Tags:\n"); foreach (var uetTag in uetTags) { CampaignManagementExampleHelper.OutputUetTag(uetTag); } // After you retreive the tracking script from the AddUetTags or GetUetTagsByIds operation, // the next step is to add the UET tag tracking code to your website. We recommend that you, // or your website administrator, add it to your entire website in either the head or body sections. // If your website has a master page, then that is the best place to add it because you add it once // and it is included on all pages. For more information, please see // Universal Event Tracking at https://docs.microsoft.com/en-us/bingads/guides/universal-event-tracking. // We will use the same UET tag for the remainder of this example. var tagId = uetTags[0].Id; // Optionally you can update the name and description of a UetTag with the UpdateUetTags operation. OutputStatusMessage("UET Tag BEFORE update:\n"); CampaignManagementExampleHelper.OutputUetTag(uetTags[0]); uetTags = new[] { new UetTag { Description = "Updated Uet Tag Description", Id = tagId, Name = "Updated Uet Tag Name " + DateTime.UtcNow, } }; await CampaignManagementExampleHelper.UpdateUetTagsAsync(uetTags); uetTags = (await CampaignManagementExampleHelper.GetUetTagsByIdsAsync(new[] { (long)tagId })).UetTags; OutputStatusMessage("UET Tag AFTER update:\n"); CampaignManagementExampleHelper.OutputUetTag(uetTags[0]); // Add conversion goals that depend on the UET Tag Id retreived above. // Please note that you cannot delete conversion goals. If you want to stop // tracking conversions for the goal, you can set the goal status to Paused. var conversionGoals = new ConversionGoal[] { new DurationGoal { ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, MinimumDurationInSeconds = 60, Name = "My Duration Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = tagId, }, new EventGoal { // The type of user interaction you want to track. ActionExpression = "play", ActionOperator = ExpressionOperator.Contains, // The category of event you want to track. CategoryExpression = "video", CategoryOperator = ExpressionOperator.Contains, ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, // The name of the element that caused the action. LabelExpression = "trailer", LabelOperator = ExpressionOperator.Contains, Name = "My Event Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = tagId, // A numerical value associated with that event. // Could be length of the video played etc. Value = 5.00m, ValueOperator = ValueOperator.Equals, }, new PagesViewedPerVisitGoal { ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, MinimumPagesViewed = 5, Name = "My Pages Viewed Per Visit Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = tagId, }, new UrlGoal { ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, Name = "My Url Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, Scope = EntityScope.Account, Status = ConversionGoalStatus.Active, TagId = tagId, UrlExpression = "contoso", UrlOperator = ExpressionOperator.Contains }, new AppInstallGoal { // You must provide a valid app platform and app store identifier, // otherwise this goal will not be added successfully. AppPlatform = "Windows", AppStoreId = "AppStoreIdGoesHere", ConversionWindowInMinutes = 30, CountType = ConversionGoalCountType.All, Name = "My App Install Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, // Account scope is not supported for app install goals. You can // set scope to Customer or don't set it for the same result. Scope = EntityScope.Customer, Status = ConversionGoalStatus.Active, // The TagId is inherited from the ConversionGoal base class, // however, App Install goals do not use a UET tag. TagId = null, }, }; var addConversionGoalsResponse = await CampaignManagementExampleHelper.AddConversionGoalsAsync(conversionGoals); // Find the conversion goals that were added successfully. List <long> conversionGoalIds = GetNonNullableIds(addConversionGoalsResponse.ConversionGoalIds); OutputStatusMessage("List of errors returned from AddConversionGoals (if any):\n"); CampaignManagementExampleHelper.OutputArrayOfBatchError(addConversionGoalsResponse.PartialErrors); var conversionGoalTypes = ConversionGoalType.AppInstall | ConversionGoalType.Duration | ConversionGoalType.Event | ConversionGoalType.PagesViewedPerVisit | ConversionGoalType.Url; var getConversionGoals = (await CampaignManagementExampleHelper.GetConversionGoalsByIdsAsync(conversionGoalIds, conversionGoalTypes)).ConversionGoals; OutputStatusMessage("List of conversion goals BEFORE update:\n"); foreach (var conversionGoal in getConversionGoals) { CampaignManagementExampleHelper.OutputConversionGoal(conversionGoal); } var updateConversionGoals = new ConversionGoal[] { new DurationGoal { ConversionWindowInMinutes = 60, CountType = ConversionGoalCountType.Unique, // You can change the conversion goal type e.g. in this example an event goal // had been created above at index 1. Now we are using the returned identifier // at index 1 to update the type from EventGoal to DurationGoal. Id = conversionGoalIds[1], MinimumDurationInSeconds = 120, Name = "My Updated Duration Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 10.00m, CurrencyCode = null }, // The Scope cannot be updated, even if you update the goal type. // You can either send the same value or leave Scope empty. Scope = EntityScope.Account, Status = ConversionGoalStatus.Paused, // You can update the tag as needed. In this example we will explicitly use the same UET tag. // To keep the UET tag unchanged, you can also leave this element nil or empty. TagId = tagId, }, new EventGoal { // For both add and update conversion goal operations, you must include one or more // of the following events: // ActionExpression, CategoryExpression, LabelExpression, or Value. // For example if you do not include ActionExpression during update, // any existing ActionOperator and ActionExpression settings will be deleted. ActionExpression = null, ActionOperator = null, CategoryExpression = "video", CategoryOperator = ExpressionOperator.Equals, Id = conversionGoalIds[0], // You cannot update the operator unless you also include the expression. // The following attempt to update LabelOperator will result in an error. LabelExpression = null, LabelOperator = ExpressionOperator.Equals, Name = "My Updated Event Goal " + DateTime.UtcNow, Revenue = new ConversionGoalRevenue { Type = ConversionGoalRevenueType.FixedValue, Value = 5.00m, CurrencyCode = null }, // You must specify the previous settings unless you want // them replaced during the update conversion goal operation. Value = 5.00m, ValueOperator = ValueOperator.Equals, }, new PagesViewedPerVisitGoal { Id = conversionGoalIds[2], Name = "My Updated Pages Viewed Per Visit Goal " + DateTime.UtcNow, // When updating a conversion goal, if the Revenue element is nil or empty then none // of the nested properties will be updated. However, if this element is not nil or empty // then you are effectively replacing any existing revenue properties. For example to delete // any previous revenue settings, set the Revenue element to an empty ConversionGoalRevenue object. Revenue = new ConversionGoalRevenue(), }, new UrlGoal { Id = conversionGoalIds[3], Name = "My Updated Url Goal" + DateTime.UtcNow, // If not specified during update, the previous Url settings are retained. UrlExpression = null, UrlOperator = ExpressionOperator.BeginsWith } }; var updateConversionGoalsResponse = await CampaignManagementExampleHelper.UpdateConversionGoalsAsync(updateConversionGoals); OutputStatusMessage("List of errors returned from UpdateConversionGoals (if any):\n"); CampaignManagementExampleHelper.OutputArrayOfBatchError(updateConversionGoalsResponse.PartialErrors); getConversionGoals = (await CampaignManagementExampleHelper.GetConversionGoalsByIdsAsync(conversionGoalIds, conversionGoalTypes)).ConversionGoals; OutputStatusMessage("List of conversion goals AFTER update:\n"); foreach (var conversionGoal in getConversionGoals) { CampaignManagementExampleHelper.OutputConversionGoal(conversionGoal); } } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch ConversionGoal Management service exceptions catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }