---
id: agencias/conversoes-offline-crm
industry: Marketing agencies
usage: common
effort: medium
grain: One row per conversion event and per destination, current window only
---

# CRM conversions back to Meta and Google

Qualified leads, meetings and sales from each client's CRM sent to Meta and Google exactly once, inside the window each platform accepts.

## Executive summary

**For:** Agencies running media for clients whose sale happens in the CRM, on WhatsApp or in store.

**What it solves**

- Platforms start optimizing for qualified leads, meetings or sales, not for filled forms.
- Each event arrives once, within the deadline the platform accepts.
- Disqualified leads and current customers leave prospecting through exclusion audiences.

**Why it matters:** Meta and Google optimize for what they receive. If they only see the form, they deliver more forms, including from people who never buy. Sending back the CRM result changes who sees the ad, but a duplicated send, one outside the window or one with no identifier spoils the learning without any screen flagging it.

**Business questions it answers**

- Which events were sent to Meta yesterday, by client?
- How many CRM sales were sent back to Google this month?
- Did match quality drop? Which fields are missing?
- Does any client have the table ready and the send turned off?
- How many disqualified leads are in the exclusion audience?

**What you need**

- Each client's CRM connected to Nekt, with the date of each stage
- The stages that become events and each one's value, agreed with the client
- Meta Conversions and Google Ads destinations set up per account, with the conversion action created on the platform

**What you get**

- One table per destination and per client with the current window's events
- Normalized identifiers: email, phone, fbc and external ID
- Customer and disqualified audiences kept up to date
- A monitor that compares the table produced with the send made

## Detailed

### The problem

The same sale must reach Meta and Google in different formats and deadlines. Meta rejects events older than 7 days or dated in the future; Google accepts up to 90 days from the click but rejects empty identifiers. The destination resends the whole table at every run, so whoever does not control the window sends the same event several times. And a successful run in Nekt does not prove the platform matched the sale to a click.

### Business rules

