Microsoft Ads API SOAP + n8n conflict

Describe the problem/error/question

We want to stay within n8n if possible to automate Microsoft Ads reporting/campaign management. We understand that SOAP API is the current default for reporting as things are moved more towards REST. We request guidance on this specific issue as it is the only bottleneck preventing us from connecting with MS API from n8n. MS_ads permissions, dev tokens, account/customer IDs are validated and accounted for. Would like to know anyone else’s experience and if you just gave up and went the .NET/python/3rd party route.

To connect with SOAP API, the body must contain the AuthenticationToken, but n8n hides that value for security, right?

<s:Envelope xmlns:s=“schemas.xmlsoap.org” xmlns:i=“www.w3.org” xmlns:a=“schemas.microsoft.com” xmlns:b=“bingads.microsoft.com”>
<s:Header>
<b:DeveloperToken>YOUR_DEVELOPER_TOKEN</b:DeveloperToken>
<b:AuthenticationToken>YOUR_ACCESS_TOKEN</b:AuthenticationToken>
<b:CustomerId>YOUR_CUSTOMER_ID</b:b:CustomerId>
<b:AccountId>YOUR_ACCOUNT_ID</b:b:AccountId>
</s:Header>
<s:Body>

</s:Body>
</s:Envelope>

What is the error message (if any)?

Invalid credentials, since I lack the ability to pass the authenticationtoken via the SOAP body, and MS API apparently doesn’t play nicely with n8n’s Auth: Bearer header.

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

Share the output returned by the last node

Information on your n8n setup

  • n8n version:
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app):
  • Operating system:

Hi,

Let’s see what options you have. Are you using n8n cloud or are you self-hosted? If self-hosted, you do have other options, such as a python code node that can do the heavy lifting; the n8n cloud has strict security restrictions. You could then use sdk’s from microsoft for ads, which would make it easier for you. Constructing/maintaining soap envelopes is brittle and tedious. This is my approach with SOAP as a Java aficionado.

If you’re using n8n cloud, then that’s a different story.

Khem

Hi Khem, thank you for reaching out. Appreciate it!

We are self hosted. So we could use python code node. Do you have guidance on how it can do heavy lifting? I just need some initial guidance and then I can hack the rest via GPT. :smiley:

Thank you!

In my attempts to find workarounds for SOAP, I did attempt a FormURLencoded to try to get an access token from the code within the URL resulted from connecting to the Microsoft oAuth2 credential, but n8n said it was claimed. Assuming that n8n claims that access token and the authorization token too.

Got this from Gemini:

”Strategy 1: The “Clean” Way (Manual Token Retrieval)

Since n8n hides its internal tokens, the most stable way to use Python is to generate a dedicated Refresh Token specifically for your Python script. This prevents your Python code from “stealing” the session from your n8n nodes.

  1. Use an OAuth Playground or Postman: Use the Client ID and Secret you already have.

  2. Authorize manually: Follow the Microsoft Identity Platform flow to get a code and exchange it for a refresh_token.

  3. Hardcode or Use Environment Variables: Take that string and put it into your Python code.

    Note: Refresh tokens for Microsoft Ads are long-lived (90 days). As long as your Python script uses it to get a new access_token every time it runs, the refresh_token itself remains valid.”
    —————————————————————————–
    This is the Python code node it suggests to do and it seems if I can solved the refresh_token issue, I can make headway into getting some reporting from this node:

    ”from bingads.v13.reporting import *
    from bingads import AuthorizationData, OAuthWebAuthCodeGrant, ServiceClient

    1. Setup Authentication

    developer_token = “YOUR_DEV_TOKEN”
    client_id = “YOUR_CLIENT_ID”
    client_secret = “YOUR_CLIENT_SECRET”
    refresh_token = “YOUR_REFRESH_TOKEN”

    2. Create the Auth Object

    auth_data = AuthorizationData(
    account_id=None,
    customer_id=None,
    developer_token=developer_token,
    authentication=OAuthWebAuthCodeGrant(
    client_id=client_id,
    client_secret=client_secret
    )
    )

    Request a new access token using your refresh token

    auth_data.authentication.request_oauth_tokens_by_refresh_token(refresh_token)

    3. Example: Fetch Customer Accounts

    This replaces the need to manually build SOAP XML

    customer_service = ServiceClient(
    service=‘CustomerManagementService’,
    version=13,
    authorization_data=auth_data,
    environment=‘production’
    )

    Call the API

    response = customer_service.GetAccountsInfo(
    CustomerIds=None,
    OnlyCurrentCustomer=True
    )

    4. Return the data to n8n

    return [{“json”: {“accounts”: str(response)}}]”

