Integrations
Settings → Integrations connects Evolzer CRM to the outside world: social accounts, email, DNS providers, TeamFlow, Meta lead capture and reverse sync, Google Ads conversions, and your own website. Sending leads out to another system is its own Settings section — see Lead Forwarding at the end of this page. Letting another CRM update your lead stages has its own section too — see Incoming Status Webhooks.
Admins only.
Connected Accounts
A card grid of social connections — Meta (Facebook & Instagram), X, and LinkedIn — each showing Connected / Not connected / Setup needed. Click a card to open its sheet and use the one-click Continue with … OAuth login; no manual token pasting. Connected cards show the account name and token expiry, with Reconnect and Disconnect buttons.
The Meta connection is the one the Lead Chains wizard and the Conversions API pixel picker reuse.
Email (SMTP)
Send drip campaigns, workflow emails, and Copilot mail through your own mail server:
- Tick Use custom SMTP for outbound email.
- Fill in host, port, username, password, From address, and TLS/SSL.
- Set the Daily send limit (protects your sender reputation) and click Save SMTP Settings; Send test email delivers to your own address and logs in the Inbox.
When disabled, mail goes through the built-in provider instead. This card also holds the overdue-task daily digest toggle and the Inbound email webhook: generate (or rotate) a key and point your provider's inbound hook at the shown URL so replies land in the Inbox — linked to the sender's lead, logged on their timeline, and stopping their active drips.
DNS Providers
Connect Cloudflare, GoDaddy, or Hostinger with an API token (GoDaddy needs key + secret) and a label. Landing pages use these connections to point a custom domain at the CRM automatically instead of you editing DNS records by hand. Removing a connection keeps already-connected domains working, but they can't be re-synced.
TeamFlow
When a deal is marked won, a delivery project is created in TeamFlow with the client's details. Enter the TeamFlow URL and its integration API key, tick the enable checkbox, and Test connection. See the Projects guide.
Meta Instant Leads
The org-level webhook for Facebook & Instagram Lead Ads (the manual alternative to a Lead Chain's OAuth connection). The card gives you the Callback URL to paste into the Meta App Dashboard → Webhooks, a Verify Token generator, and fields for the Facebook Page ID, App Secret, and a Page Access Token with leads_retrieval permission. Secrets are stored encrypted and shown masked after saving (••••xxxx); enter a new value to replace one. The setup steps are printed on the card.
Meta Reverse Sync (Conversions API)
Report lead progress back to Meta so your ads optimize for leads that actually convert, not just form fills.
Setup
- Enable the card's toggle.
- Connect your Business Manager Account — the picker lists the Business Managers on your connected Facebook account (no separate auth step).
- Choose your Pixel — a picker with Owned Pixels and Client Pixels tabs, so agencies can send events to a client's pixel shared with their Business Manager.
- Optionally paste a Conversions API Access Token — only needed for a dedicated System User token; otherwise the connected Facebook account is used.
What gets sent
- When a lead moves to a stage — set the Meta event name to send for each of your pipeline's stages (every stage except the first, which is a lead's starting point and has no event of its own), e.g. "Appointment" for a Qualified-equivalent stage. Leave a stage blank to not send an event for it. Stages come from your own Lead Stages setup (Settings → Lead Stages), so this list reflects however you've named and ordered your pipeline — not a fixed set.
- When a deal closes won — its own toggle and event name (default Purchase); the deal's value is included.
Only leads captured from Meta Lead Ads are eligible — Meta needs the original leadgen id to attribute the event. Each event sends whichever match fields the lead has (email, phone, name, city, zip, country), all hashed with SHA-256. Enable Conversion Leads optimization in your Meta campaign to use these signals.
Privacy note (shown on the card): this shares SHA-256 hashes of customer contact details with Meta — confirm your privacy policy covers it before enabling.
Note: the pixel's name is display-only and not persisted — after a reload the field shows just the pixel id until you reopen the picker.
Google Ads Conversions
Tell Google Ads which leads turn into real business. When a lead moves to a stage you choose (for example Qualified or Admission Confirmed), or a deal is won, the CRM reports it to Google Ads as an offline conversion. Google then bids for more people like the leads that actually progress, not just anyone who fills a form.
Each workspace connects its own Google account and ad account. The card sits in Settings → Integrations, right below Meta Reverse Sync, and has its own Save button (the Save Integrations button at the bottom does not save it).
Conversions are sent through Google's Data Manager API. The Google Ads API is used only to list your ad accounts and conversions for the pickers.
Server setup (once, by the person who runs the CRM)
- Google Cloud project. Use the project whose Google Ads API access is approved (Explorer access or higher; check APIs & Services → Google Ads API). In APIs & Services → Library, enable both the Google Ads API and the Data Manager API.
- Google Auth Platform (in the same project):
- Branding: app name, support email, your logo and domain.
- Data access: add the scopes
https://www.googleapis.com/auth/adwordsandhttps://www.googleapis.com/auth/datamanager(plusopenidandemail). - Audience: External.
- Clients: create a Web application client. Add the redirect URI
https://<your-crm-domain>/api/auth/google-ads/callback. It must match exactly: same domain,https, no trailing slash.
- Environment. Set
GOOGLE_ADS_CLIENT_IDandGOOGLE_ADS_CLIENT_SECRETfrom that client and restart the app.AUTH_SECRETmust also be set (it normally is), because it signs the sign-in check.SECRETS_ENCRYPTION_KEYmust be set too: Google's sign-in grants full access to the ad account, so the CRM only stores it encrypted, and the card says Google isn't set up until the key is there.GOOGLE_ADS_DEVELOPER_TOKENis optional: Google retired developer tokens on 9 September 2026, and API access now comes from the Cloud project. To pause all uploads, setGOOGLE_ADS_CONVERSIONS_DISABLED=1and restart. - Publishing status. While the app is in Testing, only the test users you list can connect, and the connection breaks every 7 days because Google expires the refresh token. Switch to In production and complete Google's verification (both scopes are sensitive, so this takes a few weeks). Until it's verified, users see an "unverified app" screen they can click through.
- The person who connects needs Admin or Standard access to the ad account (or to its manager account) and 2-step verification on their Google account.
If the Cloud project doesn't have Google Ads API access yet, the account and conversion pickers show an error. You can still type the IDs by hand (see below): sending conversions doesn't need that access.
In Google Ads
- Create one conversion per stage you want to report: Goals → Conversions → New conversion action → Import → CRMs, files or other data sources → Track conversions from clicks. Set Count to One and the click-through window to up to 90 days. Only this type ("Import from clicks") shows in the CRM. Google doesn't accept iPhone click IDs (
wbraid/gbraid) on One conversions, so those leads are matched by email and phone only; pick Every if many of your ad clicks come from iPhones and you want them matched by click too. - Wait 4–6 hours after creating a conversion before the first upload. Google rejects uploads to brand-new conversions, and the CRM retries them on its own.
- Turn on enhanced conversions for leads and accept the customer data terms (Goals → Conversions → Settings). If a manager account owns your conversions (cross-account conversion tracking), do this on the manager account.
- Keep auto-tagging on (Admin → Account settings), so your ad clicks carry a
gclid.
Connect and map
- Open Settings → Integrations → Google Ads Conversions and click Connect Google. Sign in and allow every permission Google asks for. If you untick one, the CRM asks you to connect again.
- Pick the Ad account. Accounts you reach through a manager account show "· via manager name", and the CRM uses that manager to access them. Refresh loads the list again. If the list can't load, click Enter IDs by hand and type the Customer ID (top of Google Ads, like
123-456-7890) and, only if you reach the account through a manager, the Manager ID. If a manager account owns your conversions (cross-account conversion tracking), also type the manager's ID as Conversion owner ID; otherwise Google rejects every upload with "can't find this conversion". In that mode you also type each conversion's ID: open the conversion in Google Ads; it's the number afterctId=in the page address. - Any warnings about the account (customer data terms not accepted, enhanced conversions for leads off) show under the picker.
- Tick Send conversions to Google Ads.
- Also match by email and phone (on by default) sends the lead's hashed email and phone, so Google can match leads that have no click ID. Phone country code (default
91) is added to numbers saved without one: a 10-digit number, or for codes other than 91 a 6- to 10-digit number (for example Singapore or the UAE). A written trunk(0), as in+44 (0)20 …, is dropped. - Tick Our leads agreed to ad data use if your forms ask for consent. You need this for visitors from the EU and UK.
- In the table, pick a Google conversion for each stage under When a lead moves to, or leave it at Don't send. Each Google conversion can be used for one stage only, otherwise a lead moving through two stages would count twice in Google. The first stage (where every lead starts) isn't listed.
- Optional: tick Send won deals and pick a conversion. It sends the deal's value and currency. If the deal has no currency, the ad account's currency is used.
- Click Save, then Validate. Validate sends a test only upload for each mapped conversion and shows ✓ or ✗ with Google's answer. Nothing is recorded in Google Ads.
If you switch to a different ad account, the stage mapping is cleared, because conversions belong to one account. Pick them again and save.
How do I know it's sending?
The Recent conversions log at the bottom of the card shows the last 50 (newest queued first), with filters for All, Sent, Failed and Skipped. Failed also lists sent conversions whose batch Google later rejected, fully or partly:
| Status | Meaning |
|---|---|
| Waiting | Queued. The CRM sends every minute. It also holds a conversion until 6 hours after the ad click, because Google rejects newer clicks. The details say why it's waiting. |
| Sending | Being sent right now. |
| Sent | Google received it. Later the details show Google's result for that batch: processing, accepted, partly accepted or rejected, with reasons such as "click not found". Google only gives counts per batch, not a result per lead. |
| Partly accepted / Rejected by Google | Sent, but Google didn't match some (or all) of that batch. The details give Google's reasons. |
| Retrying | Google or the network had a problem. The CRM tries again after 5 min, 30 min, 2 h, 6 h and 24 h. |
| Failed | Google rejected it for good (the details give Google's reason), or every retry failed. |
| Skipped | Not sent, with the reason (see below). |
Results show in Google Ads (Goals → Conversions, and the upload history under Goals → Conversions → Uploads) within about 3 hours. Google dates a conversion on the ad click, not on the day the lead moved.
If the card says Reconnect needed, Google access was revoked or expired (for example after 7 days while the app is in Testing). Click Reconnect. Nothing is lost: waiting conversions are sent once you're connected again. Disconnect stops sending, skips anything still queued, and removes Google's access, unless another workspace uses the same Google account.
What gets sent to Google
- A transaction ID made of the lead and the stage (
<lead id>:<stage id>, or<lead id>:wonfor a won deal). Google uses it to drop duplicates. - The time the lead moved, in your organisation's time zone (with its UTC offset).
- One click ID:
gclid, otherwisewbraid, otherwisegbraid. The iPhone click IDs (wbraid,gbraid) only go to conversions set to Count Every. - If email and phone matching is on: the email and phone, normalised and SHA-256 hashed before they leave the CRM. Emails are lower-cased; for gmail.com and googlemail.com the dots and anything after
+are removed. Phones are converted to+<country code><number>. - For won deals: the deal value and currency.
Names, notes, addresses and custom fields are never sent.
Why a conversion is skipped
- Sync off: Send conversions to Google Ads was off when it was due.
- Not connected: Google was disconnected.
- Lead deleted.
- No identifier: the lead has no Google click ID, and no email or phone (or email and phone matching is off). A lead with only an iPhone click ID going to a Count One conversion is skipped the same way unless email and phone matching can be used.
- Too old: the lead is older than the conversion's click window (90 days at most).
- Click too old: the ad click was more than 90 days ago by the time the conversion could be sent (for example after a long wait to reconnect Google). If email and phone matching is on and the lead has an email or phone, it is sent without the click ID instead of being skipped.
Moving a lead back and forth counts once per stage: if Lead → Qualified was already sent, moving it out and back doesn't send it again. A conversion that was skipped or failed is queued again when the lead re-enters that stage. Bulk stage changes from the Leads table are included. Leads moved by the old workflow Update status step, or reassigned when a stage is deleted, are not reported.
Capturing the click ID
Google needs the click ID (or email/phone matching) to tie a lead to an ad click:
- Google Ads lead forms: automatic. The click ID comes with the lead.
- CRM landing pages: automatic. The page reads
gclid,gbraidandwbraid(and theutm_*tags) from its address and keeps them for the visit, so they're sent even if the visitor clicks around first. If the email belongs to a lead you already have, the click ID is noted on its timeline but not added to the lead (the page is public, so anyone could submit someone else's email). - Your website and form builders: send fields named
gclid,gbraidandwbraid(also accepted:gcl_id,gclid_field,google_click_id), or apage_url/landing_pagefield holding the full page address with its query string (https://…; a field with that name holding anything else stays a normal field). The snippets on the Website Leads card already include the click ID fields. For Elementor, see the hidden field step under Elementor and other no-code form builders.
Website leads that arrive with a click ID get the source Google. Landing-page leads keep the source Landing page.
Website Leads
Capture form submissions from any website via a simple API call:
- Enable the toggle and click the generate button to create an API Key. Copy it immediately — it's shown only once; after saving, only the last 4 characters display. Regenerating invalidates the old key on save.
- Copy the ready-made snippet into your website's form handler — it POSTs
name(required) plus optionalemail,phone,message, andsourceto the public lead endpoint with the key in thex-api-keyheader. Pick the language with the toggle above the code — JavaScript, React, PHP, Angular or cURL — and the copy button copies whichever one is showing. Each example already contains your organisation's real custom-field keys. The cURL version uses sample values, so you can paste it into a terminal to check the key works: a lead appears in Leads straight away. The PHP example runs on your own server, which keeps the key out of the browser — the safest option if your site has a backend.
Submissions arrive on the Leads page with Source "Website". Spam tip from the card: add a hidden input named website to your form — submissions that fill it are treated as bots and silently dropped.
Custom fields and UTM parameters
The same request can carry attribution and any custom fields you've defined for leads.
UTM parameters — send them at the top level of the body, in either snake_case (as most marketing tools emit them) or camelCase:
body: JSON.stringify({
name: form.name,
email: form.email,
utm_source: params.get("utm_source"), // or utmSource
utm_medium: params.get("utm_medium"), // or utmMedium
utm_campaign: params.get("utm_campaign"), // or utmCampaign
utm_term: params.get("utm_term"),
utm_content: params.get("utm_content"),
})
A typical pattern is to read them from the landing page's URL when it loads (new URLSearchParams(location.search)), store them in hidden inputs or sessionStorage, and include them when the form submits — so a visitor who browses for a while before filling the form still carries the campaign that brought them.
They land on the lead's UTM fields and are available everywhere UTMs are: the Leads table columns, Search & conditions filters, workflow conditions, and Meta Reverse Sync attribution.
Custom fields — first define the field in Settings → Custom Fields (entity: Lead) and note its key. Then send values under a customFields object keyed by those keys:
body: JSON.stringify({
name: form.name,
customFields: {
budget: form.budget, // key exactly as defined in Settings
preferred_city: form.city,
},
})
Values are checked against your definitions before saving: keys you haven't defined are dropped, numbers are parsed, dates normalised, checkboxes coerced to true/false, and select values must match one of the field's options (anything else is stored as empty). This means a stranger who finds your endpoint can't write arbitrary data into your leads — only the fields you've chosen to expose.
The Website Leads card shows your organisation's actual field keys in its snippet, so you can copy them directly. Custom field values appear in the lead drawer's Custom Fields section, as optional table columns, and can be used in Search & conditions and workflow conditions (cf: fields).
Elementor and other no-code form builders
Form builders that cannot send custom headers — such as Elementor Pro — can pass the API key as a URL query parameter instead of a header:
https://your-domain.com/api/public/leads?key=<YOUR_API_KEY>
Setup in Elementor Pro:
- Open the Elementor editor, select your form widget, and open Actions After Submit.
- Add the Webhook action and paste the URL above (with your real key in place of
<YOUR_API_KEY>). - In each form field's Advanced tab, set its Custom ID to the matching field name:
name— the submitter's full name (required; the endpoint also acceptsemailorphoneas a fallback)email— email addressphone— phone numbermessage— free-text message
- For any Lead custom field, set the Custom ID to the field's key exactly as defined in Settings → Custom Fields (Lead).
- For Google Ads Conversions, add a Hidden field with Custom ID
gclid, and set its Default Value to Dynamic Tags → Request Parameter →gclid. Repeat forgbraidandwbraid. This only works on the page the visitor landed on from the ad. If your form is on a different page, the site must keep the parameter itself (for example in a cookie orsessionStorage), or send the landing page address in a hiddenpage_urlfield.
The endpoint accepts both simple Elementor webhook payloads (field labels as top-level keys) and advanced payloads (the nested fields[id][value] format), as well as Contact Form 7 field names such as your-name and your-email.
Security note: A key in a URL can appear in server access logs and in your form builder's configuration panel — treat it like a password. If it leaks, generate a new key on the Integrations page and save; the old key stops working immediately.
Remember to click Save Integrations at the bottom after changing the Meta or Website cards.
Lead Forwarding
Everything above brings data into the CRM. To push new leads out — to a client's own CRM, a school admissions platform such as Schoolknot, or a Zapier, Make or n8n webhook — use Settings → Lead Forwarding, a separate section in the Integrations group of the Settings panel. There you add a destination (URL, headers, encrypted secrets, a body template with {{lead.*}}, {{custom.*}} and {{utm.*}} placeholders, value maps and source filters), preview and test it against a real lead, and follow every send in a Deliveries log with automatic retries. Full details in the Lead Forwarding guide.
Incoming Status Webhooks
Lead Forwarding sends leads out. Incoming status webhooks work the other way round: another CRM — for example a school admissions system — calls your webhook whenever a lead's status changes there, and the lead moves to the matching stage here. It is exactly like moving the lead yourself: the timeline gets an entry such as Status from Schoolknot: Admission Confirmed → Converted, and workflows, drip exits and Meta Reverse Sync all fire as they would for a manual move. If the lead is already in that stage, nothing happens.
Admins only (the Manage integrations permission).
Setup
- Open Settings → Status Webhooks (in the Integrations group) and click New webhook. Name it after the other CRM — the name appears on the lead timeline.
- Copy the Secret key straight away. It is shown only once; afterwards only its last 4 characters are visible. Rotate key issues a new one.
- Give the other CRM the Webhook URL and the key — or simply copy the ready-made Instructions for the other CRM block, which already lists your stage names and the fields to send. Example request shows the same call as curl (JSON or form) and as a JSON body.
- Under Status mapping, map each of their status texts to one of your stages. New texts that arrive without a mapping appear under Unmapped statuses; pick a stage and click Map. Also move leads whose latest update was this status is ticked by default: it also moves the leads that were waiting on that status (last 7 days, up to 100 leads). A lead is left alone if a newer call came in for it, or if its stage was changed some other way since (by hand, a workflow or another webhook). Untick it to only save the mapping. Typing a waiting status into the Add row offers the same option.
- Watch the Log tab to see each call that reached the webhook and what happened to it. Calls with a missing or wrong key are rejected (401) and are not logged, so if nothing shows up, check the URL and the key (or rotate it and send the new one).
For exact matching, the other CRM needs our lead ID. If you also send leads to it with Lead Forwarding, add a field such as "crm_lead_id": "{{lead.id}}" to the forwarding body so it can store the ID and send it back as lead_id. Without it, the webhook falls back to the phone number or email.
The URL
https://your-domain.com/api/webhooks/lead-status/<webhook-id>
Each webhook has its own URL. POST, PUT and PATCH are all accepted. Send the key in one of three ways:
- the
X-Api-Key: <key>header (preferred) Authorization: Bearer <key>?key=<key>at the end of the URL
What to send
Send JSON, form fields (application/x-www-form-urlencoded), multipart form data or JSON as text/plain — up to 64 KB. Field names are matched without regard to capitals, spaces, dashes or underscores, so leadStatus, lead_status and Lead Status are the same field. Values in the query string are used only for fields the body does not have — field by field, so status in the URL never overrides lead_status in the body.
| Field | Also accepted | Notes |
|---|---|---|
status | lead_status, stage, leadStage, disposition, state | Required. Their status, as text. state is used only when none of the other names appears anywhere, so an address state never hides a nested status. |
lead_id | crm_lead_id, our_lead_id, reference, ref, id | Our CRM lead ID — the most reliable way to find the lead. |
external_lead_id | leadgen_id, fb_lead_id | The Facebook/Google lead ID. |
phone | mobile, phone_number, mobile_number, mobile_no, phone_no, contact_number, whatsapp | Any format; the last 10 digits are compared. An extension or a second number (after ext, x, /, , or ;) is ignored. |
email | email_address, email_id | Must match exactly, without regard to capitals. |
remarks | remark, notes, note, comments, comment | Optional; added to the timeline entry. |
Fields may also sit one or two levels deep, e.g. {"data": {"lead_id": "…", "status": "…"}}. If the other CRM uses different names, set them under Advanced: field names on the Setup tab and click Save field names — for nested JSON use dots, e.g. data.lead.status. When a field name is set there, only that name is used for it, and the instructions, examples and error messages switch to that name.
How the lead is found
The webhook tries, in order: our lead ID, then the Facebook/Google lead ID, then the phone number (last 10 digits of phone or mobile), then the email address. Each of the last three can be switched off under How we find the lead (each switch saves straight away). If our lead ID is sent but not found, the enabled fallbacks are still tried. If a phone number, email or Facebook/Google lead ID matches more than one lead, nothing is changed and the call is logged as More than one lead — send lead_id instead. Only leads in your own workspace are ever matched.
How statuses map
Capital letters and extra spaces are ignored, so Admission Confirmed and admission CONFIRMED are the same status. A status that exactly matches one of your stage names (for example Converted) is applied even without a mapping. If a status is mapped to a stage that you later delete, calls with it are logged as Stage deleted until you update the mapping.
Responses
Every response is JSON: {"ok": true, "outcome": "APPLIED", "leadId": "…", "stage": "Converted", "message": "Lead moved to Converted."}. A 200 means "received" — read outcome to see what happened. Senders should not retry a 200.
| Outcome | HTTP | What it means / what to do |
|---|---|---|
APPLIED | 200 | The lead moved to the mapped stage. |
UNCHANGED | 200 | The lead was already in that stage; nothing changed. |
UNMAPPED | 200 | The status has no mapping yet. Map it under Unmapped statuses. |
LEAD_NOT_FOUND | 200 | No lead matched the identifiers sent. |
AMBIGUOUS | 200 | More than one lead matched the phone, email or Facebook/Google lead ID. Send lead_id. |
INVALID_MAPPING | 200 | The status is mapped to a stage that no longer exists. Update the mapping. |
INVALID | 400 | The body could not be read, or status or every lead identifier was missing. Fix the request. |
UNAUTHORIZED | 401 | Missing or wrong key, or an unknown webhook. |
DISABLED | 401 | The webhook is switched off, or the Leads module is off for this workspace. |
METHOD_NOT_ALLOWED | 405 | Use POST, PUT or PATCH. |
TOO_LARGE | 413 | The body is over 64 KB. |
RATE_LIMITED | 429 | Too many calls; wait for the Retry-After seconds. |
ERROR | 500 | A temporary problem on our side. Retry later — if the lead had already moved, the retry completes the update (timeline entry, workflows and Meta Reverse Sync). |
Limits
- Up to 120 calls per minute per webhook. Beyond that the webhook answers 429 with
Retry-After: 60. Leads moved by Map (re-apply) do not count. - The log keeps 90 days (older calls are deleted automatically, also for webhooks that no longer receive calls), up to 2,000 calls per webhook. The Log tab shows the latest 50, filterable by outcome; click a row to see the message and the fields received (anything that looks like a key, token or password is removed before it is stored).
- Up to 20 status webhooks per workspace.
Security note: A key in a URL can end up in server access logs, so prefer the
X-Api-Keyheader. Treat the key like a password. If it leaks, click Rotate key — the old key stops working at once, so update the other CRM with the new one straight away.