Microsoft Ads CAPI
Datafly Signal delivers conversion events to Microsoft Advertising server-to-server using the Conversions API (CAPI), the server-side counterpart to the Universal Event Tracking (UET) JavaScript tag. This gives you conversion measurement for Bing, Edge, and Microsoft Audience Network campaigns that does not depend on browser cookies or client-side script execution.
Why route Microsoft Ads through Datafly Signal
- Conversions survive ad blockers and tracking prevention. A browser tag served from an advertising domain is blocked outright for part of your traffic, and Safari’s ITP shortens the cookies it can set. Signal sends the event from your own server, so the conversions Microsoft Ads optimises against are not filtered by the browser.
- Email and phone number are hashed before leaving your infrastructure. Signal normalises (trim and lowercase) and applies SHA-256 inside your own deployment, so Microsoft Ads receives match keys rather than raw personal data.
- Client IP and user agent are preserved. Server-to-server delivery normally loses both, which weakens matching. Signal forwards the genuine visitor values as request headers so Microsoft Ads attributes the event to the right session.
- Click identifiers are captured and replayed. Signal stores
msclkidfirst-party at collection time and attaches them to the conversion later, so attribution survives even when the original landing cookie has expired. - Delivery is batched, rate-limited, and retried. Signal buffers to Kafka, respects the Microsoft Ads rate limit, and retries on 429 or transient failure rather than dropping the event.
- Presets for Retail and B2B / SaaS. The mapping starts from a working configuration for your vertical instead of a blank field map.
- One consent decision governs every destination. Microsoft Ads is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.
- No vendor tag on the page. Removing it removes its page weight and its independent access to your visitors.
Prerequisites
You need a Microsoft Advertising account, a UET tag, conversion goals, and a Conversions API token.
Step 1: Create a Microsoft Advertising Account
If you don’t already have one:
- Go to ads.microsoft.com and click Sign up now.
- Sign in with your Microsoft account (or create one).
- Enter your business information, time zone, and currency.
- Complete the account setup wizard.
Step 2: Create a UET Tag
A UET tag is the container CAPI events are scoped to. The tag ID appears in the API endpoint path, and the API token is issued per tag.
- In Microsoft Advertising, go to Tools > UET tags.
- Click Create UET tag.
- Give the tag a name (e.g. “Datafly Signal”) and optionally a description.
- Click Save.
- Note the UET Tag ID (numeric) from the tag list.
Microsoft recommends running CAPI alongside the UET JavaScript tag rather than instead of it. UET captures browser and page context that a server cannot see, while CAPI carries the events and detail that happen after the page. Signal makes the two safe to run together by sending a shared eventId on every event, so Microsoft deduplicates rather than double-counting. See Deduplication below.
Running CAPI on its own is supported and still measures conversions. You give up the browser context UET observes, so match quality is lower.
Step 3: Set Up Conversion Goals
Conversion goals define the actions you optimise for.
- In Microsoft Advertising, go to Tools > Conversion tracking > Conversion goals.
- Click Create conversion goal.
- Select the goal type (e.g. Purchase, Lead, Sign-up, Custom).
- Choose Event tag as the tracking method and select your UET tag.
- Configure the goal settings:
- Goal name — descriptive name (e.g. “Purchase”, “Lead Form Submit”)
- Action — must match the
eventNameSignal sends (see Event Mapping) - Revenue — variable revenue for purchases, fixed for leads
- Attribution window — default is 30-day click-through
- Click Save.
- Repeat for each conversion action you want to track.
Step 4: Get the Conversions API Token
CAPI authenticates on a token scoped to a single UET tag. This is not an OAuth token and not a Bing Ads developer token.
- In Microsoft Advertising, go to Tools > UET tags and edit your tag with the pencil icon.
- Click Save and next.
- In the Set up tagging section, select Use Conversions API.
- In the Conversions API section, click Copy Token, then Next, then Done.
The token does not expire on a schedule, so no refresh flow is needed. Treat it as a credential: it grants the ability to write conversions to that tag. If it leaks, generate a new one from the same screen.
Configure in Signal
Configuration Fields
| Field | Required | Description |
|---|---|---|
uet_tag_id | Yes | Your UET tag ID, from Tools > UET tags. Used in the endpoint path. Use the same tag your UET JavaScript reports to, so conversions deduplicate. |
access_token | Yes | The tag-scoped Conversions API token from Step 4. |
Management UI Setup
- Go to Integrations > Add Integration > Microsoft Ads CAPI.
- Choose the preset that matches your vertical (Retail or B2B / SaaS).
- Enter your
uet_tag_idandaccess_token. - Select consent categories (typically
advertising). - Click Save.
Management API Setup
curl -X POST http://localhost:8084/v1/admin/integrations \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_id": "src_abc123",
"vendor": "microsoft_ads",
"name": "Microsoft Ads CAPI",
"enabled": true,
"config": {
"uet_tag_id": "12345678",
"access_token": "your_conversions_api_token"
},
"consent_categories": ["advertising"]
}'API Endpoint
POST https://capi.uet.microsoft.com/v1/{uet_tag_id}/events
Authorization: Bearer <Conversions API token>Events are sent as JSON under a data array. Signal batches up to 500 events per request (Microsoft’s limit is 1,000) with a 2-second flush window, and sets continueOnValidationError so one malformed event cannot reject the rest of the batch.
Identity Signals
Every event must carry at least one identifier in userData, or Microsoft rejects it. Signal always sends the visitor’s anonymous ID, so this holds even for a first-time anonymous visitor with no PII.
Automatic Signals
Sent automatically — no configuration needed:
| Signal | CAPI field | Description |
|---|---|---|
| Anonymous ID | userData.anonymousId | Signal’s first-party visitor ID. Always present. |
| User ID | userData.externalId | Your own ID for a signed-in user, when datafly.identify() has been called. |
| Microsoft click ID | userData.msclkid | Captured from the msclkid URL parameter on ad click. The primary attribution signal. |
| IP address | userData.clientIpAddress | Forwarded from the original request. |
| User agent | userData.clientUserAgent | Forwarded from the original request. |
When a user clicks a Microsoft ad, the msclkid parameter is appended to the landing page URL:
https://example.com/landing?msclkid=dd4afccc-b1c9-4a4c-ad95-44dd7e5006abDatafly.js captures it and persists it as a first-party cookie for the attribution window.
msclkid is essential for click attribution. Ensure Auto-tagging is enabled in your Microsoft Advertising account settings. Without it, click IDs are never appended to your URLs.
User-Provided Signals (Hashed)
When user data is available via datafly.identify(), these are SHA-256 hashed server-side before sending:
| Signal | CAPI field | Normalisation |
|---|---|---|
userData.em | Trimmed, lowercased, then SHA-256 | |
| Phone | userData.ph | Converted to E.164, then SHA-256 |
All PII hashing happens inside your own infrastructure. Raw email addresses and phone numbers never leave it. Enhanced matching improves attribution for users who cannot be matched on click ID alone.
Consent
Signal maps your CMP’s marketing consent onto Microsoft’s adStorageConsent field: G when granted, D when denied.
Events whose marketing consent is denied are suppressed before delivery, so nothing is sent for a user who has refused advertising consent. If your CMP reports no marketing signal at all, the field is omitted and Microsoft applies its documented default of granted.
Events marked D are not used for any advertising purpose, including attribution and remarketing.
Event Mapping
CAPI has two event types. pageLoad carries page context for destination-URL goals; custom carries a named conversion action that must match the Action on your conversion goal.
Retail preset
| Signal event | eventType | eventName |
|---|---|---|
page | pageLoad | — |
add_to_cart | custom | add_to_cart |
purchase | custom | purchase |
B2B / SaaS preset
| Signal event | eventType | eventName |
|---|---|---|
page | pageLoad | — |
sign_up | custom | sign_up |
subscription_started | custom | subscribe |
To customise, edit the integration’s Field Mappings in the Management UI.
Example: Purchase Event
Datafly.js call:
datafly.track("purchase", {
order_id: "ORD-001",
revenue: 129.99,
currency: "USD"
});Microsoft Ads CAPI payload sent by Signal:
{
"data": [
{
"eventType": "custom",
"eventName": "purchase",
"eventTime": 1786968000,
"eventId": "evt_abc123def456",
"eventSourceUrl": "https://example.com/checkout/confirmation",
"pageTitle": "Order confirmed",
"referrerUrl": "https://www.bing.com/",
"adStorageConsent": "G",
"userData": {
"anonymousId": "b171a9b06ce011ecafcd1b209be8601b",
"externalId": "user-42",
"msclkid": "dd4afccc-b1c9-4a4c-ad95-44dd7e5006ab",
"em": "ec81f3ac7b2b19675bab9d54cf416f9f18cff87c97da5cca82c0f0891bc40602",
"clientIpAddress": "203.0.113.50",
"clientUserAgent": "Mozilla/5.0 ..."
},
"customData": {
"value": 129.99,
"currency": "USD",
"transactionId": "ORD-001",
"pageType": "purchase"
}
}
],
"continueOnValidationError": true,
"dataProvider": "datafly"
}Product-level remarketing exclusion
Microsoft’s dynamic remarketing needs both a page type and a product
identifier. purchase and add_to_cart send customData.pageType together
with customData.items, mapped from properties.items (or properties.products).
Before v0.7.0 the blueprint sent pageType with no product identifier. UET
accepts that — the event validates, the integration looks configured, and the
exclusion matches no product. Buyers carried on being retargeted for items
they had already bought, with nothing failing anywhere to indicate it. If you
configured this integration before v0.7.0, re-check that purchases now carry
customData.items.
The identifiers you send must match the product IDs in your Microsoft Merchant Center feed exactly. A variant ID sent against a feed of parent IDs, or a SKU against GTINs, matches nothing and fails silently in the same way.
Deduplication
If the same conversion can reach Microsoft from both the UET JavaScript tag and CAPI, all three of the following must match for Microsoft to recognise the duplicate:
- the UET tag ID
- the
eventId - the
eventName
Signal sends the event’s ID as eventId automatically. To deduplicate against your UET tag, pass that same ID to uetq client-side as event_id, and use the same action name.
window.uetq = window.uetq || [];
window.uetq.push('event', 'purchase', {
event_id: '<the same id Signal sends>'
});Testing Your Integration
- Trigger events on your website with the integration enabled.
- In Signal, check Live Events to confirm delivery with a
200response. - In Microsoft Advertising, go to Tools > Conversion tracking > Conversion goals and check the Tracking status column — it should show “Recording conversions” or “Tag active”.
- Open individual goals to see conversion counts.
Microsoft Advertising may take up to 4 hours to reflect conversions in the dashboard. If nothing appears after 6 hours, work through the troubleshooting table below.
A 200 response does not always mean every field was accepted. Microsoft returns 200 with validation warnings when an optional field fails validation — it removes that field and processes the event without it. An invalid referrerUrl or a badly hashed em behaves this way, so a goal can record conversions while enhanced matching is silently doing nothing.
Troubleshooting
| Symptom | Possible Cause | Resolution |
|---|---|---|
401 Unauthorized | Token missing, wrong, or issued for a different tag | Confirm the token was copied from the Use Conversions API screen of the tag whose ID is in uet_tag_id. |
404 Not Found | Wrong UET tag ID in the endpoint path | Check uet_tag_id against Tools > UET tags. |
400 InvalidEnumValue on eventType | A field mapping overrode eventType with something other than pageLoad or custom | Restore the preset’s vendor_event_name, which supplies eventType. |
400 InvalidEventTime | Event timestamp is missing, or older than 7 days | Microsoft rejects events outside a 7-day window. Check for a replay or a backlogged queue. |
| Events accepted but no conversions appear | eventName does not match the goal’s configured action | Align the goal’s Action in Microsoft Advertising with the eventName in the Event Mapping table. |
| Duplicate conversions | UET tag and CAPI sending different eventId or eventName values | See Deduplication. All of tag ID, eventId and eventName must match. |
msclkid not captured | Auto-tagging disabled | Enable auto-tagging in Microsoft Advertising account settings and confirm Datafly.js loads on the landing page. |
| Enhanced matching not working | em / ph rejected as invalid SHA-256 and removed with a warning | Confirm datafly.identify() is supplying email and phone before the conversion fires. |
| Nothing delivered for any user | Marketing consent denied or absent | Check your CMP mapping. Denied events are suppressed before delivery by design. |
| Revenue missing on goals | revenue and currency absent from event properties | Include both, and confirm the goal is set to variable revenue. |
For full API documentation, see Microsoft’s Conversions API guide.