By Unwired Logic - a Zendesk app for Google Ads and Meta conversion tracking
Works with: Zendesk Support (triggers and automations), Google Ads, Meta Ads
About This Application
Google + Meta Ads Conversion Tracking for Zendesk measures how well your advertising performs. When someone clicks a Google Ads or Meta ad, becomes a customer, and reaches an outcome you care about - an order won, a signup, a completed job - this app reports that outcome back to Google Ads and Meta as a conversion. The ad platforms can then optimise your campaigns toward real customers rather than just clicks.
Unlike a website pixel, the conversion is reported from Zendesk, at the moment your own process says it happened. You decide that moment with a Zendesk trigger or automation: whatever condition you would use to notify a team is the condition that reports a conversion.
For Google, uploads go through Google's Data Manager API using an Unwired Logic service account that you authorise on your own Google Ads account. For Meta, they go through the Conversions API using your own access token. The app uses that access solely to upload conversions - never to read campaigns or change settings.
Contents
- How It Works with Zendesk
- What the App Does
- Instructions
- Request body
- Fields
- One conversion per click
- Checking it worked
- Google Ads Setup Prerequisites
- Meta Ads Setup Prerequisites
- Consent and GDPR
- FAQs
How It Works with Zendesk
- A visitor clicks your Google or Meta ad and lands on your website. Your site captures the click identifier and UTM parameters.
- Those values reach Zendesk on the customer's user record (or on the ticket), so the ad click is attached to the person.
- Your own Zendesk process runs as usual - the ticket moves through its stages.
- When it reaches the outcome you want to report, a trigger or automation fires and posts one JSON body to this app's webhook.
- The app uploads the conversion to Google Ads or Meta, records the outcome in Recent uploads, and - if you sent a ticket id - leaves an internal note on the ticket.
Nothing is read back out of Zendesk to do this. Everything the upload needs is in the body your trigger sends, which is why the app has no field mapping to configure.
What the App Does
- Uploads offline conversions to Google Ads (Data Manager API) and Meta (Conversions API) from Zendesk events.
- Lets you decide, per trigger, which conversion is reported - so one Zendesk account can report as many different conversions as it has triggers.
- Carries the conversion value and currency, so the platforms optimise toward revenue rather than volume.
- Matches the conversion to the original click on the click identifier, and optionally on a hashed email and phone number for better match rates.
- Honours each customer's own advertising consent, and uploads nothing for a customer who refused.
- Guarantees one conversion per ad click per event type, however many times a trigger re-fires.
- Records every decision - successes and skips alike - in a 90-day Recent uploads history.
- Writes each successful upload back onto the ticket as an internal note, so agents can see it without opening the app.
Instructions
Setup takes about an hour, and runs across the Zendesk app, Zendesk Admin Center, and your Google Ads and Meta accounts. You can enable Google, Meta, or both - anything you leave unconfigured is simply skipped.
1. Install the app
Unwired Logic installs the app on your Zendesk account. It appears in the left-hand nav bar in Zendesk Support.
The app has one setting, administrators: a list of the agent email addresses allowed to manage the configuration. Anyone not on that list can open the app but cannot read or change any settings, so keep it current.
2. Capture ad attribution onto the customer
⚠️ Required, or nothing can be uploaded. A conversion can only be reported if the app knows which ad click it belongs to. That click identifier has to reach Zendesk, and only your website can put it there.
The recommended arrangement is a set of user fields on the Zendesk user, filled by your booking or enquiry form from the values your site captured at the ad click:
| Field key | Type | Holds |
|---|---|---|
ul_ads_click_id |
Text | The Google gclid or Meta fbclid
|
ul_ads_utm_source |
Text |
google, facebook, instagram or meta
|
ul_ads_utm_medium |
Text | Your own reporting only |
ul_ads_utm_campaign |
Text | Your own reporting only |
ul_ads_gbraid |
Text | Google's cookieless click id |
ul_ads_wbraid |
Text | Google's cookieless click id |
ul_ads_landing_url |
Text | The page the visitor arrived on |
ul_ads_utms_captured_at |
Text | When the click was captured, ISO 8601 |
ul_ads_consent |
Dropdown | Exactly granted or denied
|
Note - ul_ads_utms_captured_at must be a Text field, not a Date field. Meta needs the time of the click to the second in order to reconstruct its click identifier, and a Zendesk date field stores only YYYY-MM-DD. A date field here will silently degrade Meta match quality.
Note - ul_ads_consent option values must be exactly granted and denied (lower case). The display labels can read however you like; it is the stored value the app matches on.
You do not have to use these exact fields - the trigger body in step 7 can read from anywhere in Zendesk, including ticket fields. These are simply the names the sample body uses, and the ones Unwired Logic will create for you on request.
3. Connect Zendesk
In the app, open Zendesk access and click Connect Zendesk. Authorise the app in the window that opens, then close it and return.
This is used for one thing: writing each successful upload back onto the ticket as an internal note. Conversions upload perfectly well without it, so you can skip this step or do it later - the app will simply not annotate your tickets. The account that authorises is the account those notes are attributed to.
4. Connect Google Ads
There is no Google credential to paste into this app. Conversions are uploaded by an Unwired Logic service account, which you authorise by adding its email address as a user on your own Google Ads account. The app's Google Ads card shows that address with a copy button.
- Copy the service account address from the Google Ads card.
- In Google Ads, go to Admin → Access and security and add that address as a user with Standard access.
- Back in the app, enter your Customer ID - the account that runs your ads. You can paste it in the
XXX-XXX-XXXXform Google Ads shows; the app strips the dashes for you. - Set Ads Consent When Unknown to Granted or Denied. See Consent and GDPR.
- Save, then click Test connection. The test uses Google's validate-only mode, so it records no conversion.
Complete Google Ads Setup Prerequisites as well - without them, uploads are rejected.
5. Connect Meta Ads
In the app's Meta Ads card:
- Dataset ID - your Meta Pixel / Dataset ID from Events Manager.
- Access token - a long-lived Conversions API token. Stored encrypted, and never shown back to you.
Save, then click Test connection. The test event is routed to Events Manager's Test Events rather than your live data.
See Meta Ads Setup Prerequisites for how to obtain both values.
6. Create the webhook
- In the app, copy the Receiver URL from the Webhook setup card. It is unique to your Zendesk account - treat it as a secret.
- Go to Admin Center → Apps and integrations → Webhooks and create a webhook:
- Endpoint URL: the Receiver URL you copied
-
Request method:
POST -
Request format:
JSON - Authentication: none
- Zendesk shows a signing secret for the webhook. Copy it back into the app's Webhook signing secret field and save.
⚠️ Requests are rejected until the signing secret is saved. The app's header reports this as an error until you do.
You only ever need one webhook, however many conversions you report - every trigger invokes the same one.
7. Create your triggers
Create a Zendesk trigger or automation for each conversion you want to report. Use whatever conditions describe that outcome in your own process, then add the action Notify active webhook, choose the webhook from step 6, and paste the request body below.
Give each one a distinct eventType. That is what separates conversions in reporting and in deduplication; case is ignored, so Order_Won and order_won count as the same conversion.
8. Verify it's working
- Use Test connection on the Google Ads and Meta cards first. That proves the credentials before any real conversion depends on them.
- Put a test customer through the journey, arriving via a link carrying
utm_sourceand a click id, so the attribution fields are filled. - Fire the trigger and open Recent uploads in the app. A row appears for every decision, including the skips - see Checking it worked.
- For Google Ads, the conversion appears against your conversion action within a few hours, and reporting can take a further 24-48 hours to settle. For Meta, check Events Manager.
Request body
Paste this as the webhook's JSON body, then replace the placeholders with the fields on your own account. ticket_field_<id> takes the numeric field id from Admin Center → Ticket fields.
{
"eventType": "order_won",
"eventSource": "{{ticket.requester.custom_fields.ul_ads_utm_source}}",
"clickId": "{{ticket.requester.custom_fields.ul_ads_click_id}}",
"gbraid": "{{ticket.requester.custom_fields.ul_ads_gbraid}}",
"wbraid": "{{ticket.requester.custom_fields.ul_ads_wbraid}}",
"adsConsent": "{{ticket.requester.custom_fields.ul_ads_consent}}",
"email": "{{ticket.requester.email}}",
"phone": "{{ticket.requester.phone}}",
"currency": "JPY",
"orderId": "{{ticket.ticket_field_<order id field>}}",
"userId": "{{ticket.requester.id}}",
"ticketId": "{{ticket.id}}",
"amount": "{{ticket.ticket_field_<amount field>}}",
"googleConversionActionId": "123456789",
"metaEventName": "Purchase"
}
Every value is quoted on purpose. Zendesk renders a placeholder as text, so an unquoted {{...}} produces invalid JSON the moment the field behind it is empty.
You can leave a line out, or let it render blank - that value is simply treated as unset. Three things are required: eventType, eventSource, and at least one of clickId, gbraid or wbraid. A request missing any of them is rejected on arrival and shows as a failed delivery, with the reason, in Admin Center → Apps and integrations → Webhooks → Activity.
Fields
| Field | Required | What it does |
|---|---|---|
eventType |
Yes | Your own name for this conversion, for example order_won or signup. It appears in Recent uploads and is part of how conversions are de-duplicated. Letters, digits, spaces and . : - _, up to 64 characters. |
eventSource |
Yes | Which platform the conversion goes to: google, facebook, instagram or meta. Normally mapped from the UTM source captured on the customer, so the conversion is reported to whichever platform the click came from. |
clickId |
One of these three | The Google gclid or the Meta fbclid captured when the customer arrived from the ad. Without one of the three there is nothing to attribute the conversion to, so the request is rejected rather than recorded - check the webhook's Activity log, and check the placeholder points at the field your site actually captures into. |
gbraidwbraid
|
One of these three | Google's cookieless click identifiers, captured the same way. Either one stands in for clickId. If you report on one of these, send an orderId too - see One conversion per click. |
adsConsent |
- | Whether the customer consented to ads tracking. Accepts granted/denied, true/false or yes/no. Anything else counts as unknown and falls back to Ads Consent When Unknown. A customer who has not consented is not uploaded to either platform. |
email |
- | The customer's email address. Hashed (SHA-256) before it leaves Unwired Logic - the raw address is never sent to Google or Meta, and never stored. Optional, but it improves how reliably the platforms match the conversion to the click. |
phone |
- | The customer's phone number, hashed the same way. Must include the country code, for example +819012345678. A local number such as 090-1234-5678 has no country in it and is ignored rather than guessed at. |
currency |
- | Three-letter ISO 4217 code, written into the body directly - JPY, USD, EUR, GBP, AUD, DKK, SGD, HKD. Send one whenever you send an amount; without it the ad platform assumes its own account default. |
orderId |
- | Your own order reference, usually a ticket custom field. Sent to the ad platforms as the transaction id so they can match the conversion to the order in their own reporting. |
userId |
- | The Zendesk user id, so an entry in Recent uploads can be traced back to the customer it came from. |
ticketId |
- | The Zendesk ticket id. Send it and each successful upload is written back onto that ticket as an internal note, so an agent can see the conversion went out without opening the app. |
amount |
- | The conversion value. 1250, 1250.50, 1,250.50 and $1,250.50 are all read correctly, so a currency-formatted field can be mapped straight in. |
googleConversionActionId |
- | Required for a Google conversion: the numeric id of the conversion action to report against. See Google Ads Setup Prerequisites. Without it, a Google conversion is recorded as No action named and nothing is uploaded. |
metaEventName |
- | Required for a Meta conversion: the standard or custom event name, for example Purchase or Lead. |
One conversion per click
A conversion is identified by the click it came from, the ad platform it goes to, and its eventType. One ad click therefore produces one order_won conversion on Google no matter how many times the trigger fires on it - so a trigger bound to a field that changes often will not upload repeatedly, and a re-opened ticket will not double-count. The same eventType reported to Google and to Meta counts once on each.
eventType is compared without regard to case, so Order_Won and order_won are the same conversion. To report two different things about the same customer, give them genuinely different eventType values.
If you report on gbraid or wbraid, send an orderId as well - or failing that a ticketId or userId. Unlike a gclid, those two identifiers are not unique to a single click: Google issues them for traffic where a gclid is unavailable, and one value can cover several customers. The order reference is what keeps their conversions apart. Without it, only the first customer's conversion is reported.
Checking it worked
Every decision the app makes appears in Recent uploads, not only the successes - the useful question is usually "why did nothing upload?". History is kept for 90 days.
| Outcome | Meaning |
|---|---|
| Uploaded | Sent to the ad platform and accepted. |
| Upload failed | The ad platform rejected it. The reason is shown in the Detail column. |
| Credentials missing | No usable credential for that platform - check the Google Ads or Meta Ads card. |
| Click id invalid | The click id was not a valid one for that platform, usually a truncated or mis-mapped field. |
| No click id | Older entries only. A request carrying no clickId, gbraid or wbraid is now rejected on arrival, so it appears in the webhook's Activity log instead of here. |
| No action named | The trigger named neither a Google conversion action nor a Meta event. |
| No consent | Ads consent was denied, or unknown with no default set. Nothing is uploaded to either platform. |
If a request never arrives at all, Zendesk shows the webhook's own delivery failures in Admin Center → Apps and integrations → Webhooks → Activity, each with the reason it was rejected. Three causes account for almost all of them: the signing secret in the app does not match the one on the webhook; the body carried no click id; or the body was not valid JSON - which is what an unquoted {{...}} placeholder produces the moment the field behind it is empty.
Google Ads Setup Prerequisites
Four things have to be true in your Google Ads account before conversions upload cleanly.
1. Create conversion actions for click uploads
Create one conversion action for each conversion you want to report, then use its id in the matching trigger's googleConversionActionId.
⚠️ Use an offline / API-upload action, not a website tag. The app uploads using the click ids it was given (gclid / gbraid / wbraid), so every action must be of type UPLOAD_CLICKS - created via the "Conversions offline" path below. A website-tag action (the kind a gtag or GTM pixel creates) will not work; uploads to it are rejected or silently unattributed.
- In Google Ads, go to Tools (or Goals) → Conversions.
- Click New conversion action.
- Choose Conversions offline. Click Edit data sources, select Connect data source later, then save and continue.
- Set the category, name and value settings as you like, then save.
- Open the new conversion action and copy the
ctIdvalue from the page URL. That is the number your trigger sends.
Note - timing: wait about 4-6 hours after creating an action before uploads attribute cleanly to it, and allow a further 24-48 hours for reporting to catch up. The action must be ENABLED.
Note - the type is permanent: a conversion action's type is fixed when it is created; Google has no "edit type" option. You cannot convert an existing website-tag action into an upload-clicks one - leave it as-is for on-site tracking and create a new offline action. Name, category, value settings and status can still be edited later.
2. Tag your ad campaigns with utm_source
⚠️ Required, or no conversion is uploaded. The ad's landing URL must carry a utm_source naming the platform - utm_source=google for Google Ads, or utm_source=facebook, instagram or meta for Meta. That is the value your trigger passes as eventSource, which decides where the conversion is sent.
Google Ads auto-tagging adds the gclid for you, but it does not add utm_source - so set it yourself on each campaign's final URL (for example, append ?utm_source=google). The other UTMs are optional and used only for your own reporting.
3. Turn on enhanced conversions and accept the customer-data terms
⚠️ Required if you send email or phone, or those uploads are rejected. Google only accepts conversions carrying customer data once you have turned on enhanced conversions and accepted the customer-data terms. Until then those uploads fail with a "terms not signed" error, which appears in Recent uploads.
In Google Ads, go to Goals → Conversions → Settings (on some accounts Tools → Conversions) and turn on both switches, accepting the customer-data terms when prompted:
- Enhanced conversions
- Enhanced conversions for leads
Note - the setup method does not matter. Each switch asks you to choose a method for collecting user-provided data (Google tag, Tag Manager, or Google Ads API). That choice governs Google's own on-site collection only - it does not affect this app, which uploads through the Data Manager API regardless. What matters is that both switches are on and the terms are accepted.
Note - timing: accepting the terms can take a few hours to take effect.
4. Check your Data Manager consent settings (EEA / UK)
This is separate from the customer-data terms above, and matters if you advertise to visitors in the EEA or UK. The app labels every upload with that individual customer's own consent, and only uploads when it has that consent - so everything it sends arrives labelled as consented.
The Google page below is the account-level fallback for uploaded data that arrives without a consent label, so it does not normally come into play for this app's uploads. In Google Ads, go to Tools → Data manager → Consent settings, then under Imported and uploaded data:
- No - do not automatically mark this data as consented (the default) is the safe, consent-first choice, and leaves this app's uploads unaffected.
- Yes treats unlabelled uploaded data as consented. Only choose it if you have a lawful basis to assert consent for all uploaded data.
Meta Ads Setup Prerequisites
Complete these one-time steps to obtain the Dataset ID and Conversions API access token for step 5.
1. Create or access Facebook Business Manager
- Visit business.facebook.com.
- Create a Business Manager account or log in to your existing one.
- Complete business verification if required.
2. Create or access a Meta Ads account
- Go to Business Settings → Accounts → Ad Accounts.
- Click Add and create a new ad account, or link an existing one.
3. Set up a Meta Pixel (Dataset)
- Go to Business Settings → Data Sources → Pixels.
- Click Add and create a pixel, or select an existing one.
- Copy the Pixel ID - this is the Dataset ID, and goes in the app's Dataset ID field.
Note: the Meta Pixel ID and the Conversions API Dataset ID are the same number.
4. Enable the Conversions API and generate an access token
- In Business Manager, go to Events Manager.
- Select your pixel / dataset, then the Settings tab.
- Scroll to Conversions API and click Generate access token.
- Copy the token straight into the app's Access token field - Meta will not show it to you again.
Important: treat the access token like a password. Anyone holding it can send events to your ad account. It is stored encrypted and never shown back to you.
Consent and GDPR
This app sends advertising identifiers, and optionally a hashed email and phone number, to Google and Meta so a conversion can be matched to an ad click. Under GDPR and ePrivacy that is advertising data requiring the visitor's prior consent.
As the operator you are the data controller: you are responsible for your cookie-consent banner, your legal basis, and your privacy notice. The app's job is to honour whatever consent decision reaches it.
It does that in a strict order:
-
The customer's own decision wins whenever it is known - the
adsConsentvalue your trigger sends. A customer who refused is never uploaded to Google or to Meta. - Otherwise, your Ads Consent When Unknown setting applies. This is for customers whose consent could never be determined - for example people captured before you switched consent collection on. It never overrides a real decision.
- Otherwise, denied. Left blank, Ads Consent When Unknown defaults to Denied, so nothing uploads.
Set Ads Consent When Unknown to Granted only where you have a lawful basis for those customers. It governs both platforms - a customer treated as denied gets no upload anywhere, because a click identifier is itself data tied to a person, so uploading "just the click id" is not a lesser alternative.
A refusal is permanent. Once a conversion is skipped for lack of consent it is not retried if the setting changes later.
FAQs
Do I need a website pixel as well?
No. This app reports from Zendesk, at the point your own process says the outcome happened. You can run a website pixel alongside it for on-site events; just do not point this app's conversions at a website-tag conversion action (see Google Ads Setup Prerequisites).
Do I have to connect both Google and Meta?
No. Enable either or both. A trigger's eventSource decides where its conversion goes, so you can start with one platform and add the other later.
How many triggers can I have?
As many as you like. There is no configured list of events - every trigger describes its own conversion in the body it sends, so adding one is a Zendesk change only, with nothing to change in this app.
Do I need to add UTM parameters to my ad campaigns?
Yes - utm_source is required, because it becomes eventSource and decides which platform receives the conversion. Google Ads auto-tagging adds the gclid but not utm_source, so set it on each campaign's final URL yourself. The other UTMs are for your own reporting.
What data is sent to Google and Meta?
The click identifier, the conversion name, the time, and - when your trigger sends them - the order reference, the value and currency, and the customer's email and phone hashed with SHA-256. Raw email addresses and phone numbers are never sent and never stored.
Could the same conversion be counted twice?
No. A conversion is claimed against its click id and eventType, so it uploads at most once however many times the trigger re-fires or the webhook is retried.
What if the ticket has no click id?
Nothing is uploaded, and the attempt is recorded as No click id. That almost always means the trigger's placeholder points at a field your site never fills - see step 2.
Why is my Google upload rejected with "terms not signed"?
Enhanced conversions and the customer-data terms are not yet in place, or are still propagating. See Google Ads Setup Prerequisites step 3.
Is my Meta access token secure?
It is encrypted at rest (AWS KMS), stored privately for your account, and never shown back to you or sent to the browser. It is used only to upload conversions to your own dataset. You can revoke it at any time in Events Manager.
Can I revoke access?
Yes. For Google, remove the Unwired Logic service account from your Google Ads account's users. For Meta, revoke the token in Events Manager. For Zendesk, click Revoke on the Zendesk access card - conversions keep uploading, you just lose the ticket notes.
How soon do conversions appear?
For Google Ads, typically within a few hours against the conversion action, with reporting settling over a further 24-48 hours. A newly created conversion action needs 4-6 hours before uploads attribute to it at all.
What if the conversion happens long after the ad click?
Each platform only attributes a conversion if the original click is recent enough for its own attribution window. A very old click id is accepted by the app and uploaded, but the platform may not credit it.
Comments
0 comments
Please sign in to leave a comment.