Skip to main content

Microsoft Advertising

Microsoft Advertising (the Bing Ads API, version 13) spreads its REST endpoints over several hosts, and a Permaura connector pins exactly one, so the catalog carries two connectors for it:

  • Microsoft Advertising reaches https://campaign.api.bingads.microsoft.com/CampaignManagement/v13: campaigns, ad groups, ads, keywords, budgets, targeting and conversion goals, with every create and update held behind an approval.
  • Microsoft Advertising Reporting reaches https://reporting.api.bingads.microsoft.com/Reporting/v13: it requests a performance report and polls for the download link. Both of its actions are reads.

They take the same app, the same developer token and the same account details, so set those up once and make two connections. An agent that only answers performance questions needs only the second.

What you need​

1. A Microsoft identity app. Register an application in the Microsoft Entra admin center (or the Azure portal) under App registrations, with Accounts in any organizational directory and personal Microsoft accounts as the supported account type, since Microsoft Advertising users sign in with either. Under Authentication add a Web platform whose redirect URI is your gateway's address followed by /oauth/callback: http://127.0.0.1:7376/oauth/callback on your own machine, https://<your-gateway-host>/oauth/callback otherwise. Under Certificates & secrets create a client secret and copy its value (not its id) straight away; it is shown once. The application (client) id is on the overview page. No API permission needs adding by hand: the gateway asks for https://ads.microsoft.com/msads.manage and offline_access at sign-in, and the consent page grants them.

2. A developer token. Sign in to Microsoft Advertising as a Super Admin and open the developer settings (ads.microsoft.com/cc/Settings/DevSettings, Request token). A universal token works for every user who signs in through your app. Microsoft issues it without a review step; the sandbox has its own universal token, BBD37VB98.

3. The customer id and account id. Open the Campaigns tab in Microsoft Advertising and read the URL: cid= is the customer id (the manager account) and aid= is the account id (the ad account). Not the eight-character account number shown in the UI; the API never takes that.

Add the connection​

In the console, open Access, Connections, Add connection, pick Microsoft Advertising from the catalog, and fill in Developer token, Customer id and Account id. Under the credential choose OAuth, then Gateway-held · your own app, paste the application id and the client secret, check the redirect URI matches the one on your app registration, and choose Add. Microsoft's sign-in opens in a new tab; sign in as a user with access to the account and accept the consent page, and your browser lands on the gateway's "Connected" page once the tokens are sealed. Repeat with Microsoft Advertising Reporting for the second connection; the same app and values work.

Over the REST API, the three values travel as static_headers on the from-preset call:

BASE=http://127.0.0.1:7376
curl -s -X POST $BASE/v1/connections/from-preset \
-H @<(printf 'authorization: Bearer %s\n' "$TOKEN") -H 'content-type: application/json' -d '{
"preset_id": "microsoft-ads",
"name": "msads-acme",
"static_headers": { "DeveloperToken": "<your-developer-token>", "CustomerId": "<cid>", "CustomerAccountId": "<aid>" }
}'

Then start the authorization against the returned connection_id as on the Reddit page. The gateway refuses a from-preset call for either connector that leaves one of the three out.

The developer token is stored on the connection in the clear, beside the pinned host, for the reasons and with the trade-offs the Google Ads page sets out: it identifies your API access, the signed-in user's OAuth token is what authorizes the account, and that token is the part the gateway seals and refreshes. Access tokens from Microsoft last an hour; the gateway refreshes them two minutes before expiry with the refresh token offline_access provides.

What the agent can do​

Microsoft Advertising:

ActionWhat it doesOn by default
campaigns.list, campaigns.readLists an account's campaigns; reads campaigns by idYes
ad_groups.list, ad_groups.readLists a campaign's ad groups; reads ad groups by idYes
ads.list, ads.readLists an ad group's ads; reads ads by idYes
keywords.listLists an ad group's keywords with bids and match typesYes
budgets.listLists the account's shared budgetsYes
campaign_criteria.list, ad_group_criteria.listLists targeting such as locations, ages and devicesYes
conversion_goals.listLists conversion goalsYes
campaigns.create, campaigns.updateCreates campaigns; pauses, activates or changes them and their budgetsNo, and needs approval
ad_groups.create, ad_groups.updateCreates ad groups; pauses, activates or changes them and their bidsNo, and needs approval
ads.create, ads.updateCreates ads; pauses, activates or changes themNo, and needs approval
keywords.create, keywords.updateAdds keywords; changes their bids or statusNo, and needs approval
budgets.create, budgets.updateCreates or changes shared budgetsNo, and needs approval

Microsoft Advertising Reporting:

ActionWhat it doesOn by default
reports.submitRequests a report and returns its request idYes
reports.pollPolls the request; when its status is Success the answer carries the download URLYes

Good to know​

  • Every call is a POST, reads included. The REST API mirrors its SOAP operations: GetCampaignsByAccountId is POST /Campaigns/QueryByAccountId with the ids in a JSON body. Permaura's classifier would read those as writes, so the catalog marks every Query action and both reporting actions as reads at medium risk. Updates are PUT on the collection (PUT /Campaigns), which the classifier gates on its own.
  • Body ids must match the headers. AccountId in a body must equal the connection's CustomerAccountId; Microsoft answers a mismatch with an authorization error rather than an empty list. One connection reaches one ad account; a second account is a second connection.
  • campaigns.list returns Search campaigns unless told otherwise. Pass CampaignType as a space-separated list, for example "Search Shopping Audience PerformanceMax", to see the rest.
  • Updates send whole objects. campaigns.update takes Campaigns, a list of campaign objects each carrying its Id and the fields to change; a field left null stays as it was. Pausing is {"Id": 123, "Status": "Paused"}; "Active" resumes, and both ask for an approval.
  • Reports arrive as a file, not a response. reports.submit takes a ReportRequest whose Type names the report, for instance CampaignPerformanceReportRequest with Columns, Scope and Time. The answer is a ReportRequestId; reports.poll returns Pending until the file is ready, then Success and a ReportDownloadUrl for a zipped CSV. That URL is a signed link to Microsoft's download host, needs no credential, and lives a day, so the agent (or you) fetches it directly; the gateway's part ends at the link.
  • The sandbox is the same connector on another host. Choose Use a different host when you add the connection and enter https://campaign.api.sandbox.bingads.microsoft.com/CampaignManagement/v13 (or the reporting.api.sandbox. equivalent), with the sandbox developer token and a sandbox sign-in. Sandbox and production credentials do not cross.
  • Customer management is not covered. Finding a user's accounts through SearchAccounts lives on a third host (clientcenter.api.bingads.microsoft.com), and the ids are two numbers in a URL, so the catalog leaves that host out. Import a connector of your own under another id if you want it.