1. **One table per destination.** Meta and Google require different columns, windows and identifiers for the same sale. Build one transformation per client and per destination, already in the destination's format. Never a single table serving both. (Avoid: One conversions table for every destination.)
2. **Window inside the limit, with margin.** The Meta Conversions API rejects event_time older than 7 days: use a 6-day or 132-hour window, never exactly 7. Google Ads Click Conversions accepts up to 90 days after the click (63 days for enhanced conversions for leads): real pipelines use 55 days to leave a margin. An event dated in the future makes Meta reject the batch: cap event_time at the run time. (Avoid: An exact 7-day window or a months-long backfill to Meta.)
3. **The table holds only the current window.** The destination resends the whole table at every run. So the table carries only the current window's events, tied to the run time, with disjoint windows when it runs several times a day. Each update goes out once and the daily count matches Events Manager. (Avoid: A cumulative table with the whole history.)
4. **Deterministic event_id.** Build event_id from the lead or deal ID and the event name (<id>-<event>). If the same deal comes back in a later window, it goes out with the same event_id and Meta's deduplication absorbs it. On Google, use order_id on the same principle. (Avoid: A random event_id or one with the run date.)
5. **Normalized identifiers.** Email lowercase, no spaces and with an @. Phone digits only, with 55 in front when it has 10 or 11 digits, valid with 12 or 13. First name letters only. fbc in the format fb.1.<timestamp in ms>.<fbclid> when the landing URL has an fbclid; fbp, IP and user agent from the page when they exist. A row with no email and no phone does not go out. (Avoid: Sending the phone number as typed.)
6. **A stage becomes an event with an agreed name and value.** Each client defines which stages become events (qualified lead, meeting booked, proposal, sale) and each one's value. A sale carries the won deal value; an intermediate stage may carry an agreed fixed value. Events from the CRM alone go as system_generated (offline); events with page data go as website, the only kind that gets a match quality score. (Avoid: Sending every new deal as Purchase.)
7. **One owner per event.** If the pixel, an automation or another tool already sends an event (calendar booking, form lead), the CRM pipeline does not send the same fact under another name. Use a flag on the lead to avoid firing again and keep event names distinct between pipelines. (Avoid: Two sends of the same booking under different names.)
8. **Only what may have come from the platform.** Send the conversions that may have a click on the destination: when the client asks, only deals with a Meta source go to the Conversions API. Never send imported bases, test contacts or emails from the client's or the agency's team. (Avoid: Sending the whole CRM base.)
9. **Sent is not counted.** Check both sides: the transformation ran, the destination is active and ran after it. A recent transformation with an inactive destination is a signal never delivered. And even when the send is accepted, without a matching click in the window the sale does not become a conversion: the sign that it works is the offline conversion action showing conversions in the ad account. (Avoid: Concluding the platform received it because the table exists.)
10. **Audiences use the same cleanup.** Customer audiences (Customer Match, Custom Audiences) and exclusion audiences (disqualified, current customers, another business line's base) use the same email and phone normalization, with no test or team contacts, deduplicated by phone. Disqualified is the disqualification stage or a loss for profile reasons, agreed with the client. (Avoid: Exporting the CRM list without cleaning it.)

### Limits of each destination

What each platform accepts and what the pipeline does to fit.

- **Meta Conversions API** → event_time up to 7 days (`6-day or 132-hour window`)
- **Meta · event in the future** → Batch rejected (`event_time capped at the run time`)
- **Meta · deduplication** → Same event_id within 48 hours (`<id>-<event>`)
- **Meta · action_source website** → Gets a match quality score (`em, ph, fbc, fbp, IP, user agent`)
- **Meta · action_source system_generated** → Offline, no match quality score (`CRM sale`)
- **Google Ads Click Conversions** → Up to 90 days from the click (`55-day window`)
- **Google · empty identifier** → Row rejected (`only rows with a valid email`)
- **Customer Match and Custom Audiences** → Customer and exclusion audiences (`same normalization`)

### Definitions

| Metric | Definition |
|---|---|
| Conversion event | one row per CRM record and stage, with event_name, event_time, event_id and identifiers, inside the destination's window |
| Current window | update interval of the records this run sends; it does not overlap the previous run's window |
| event_id | lead or deal ID plus the event name |
| Identification rate | events with a valid email or phone / eligible events in the window |
| Events delivered | table events covered by a completed destination run that came after the transformation |
| Counted conversions | conversions of the offline action in the ad account in the period (on Google, an UPLOAD_CLICKS action) |
| Exclusion audience | normalized contacts deduplicated by phone in a disqualification or customer stage, with no tests |

### Pitfalls

- **Half-built pipeline.** The transformation ran every day and the table was right, but the destination had been inactive for weeks. Google kept optimizing for leads. How to avoid: A monitor comparing the table produced with the send made.
- **Batch rejected by date.** One event dated a few minutes in the future made Meta reject the whole batch. How to avoid: Cap event_time at the run time.
- **Double count in Events Manager.** The cumulative table was resent at every run and the same lead showed up several times a day. How to avoid: Disjoint window tied to the run time.
- **Deduplication that did nothing.** Complete and partial form responses had IDs from different spaces; deduplicating by response ID removed nothing and the lead went out twice. How to avoid: Deduplicate by normalized phone within a 7-day window.
- **A sale that is an echo.** The sale conversion in the ad account was data sent by the agency itself and was presented to the client as platform measurement. How to avoid: Label offline conversions as sent by the agency.
- **Sent and never counted.** Months of successful sends and no offline conversion attributed, because the emails did not match clicks within the window. How to avoid: Track the offline action in the account, not the send status.

### Acceptance test

- [ ] No event_time in the Meta table is older than 7 days or in the future at send time.
- [ ] Across 7 consecutive runs, each event_id appears in a single window, and the daily count in Events Manager equals the table's.
- [ ] Every row has a normalized email or phone, and every phone has 12 or 13 digits starting with 55.
- [ ] For each active conversion table there is an active destination that ran after it within the last 24 hours.
- [ ] In a closed month, sales sent to Google show up in the account's offline action, or the difference is explained.

### Context document

```text
CRM conversions to the platforms
- Each client has one table per destination. Conversion tables are outputs for Meta and Google, not a source for results analysis.
- Meta Conversions API: event_time up to 7 days (6-day or 132-hour window) and never in the future. Deduplication by event_id within 48 hours.
- Google Ads Click Conversions: up to 90 days from the click (63 for enhanced conversions for leads); rows with no identifier are rejected.
- event_id = record ID + event name. The table holds only the current window.
- CRM-only events go as system_generated (offline, no match quality score). Events with page data go as website.
- A table produced is not a send made: confirm an active destination and a run after the transformation. A send accepted is not a counted conversion: check the offline action in the account.
- A sale conversion showing on the platform was sent by the agency. Do not present it as independent measurement.
Per-client exceptions:
- <client>: events <LeadQualificado>, <ReuniaoMarcada> and <Purchase>; LeadQualificado = lead still open <n> days after creation.
- <client>: only deals with a Meta source go to the Conversions API.
- <client>: fixed value of <x> for stage <stage> on Google.
```

### Variations

- **Forms with partial responses:** Partial and complete go out under the same event name, the partial event_id gets its own suffix and deduplication is by phone.
- **One pipeline per end client:** Agencies with dozens of clinics or franchises generate one table and one destination per form, from a template.
- **Checkout sale:** An approved sale on an info product platform sent per transaction, with a validated email.
