🏢 Bing Ads - Account Management
📖 Overview
Bing Ads uses a hierarchical account structure with Manager Accounts (similar to Google Ads MCC) and User Accounts. The Customer Management Service API provides SOAP-based access to account information through the GetLinkedAccountsAndCustomersInfo operation.
Source Files:
- Controller:
external/Integrations/BingAds/Controllers/Accounts/AccountsController.js - Model:
external/Integrations/BingAds/Models/Accounts/AccountsModel.js - Routes:
external/Integrations/BingAds/Routes/accounts.js
External API: Microsoft Advertising Customer Management Service v13
SOAP Endpoint: https://clientcenter.api.bingads.microsoft.com/Api/CustomerManagement/v13/CustomerManagementService.svc
🏗️ Account Hierarchy
graph TD
MA[Manager Account] --> C1[Customer 1]
MA --> C2[Customer 2]
C1 --> A1[Account 1]
C1 --> A2[Account 2]
C2 --> A3[Account 3]
C2 --> A4[Account 4]
style MA fill:#4CAF50
style C1 fill:#2196F3
style C2 fill:#2196F3
style A1 fill:#FF9800
style A2 fill:#FF9800
style A3 fill:#FF9800
style A4 fill:#FF9800
Hierarchy Levels:
- Manager Account: Top-level account (agency/reseller)
- Customer: Organization/client under manager
- User Account: Individual advertising account under customer
🔄 Data Flow
sequenceDiagram
participant Client as API Client
participant DC as DashClicks API
participant MW as Token Middleware
participant Model as AccountsModel
participant SOAP as Bing SOAP API
Client->>DC: GET /managers or /accessible-user-accounts
DC->>MW: Check token expiration
MW->>DC: Valid access token
DC->>Model: managerAccounts() or accesibleUsersAccounts()
Model->>Model: Build SOAP XML request
Model->>SOAP: POST SOAP envelope
SOAP-->>Model: SOAP XML response
Model->>Model: Parse XML to JSON
Model-->>DC: Return JSON data
DC-->>Client: JSON response with accounts
🔧 Account Functions
Get Manager Accounts
GET /managers
Purpose: Retrieve top-level manager accounts (MCC) with parent-only filtering
Controller: AccountsController.managerAccounts()
Model Method: AccountsModel.getManagerAccounts()
SOAP Operation: GetLinkedAccountsAndCustomersInfo
SOAP Service: Customer Management Service v13
Authentication: OAuth 2.0 Bearer token (via middleware)
Request:
GET /v1/integrations/bing/ads/accounts/managers
Authorization: Bearer {jwt_token}
SOAP Request XML:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:v13="https://bingads.microsoft.com/Customer/v13">
<soapenv:Header>
<v13:DeveloperToken>{DEVELOPER_TOKEN}</v13:DeveloperToken>
<v13:AuthenticationToken>{ACCESS_TOKEN}</v13:AuthenticationToken>
</soapenv:Header>
<soapenv:Body>
<GetLinkedAccountsAndCustomersInfoRequest xmlns="https://bingads.microsoft.com/Customer/v13">
<OnlyParentAccounts>true</OnlyParentAccounts>
</GetLinkedAccountsAndCustomersInfoRequest>
</soapenv:Body>
</soapenv:Envelope>
SOAP Headers:
| Header | Value | Description |
|---|---|---|
DeveloperToken | From env var | Microsoft Ads developer token |
AuthenticationToken | From middleware | OAuth 2.0 access token |
SOAPAction | GetLinkedAccountsAndCustomersInfo | SOAP operation name |
Request Parameters:
| Parameter | Type | Description |
|---|---|---|
OnlyParentAccounts | Boolean | true - Only return parent (manager) accounts |
SOAP Response XML Structure:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetLinkedAccountsAndCustomersInfoResponse xmlns="https://bingads.microsoft.com/Customer/v13">
<AccountInfoWithCustomerData>
<AccountInfoWithCustomerData>
<CustomerId>123456</CustomerId>
<CustomerName>Agency Name</CustomerName>
<AccountId>987654321</AccountId>
<AccountName>Manager Account</AccountName>
<AccountNumber>X123456</AccountNumber>
<AccountLifeCycleStatus>Active</AccountLifeCycleStatus>
<PauseReason>None</PauseReason>
</AccountInfoWithCustomerData>
</AccountInfoWithCustomerData>
</GetLinkedAccountsAndCustomersInfoResponse>
</s:Body>
</s:Envelope>
JSON Response (Parsed from SOAP XML):
{
"success": true,
"message": "SUCCESS",
"data": {
"GetLinkedAccountsAndCustomersInfoResponse": {
"AccountInfoWithCustomerData": {
"AccountInfoWithCustomerData": [
{
"CustomerId": 123456,
"CustomerName": "Agency Name",
"AccountId": 987654321,
"AccountName": "Manager Account",
"AccountNumber": "X123456",
"AccountLifeCycleStatus": "Active",
"PauseReason": "None"
}
]
}
}
}
}
Response Fields:
| Field | Type | Description |
|---|---|---|
CustomerId | Integer | Customer identifier |
CustomerName | String | Customer name |
AccountId | Integer | Account identifier (used in other API calls) |
AccountName | String | Account name |
AccountNumber | String | Account number (X-prefixed) |
AccountLifeCycleStatus | String | Active, Inactive, Pause |
PauseReason | String | Reason if paused |
Error Response:
{
"success": false,
"error": "SOAP Fault: Invalid authentication token"
}
Side Effects:
- None (read-only operation)
Get Accessible User Accounts
GET /accessible-user-accounts
Purpose: Retrieve all user accounts accessible under a specific customer ID
Controller: AccountsController.accesibleUsersAccounts()
Model Method: AccountsModel.getAccesibleUsersAccounts(customerID)
SOAP Operation: GetLinkedAccountsAndCustomersInfo
SOAP Service: Customer Management Service v13
Authentication: OAuth 2.0 Bearer token (via middleware)
Request:
GET /v1/integrations/bing/ads/accounts/accessible-user-accounts?customerID=123456
Authorization: Bearer {jwt_token}
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
customerID | Integer | ✅ | Customer ID from manager accounts |
Validation:
- If
customerIDmissing, returns error: "Please provide customerID"
SOAP Request XML:
<s:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header xmlns="https://bingads.microsoft.com/Customer/v13">
<Action mustUnderstand="1">GetLinkedAccountsAndCustomersInfo</Action>
<AuthenticationToken i:nil="false">{ACCESS_TOKEN}</AuthenticationToken>
<DeveloperToken i:nil="false">{DEVELOPER_TOKEN}</DeveloperToken>
</s:Header>
<s:Body>
<GetLinkedAccountsAndCustomersInfoRequest xmlns="https://bingads.microsoft.com/Customer/v13">
<CustomerId i:nil="false">123456</CustomerId>
<OnlyParentAccounts>true</OnlyParentAccounts>
</GetLinkedAccountsAndCustomersInfoRequest>
</s:Body>
</s:Envelope>
SOAP Headers:
| Header | Value | Description |
|---|---|---|
Action | GetLinkedAccountsAndCustomersInfo | SOAP action with mustUnderstand="1" |
AuthenticationToken | From middleware | OAuth 2.0 access token |
DeveloperToken | From env var | Microsoft Ads developer token |
Request Parameters:
| Parameter | Type | Description |
|---|---|---|
CustomerId | Integer | Specific customer ID to filter accounts |
OnlyParentAccounts | Boolean | true - Only return parent accounts under customer |
SOAP Response XML Structure:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetLinkedAccountsAndCustomersInfoResponse xmlns="https://bingads.microsoft.com/Customer/v13">
<AccountInfoWithCustomerData>
<AccountInfoWithCustomerData>
<CustomerId>123456</CustomerId>
<CustomerName>Client Company</CustomerName>
<AccountId>111222333</AccountId>
<AccountName>Search Campaign Account</AccountName>
<AccountNumber>Y987654</AccountNumber>
<AccountLifeCycleStatus>Active</AccountLifeCycleStatus>
<PauseReason i:nil="true"/>
</AccountInfoWithCustomerData>
<AccountInfoWithCustomerData>
<CustomerId>123456</CustomerId>
<CustomerName>Client Company</CustomerName>
<AccountId>444555666</AccountId>
<AccountName>Shopping Campaign Account</AccountName>
<AccountNumber>Y123456</AccountNumber>
<AccountLifeCycleStatus>Active</AccountLifeCycleStatus>
<PauseReason i:nil="true"/>
</AccountInfoWithCustomerData>
</AccountInfoWithCustomerData>
</GetLinkedAccountsAndCustomersInfoResponse>
</s:Body>
</s:Envelope>
JSON Response (Parsed from SOAP XML):
{
"success": true,
"message": "SUCCESS",
"data": {
"GetLinkedAccountsAndCustomersInfoResponse": {
"AccountInfoWithCustomerData": {
"AccountInfoWithCustomerData": [
{
"CustomerId": 123456,
"CustomerName": "Client Company",
"AccountId": 111222333,
"AccountName": "Search Campaign Account",
"AccountNumber": "Y987654",
"AccountLifeCycleStatus": "Active",
"PauseReason": null
},
{
"CustomerId": 123456,
"CustomerName": "Client Company",
"AccountId": 444555666,
"AccountName": "Shopping Campaign Account",
"AccountNumber": "Y123456",
"AccountLifeCycleStatus": "Active",
"PauseReason": null
}
]
}
}
}
}
Response Fields:
| Field | Type | Description |
|---|---|---|
CustomerId | Integer | Customer identifier (same for all accounts) |
CustomerName | String | Customer name |
AccountId | Integer | Account identifier (use in campaigns, ads API) |
AccountName | String | Descriptive account name |
AccountNumber | String | Account number (Y-prefixed for user accounts) |
AccountLifeCycleStatus | String | Active, Inactive, Pause |
PauseReason | String/Null | Reason if paused |
Error Response (Missing customerID):
{
"success": false,
"error": "Please provide customerID"
}
Error Response (SOAP Fault):
{
"success": false,
"error": "Invalid customer ID"
}
SOAP Fault Handling:
- Checks for
response.Envelope.Body.Fault - Throws error with
faultstringmessage - Common faults: Invalid credentials, unauthorized access, invalid customer ID
Side Effects:
- None (read-only operation)
📊 Account Status Values
AccountLifeCycleStatus:
| Status | Description |
|---|---|
Active | Account is active and can serve ads |
Inactive | Account is inactive (billing issues, suspended) |
Pause | Account is manually paused |
Pending | Account setup in progress |
PauseReason (when status is Pause):
| Reason | Description |
|---|---|
None | Not paused |
UserInitiated | User manually paused |
BillingIssue | Payment method problem |
PolicyViolation | Ads policy violation |
🔗 Integration Flow
Step 1: Get Manager Accounts
GET /v1/integrations/bing/ads/accounts/managers
Returns top-level manager accounts with CustomerId values.
Step 2: Get User Accounts
GET /v1/integrations/bing/ads/accounts/accessible-user-accounts?customerID={CustomerId}
Returns all user accounts under the specific customer.
Step 3: Use Account IDs
Use AccountId from user accounts in other API calls:
- Campaigns:
/campaigns?customerAccountID={AccountId}&customerID={CustomerId} - Ad Groups:
/adgroups?customerAccountID={AccountId}&customerID={CustomerId} - Ads:
/ads?customerAccountID={AccountId}&customerID={CustomerId}
🔐 Required Parameters
All Bing Ads API calls require:
customerAccountID- TheAccountIdfrom user accountscustomerID- TheCustomerIdfrom manager accounts
Example:
GET /campaigns?customerAccountID=111222333&customerID=123456
🧩 SOAP vs REST
Why SOAP for Accounts?
- Microsoft Advertising uses SOAP protocol for Customer Management Service
- XML-based requests and responses
- Custom SOAP client with
fast-xml-parser
XML Parsing:
- SOAP XML → JavaScript Object (via
SoapClient.parse()) - Nested structure:
Envelope.Body.{ResponseName} - Fault handling:
Envelope.Body.Fault.faultstring
⚠️ Important Notes
- 🏢 Manager Account Required: Top-level manager account needed for hierarchy
- 🔢 CustomerID Required: Must provide customerID for user accounts endpoint
- 📊 Account Selection: Frontend should let users select specific account
- 🔑 AccountId Critical: Use
AccountId(notAccountNumber) in API calls - 📝 OnlyParentAccounts: Always
trueto get parent-level accounts - 🔄 Token Auto-Refresh: Middleware handles token expiration automatically
- ⚡ SOAP Protocol: Uses XML instead of JSON
- 🧰 Custom Parser: Uses
fast-xml-parserlibrary - 🔐 Developer Token: Required in addition to OAuth token
- 📋 Multiple Accounts: Users may have access to multiple accounts under single customer
🔗 Related Documentation
- Authentication: OAuth 2.0 Flow with Auto-Refresh
- Campaigns: Campaign Management
- Microsoft Docs: GetLinkedAccountsAndCustomersInfo
- Customer Management API: Service Reference