GA4 events reach Google Ads through the property link, at the user level. An offline conversion upload goes the direct way: KarmaFlow tells your Google Ads account that a specific click (its gclid) became a booked job or a qualified lead, with a value. Smart Bidding then learns from revenue attributed to the click, and Enhanced Conversions for Leads covers the contacts who arrived without a click id.
Nothing is uploaded automatically. You decide which trigger becomes which conversion action in a workflow, with the Upload Google Ads Conversion step.
Availability. Uploading is a write to the Google Ads API. It is switched off platform-wide until Google has approved the change to KarmaFlow's API application; the settings card and the workflow step both say so in words, and every attempt in the meantime is recorded as Not sent, platform switch off. You can prepare everything now: the card, the conversion actions and the workflows all work, and the uploads start the moment the switch is on.
Settings (the gear icon) → Integrations → Google Ads · Local Services → 5 · Offline conversions to Google Ads
Before an account is linked the card explains how to link one and stops there. Once linked:
Validate a conversion (nothing is recorded). Enter a contact id or a gclid and press Validate. The request goes to Google with the validate-only flag: credentials, the action and the row shape are checked, no conversion is counted. The result names the match used (gclid, or N hashed identifier(s)) or shows Google's own rejection text. Disabled while the platform switch is off, because the request itself is a write.
The counters and the uploads table at the bottom list every attempt with its result, the trigger, the action, what it matched by, the value, and a Detail sentence in Google's words when Google rejected it.
| Result | Meaning |
|---|---|
| Uploaded | Google accepted the conversion. |
| Validated (test) | The validate-only test passed. |
| Already recorded (duplicate order id) | Google already holds a conversion with this order id for this action; a re-run of the same event, not a failure. |
| Rejected by Google | Google refused the row; Detail carries the reason (for example an unparseable click id, or a click older than the action's window). |
| Not sent, platform switch off | The platform-wide switch is off. |
| Not sent, uploads off for this account | The account switch is off. |
| Not sent, no conversion action | The step named an action that is not in the account's list; refresh the list. |
| Not sent, no click id or hashable contact data | No click id anywhere on the contact and nothing to hash. |
| Not sent, Do Not Contact | No click id, and the contact is opted out so hashed identifiers were withheld. |
Agents → Workflows → Orchestrations → your workflow → Add Step → Upload Google Ads Conversion
value and currency after a deal stage change).dealId when you want one conversion per deal instead.Under the fields, the same {{field}} chips as the GA4 step show what the workflow's trigger carries. The editor also states the current gate: Uploads are switched off platform-wide for now or Uploads are off for this account, so nobody builds a step that silently does nothing.
How a contact is matched, in order: a click id in the run itself (a chat or form event that carried attribution); the click id stored on the contact; the most recent chat session with a click id; a Google lead-form lead for the contact. With no click id, hashed email and phone are sent as enhanced conversions, unless the contact is Do Not Contact. Consent signals ride on every upload: DENIED for Do Not Contact, GRANTED otherwise.
The step puts googleAdsUploadStatus on the run for later branches.
flowchart TD
T["Trigger fires<br/>(e.g. Conversation analyzed, outcomeType = booked)"] --> S["Upload Google Ads Conversion step"]
S --> F{"Platform switch on?<br/>Account switch on?"}
F -- no --> L1["Logged: Not sent + reason"]
F -- yes --> A{"Conversion action known?"}
A -- no --> L2["Logged: Not sent, no conversion action"]
A -- yes --> M{"Click id on run / contact /<br/>chat session / lead form?"}
M -- yes --> U["Upload by gclid / gbraid / wbraid"]
M -- no --> E{"Enhanced conversions on and<br/>contact not Do Not Contact?"}
E -- yes --> H["Upload by hashed email / phone"]
E -- no --> L3["Logged: Not sent + reason"]
U --> R["Google answers per row:<br/>Uploaded · Already recorded · Rejected (with reason)"]
H --> R
Every row says "Not sent, platform switch off". Expected until Google approves KarmaFlow's API application change; nothing on your side is wrong. Prepare the card and the workflows; they start working when the switch is on.
"Rejected by Google" with an unparseable click id. The stored gclid was truncated or altered on the way to the contact, usually by a form that rewrote the hidden field. Check the form endpoint's submission; the contact's stored click id is what was received.
"Rejected by Google" saying the conversion is outside the window. The click is older than the conversion action's click-through lookback window in Google Ads, or the conversion time you mapped is before the click. Widen the window on the action or leave the time field empty.
The action list is empty after Refresh. The account has no conversion action of the accepting type. Create one in Google Ads under Goals → Conversions → New conversion action → Import → Other data sources or CRMs → Track conversions from clicks, then refresh.