Hi,

Option 1 (everything in n8n workflow and mess with SOAP payloads; hackish, maybe?)

Use n8n’s predefined microsoft oauth2 credentials. Use a HTTP request with the credentialsl but have it hit YOUR OWN host (check out my gist : https://gist.github.com/galois17/31fb40495f3c952eb110586471060fa6) to pull out the access token from the header:

(I stood up a server to handle a get request that just pulls out the access token and dumps it in a json response. It works as an echo server. Kind of hackish….)

The node that follows it can utilize the access_token. A follow up node could be another HTTP request where you craft the Soap payload that consists of the access_token and so on.

Would need a “soap hack” before a “soap call”.

Of course, you’re leaking the access token all over the place, but that’s an option.

In any case, you can now manually build the SOAP payload:

<s:Envelope xmlns:s=“schemas.xmlsoap.org” xmlns:i=“www.w3.org” xmlns:a=“schemas.microsoft.com” xmlns:b=“bingads.microsoft.com”>
<s:Header>
<b:DeveloperToken>YOUR_DEVELOPER_TOKEN</b:DeveloperToken>
<b:AuthenticationToken>YOUR_ACCESS_TOKEN</b:AuthenticationToken>
<b:CustomerId>YOUR_CUSTOMER_ID</b:b:CustomerId>
<b:AccountId>YOUR_ACCOUNT_ID</b:b:AccountId>
</s:Header>
<s:Body>

</s:Body>

Option 2 (my preference, more secure, no messing around with SOAP payload and exposing the access token all over the place)

Yes, what you got from Gemini is a scheme that will keep the automation going without manual/user intervention. As long as the refresh token is used within some period of time, it stays valid. What you need is an Authorization Code Flow for this, where the first time around you would use a browser (no n8n involved) to get an auth code that you could use to exchange for a refresh + access token. You can use a HTTP request node to do the exchange, but you will be passing the tokens around. i don’t know what security concerns you must address with your company, so this may be a problem. I wouldn’t even bother, just write a quick script.

Once you have the refresh+access token, you would then just be in this ****perpetual motion******** which allows for automation– i.e., if access token expires or about to expire, just use refresh token to get a new access token.

I would save the tokens in AWS SSM and retrieve it from there.

I would actually move code that interacts with bing ads to AWS lambda or Azure Functions, and avoid any manual tweaking of any soap stuff– just use the bing ads python module. The tokens (refresh + access) could be stored safely there. But that’s me…

From the python code node (bingads sdk), you would pull the tokens from AWS SSM and make whatever api calls you need to bing ads.

I would also add a scheme to the workflow to exchange refresh tokens if what’s been in use expires.

That’s the idea if I were to do this. I would avoid modifying any SOAP payload– it’s extremely brittle. Just rely on bingads sdk to do that stuff properly.


  1. Get AUTH_CODE by accessing this from your browser (something like this…):<YOUR_CLIENT_ID>&response_type=code&redirect_uri=<YOUR_REDIRECR_URL>&scope=https://ads.microsoft.com/msads.manage%20offline_access

  2. Exchange for refresh + access token. Write a small script to hit (check whether you want common or consumers) with post request including CLIENT_ID, CLIENT_SECRET, AUTH_CODE: https://login.microsoftonline.com/consumers/oauth2/v2.0/token

  3. Bring the refresh + access token into the n8n workflow. You should never have to do the above again. The access token is short-lived as designed, so use refresh token to get access token. The refresh token has a longer life and you can always exchange for a new one.. I would prefer to not to leak the access and refresh tokens all over the place, so my preference would be to move the access/refresh token management and logic to aws lambda or azure functions.

Khem

2 Likes

@rays_csc , let me know how it goes with whatever route you take (if possible)

Khem

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.