Connect your website
Connect your own website to ConnectWiz and three things start to happen:
- ConnectWiz learns which ad, campaign or site brought each visitor — the
campaign tags of the link and the ad click id (Google's
gclid, Meta'sfbclid…), when the visitor allows it. - A tap on a WhatsApp link on your site carries a short ref code into the chat, so the conversation can be tied back to the ad that brought the person.
- The forms on your site arrive in your ConnectWiz forms, with the visitor's source attached — the same submissions, contacts, tags, notifications and flows as a form hosted by ConnectWiz.
If your site already runs the ConnectWiz chat widget, its code does all of this too — no second code, no second key. A site without the chat widget uses a small standalone script instead. Nothing here replaces your own analytics or Google Tag Manager: either code runs next to them.
The website connection is part of every plan. Setting it up needs the
Manage integrations permission (integrations.manage). Sending the results
on to Meta and Google Ads, and the conversion report, are Pro and above —
see Send conversions to Meta and Google Ads.
Open the website screen
Go to Settings → Integrations and open Website (under Advertising). The first time, give the site a Site name and its Domains, one per line, leave Capture with the chat widget code on if the site runs the chat widget, then press Connect site.
example.comalso coverswww.example.com.*.example.comcovers every subdomain.- The browser key — and the chat widget's key, for this — works only from these domains: a page on any other domain that copies your snippet is refused.
You can connect more than one site (Add another site), each with its own keys and settings. Connection on switches a site off without deleting it; Disconnect site removes it — its keys stop working at once, and the visits already received stay.
What the site script does
Under What the site script does:
| Switch | What it means |
|---|---|
| Record visits that come from an ad, a campaign link or another site | A page view is recorded only when it carries a campaign signal (utm tags, an ad click id) or came from another site. Plain visits from your own pages or typed addresses are not. |
| Add a ref code to WhatsApp links | See WhatsApp ref codes below. |
| Forms the script fills | Only forms marked data-cw-form (default) or Every form on the page. |
Keys
A site has two kinds of key. They are not interchangeable. A site linked to the chat widget also accepts the chat widget key (the one already in your chat widget snippet) in place of the browser key — from the site's domains only.
| Key | Looks like | Where it goes | What it can do |
|---|---|---|---|
| Browser key | pk_… | In your pages' HTML. Public by design. | Report visits and WhatsApp clicks — only from the site's domains. It cannot read anything and cannot submit forms. |
| Server key | sk_… | In your web server's settings (an environment variable), never in a page. | Send form submissions to ConnectWiz (scope forms:submit). Nothing else. |
- New browser key replaces the browser key. The old one stops working immediately, so update the snippet on your site straight away.
- Create server key gives the key a name (for example Production server) and shows it once: Copy this key now. It is not shown again. If you lose it, use Replace — a new key is issued and the old one stops.
- Revoke switches a server key off for good. The list shows each key's Last used date, so you can see which one your server really uses.
Install
Your chat widget code already does it
With Capture with the chat widget code on, Install says Source capture runs with the chat widget code and shows the chat widget's own snippet:
<script async
src="https://api.connectwiz.net/widget/widget.js"
data-widget-key="…"
></script>
If the chat widget is already on the site, there is nothing to add. The widget code captures sources, adds WhatsApp ref codes and fills the form fields exactly like the standalone script, under the same consent rules.
- It starts at once, before the chat and independent of it: also in manual
mode (
data-manual) beforeSmaibot.init(), and while the chat itself is switched off. - The switches under What the site script does apply without touching the code: the widget asks ConnectWiz for them once per browser tab.
- The chat widget key works for this only from the site's domains. On any other domain the widget code still shows the chat but captures nothing — links and forms are left alone.
- To keep one page out, add
data-attribution="off"to the tag (orattribution=offin the address under Google Tag Manager).data-cookie-domainworks as below. - When a visitor who came from an ad starts a chat, the chat and the visit are the same person: the visit is added to the contact the chat creates, and the landing is counted once.
Turning the switch on is a deliberate step: the chat widget key is pasted on every site that shows the chat, so it captures sources only where you named the domains here.
Standalone script (sites without the chat widget)
For a site that does not run the chat widget, switch Capture with the chat
widget code off. Install then shows the standalone snippet with the site's
browser key filled in. Paste it into every page, before </head>:
<script async src="https://api.connectwiz.net/widget/cw-attribution.v1.js" data-key="pk_…"></script>
Copy it from the panel rather than from here: the panel's version carries your key and your switches. If both codes end up on one page, the first one to start does the work and the other stays out.
Options
Each option of the standalone script is a data-* attribute on the tag:
| Option | Values | Default |
|---|---|---|
data-key | Your browser key (pk_…). Required. | — |
data-forms | all fills every form on the page | Only forms with data-cw-form |
data-whatsapp | off leaves WhatsApp links alone | On |
data-visits | off stops recording visits | On |
data-cookie-domain | .example.com shares the visitor across your subdomains | The current host |
The panel adds data-forms, data-whatsapp and data-visits for you when you
change the switches.
Google Tag Manager
Add the snippet as a Custom HTML tag that fires on all pages. If your
container drops data-* attributes, put the options in the address instead —
the script reads both:
<script async src="https://api.connectwiz.net/widget/cw-attribution.v1.js?key=pk_…&forms=all"></script>
What it puts into your forms
In every form it fills, the script adds two hidden fields:
cw_attribution— where the visitor came from (JSON);cw_event_id— a fresh id for this submission.
On submit it also pushes { event: "cw_lead_attribution", cw_event_id } to the
dataLayer. Use the same id as the event ID of your Meta pixel's Lead
event: Meta then counts the browser event and ConnectWiz's server event once,
not twice.
For forms your page draws later (a pop-up, a single-page app), call
window.cwAttribution.fill() after the form appears.
window.cwAttribution.get() returns the current attribution block.
Consent
Switch on ConnectWiz cookie consent and the same code keeps the visitor's choice, tells your tags and records it — see Cookie consent. A choice made there wins over the signals below.
The script follows your cookie banner. It understands these signals, the
explicit ones first (an IAB TCF consent tool, __tcfapi, is read too — see
Your own consent tool instead):
window.cwConsent— set it before the script loads (window.cwConsent = { ads: true, analytics: true }), or callwindow.cwConsent.update({ ads: true })from your banner when the visitor decides.- Google Consent Mode — the
gtag('consent', 'default' | 'update', …)calls already in yourdataLayer. Ads consent meansad_storagegranted (andad_user_datanot denied).
No signal at all counts as no consent. What changes:
| Without marketing consent | With marketing consent | |
|---|---|---|
| Campaign tags, landing page, referrer | Kept for this browser tab only (session storage) | Kept in the first-party _cw_at cookie for 90 days |
Ad click ids (gclid, gbraid, wbraid, fbclid), Meta's _fbp / _fbc | Never read, kept or sent | Sent with the visit, the WhatsApp click and the form |
| Page address sent | Stripped of click ids | As it is |
| The visitor's IP address and browser (user agent) on the visit, the WhatsApp click and the form's source record | Not stored (a form submission keeps its own copy as the evidence of your form's consent box) | Stored, encrypted |
When the visitor accepts later, the session copy moves into the cookie with the
click ids still in the address. When they withdraw, the _cw_at cookie is
deleted and every id the script held is dropped. _cw_at is a marketing
cookie: list it in your cookie policy as one.
Ad measurement is a separate yes
Sharing a person's hashed phone number and e-mail with Meta and Google for
ad measurement needs its own, explicit consent — ad_measurement. Accepting
marketing cookies does not imply it, and Consent Mode has no such signal. Give
your form an optional checkbox (for example "Share my details for ad
measurement") and pass the answer on:
- in the browser:
window.cwConsent.update({ ad_measurement: true }), or - from your server, in the submission:
"consent": {"ad_measurement": true}.
What each answer allows is explained in Send conversions to Meta and Google Ads. Your privacy notice must cover sharing this data with Meta and Google.
WhatsApp ref codes
With Add a ref code to WhatsApp links on, a tap on a wa.me,
api.whatsapp.com or whatsapp:// link on your site is held for a moment
(under a second): the script reports the click, receives a short code and puts
a ready message into the link. WhatsApp then opens with, for example:
Hello, I would like some information (ref: K7Q2M)
- The code is 5 characters from letters and digits that are hard to confuse (no 0/O, no 1/I), unique in your workspace and valid for 30 days.
- If ConnectWiz does not answer in time, the visitor goes on to WhatsApp with the link as it was. A lost attribution is better than a button that does nothing.
- If the link already has its own text, the code is added to the end of it.
- When the visitor sends the message, ConnectWiz finds the code in the first message of the conversation, ties the conversation and the person to the click, and shows a Source chip on the conversation — see Follow up on a lead. The message itself is kept as the customer wrote it, but the AI assistant never sees the code, so it does not ask what it means.
- A code counts once. A second message, a retry or another conversation with the same code changes nothing.
Edit the message
In the WhatsApp section:
- WhatsApp channel the messages arrive on and WhatsApp number — note which of your numbers the site's links open. The code is recognised on any of your connected WhatsApp numbers (and on Instagram, Messenger, Telegram and the chat widget), because it is unique in your workspace.
- Message (language) — one text per language.
{ref}is replaced with the visitor's code; the field must contain it. The visitor's browser language picks the text; without a text for that language the first one you wrote is used, and without any, ConnectWiz's own sentence. Preview shows the result with a sample code.
Send your forms from your server
When your server receives a form, it forwards it to ConnectWiz with a server
key. ConnectWiz checks it exactly like a form filled in on ConnectWiz and runs
the same steps — the contact, tags, consent, notifications, flows and the
form.submitted webhook. The submission shows Website (server) as its
source.
Choose the form
Submissions go to the form picks the ConnectWiz form the snippets point at. The form must be published; a draft form refuses submissions. The panel warns you if the form does not create contacts — without a contact, nothing can be sent on to the ad platforms.
Create the ready-made lead form makes one in a click: Full name, Phone,
E-mail and Message, published, creating a contact for every submission, and
set as this site's form. Its field keys are name, phone, email and
message.
The request
POST https://api.connectwiz.net/api/intake/forms/{form}/submissions
Authorization: Bearer sk_…
Content-Type: application/json
{form} is the form's id; the panel's snippet already has it. The body:
| Field | Required | What it is |
|---|---|---|
values | Yes | The answers, keyed by the form's field keys: {"name": "Ada", "phone": "+905320000000"}. |
attribution | No | The cw_attribution field of the form, as it arrived (text or object). If you forward the whole form inside values, it is read from there too. |
event_id | No | The cw_event_id field — the Meta pixel deduplication key. |
client_ip | No | The visitor's IP address, not your server's. |
user_agent | No | The visitor's browser User-Agent. |
page_url | No | The page the form was on. |
idempotency_key | No | Your own id for this submission. |
consent | No | {"ad_measurement": true} when the visitor ticked your ad-measurement box. It is added to what the script already recorded. |
Send the visitor's real IP and User-Agent: Meta needs both to treat the lead as a website event.
Idempotency. Send the same idempotency_key again — a retry after a
timeout, a queue that runs twice — and you get the first submission back
("duplicate": true, HTTP 200), with no second contact, consent record,
conversion or webhook.
Examples
curl:
curl -X POST 'https://api.connectwiz.net/api/intake/forms/FORM_ID/submissions' \
-H "Authorization: Bearer $CONNECTWIZ_SERVER_KEY" \
-H 'Content-Type: application/json' \
-d '{
"values": {"name": "Ada", "phone": "+905320000000"},
"attribution": "<the cw_attribution field of the form>",
"event_id": "<the cw_event_id field of the form>",
"client_ip": "<the visitor IP>",
"user_agent": "<the visitor User-Agent>",
"idempotency_key": "<your own submission id>",
"consent": {"ad_measurement": false}
}'
PHP:
<?php
$ch = curl_init('https://api.connectwiz.net/api/intake/forms/FORM_ID/submissions');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CONNECTWIZ_SERVER_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'values' => ['name' => $_POST['name'] ?? '', 'phone' => $_POST['phone'] ?? ''],
'attribution' => $_POST['cw_attribution'] ?? null,
'event_id' => $_POST['cw_event_id'] ?? null,
'client_ip' => $_SERVER['REMOTE_ADDR'] ?? null,
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
'page_url' => $_SERVER['HTTP_REFERER'] ?? null,
'idempotency_key' => $submissionId,
// Your form's optional "share for ad measurement" checkbox, if it has one.
'consent' => ['ad_measurement' => !empty($_POST['ad_measurement'])],
]),
]);
$response = curl_exec($ch);
Send it from a background job if you can, so a slow answer never holds up your visitor's page.
Answers
| HTTP | Means |
|---|---|
201 | Received. The body carries submission_id, status, contact_id, attribution_touch_id and duplicate: false. |
200 | The same idempotency_key was sent before; the first submission is returned with duplicate: true. |
401 | missing_token or unknown_token — no key, a wrong key, or a browser key used where a server key belongs. |
403 | revoked_token, missing_scope:forms:submit, or site_disabled (the site's connection is off). |
404 | form_unavailable — the form does not exist or is not published. |
422 | The answers did not pass the form's own checks; errors names the fields. |
429 | Too many requests for this key (120 a minute). Retry later with the same idempotency_key. |
The full schema is in the API reference.
Hidden fields in your forms
The form builder has a Hidden value field type. The visitor never sees it.
- On a form hosted by ConnectWiz, it is filled from the page address: Read from the URL parameter (empty = the field key), or Value when the URL has none.
- From a connected website, your server sends it in
valueslike any other field.
Use it for things you want to keep with the submission — a product code, the branch the page belongs to. Where the visitor came from does not need a hidden field: it travels with the submission on its own.
Is anything arriving?
Is anything arriving? → Show the last touches lists the most recent visits, form touches and WhatsApp clicks from the site: when, the type, the source and campaign, which click ids came (by name, never their value), the ref code and whether ads consent was given.
To test, open a page of your site with a campaign link (for example
?utm_source=test), or tap a WhatsApp link, then press Refresh. A plain
visit without a campaign tag or a referring site is not recorded — that is by
design.