Roistat Integration
Roistat is cross-channel analytics with a focus on ad spend and ROI. It installs as a single tracker that captures clicks, calls, and leads, linking them to ad expenses from Google Ads, Yandex Direct, and other sources.
How the Tracker Works
The Roistat script writes a visit identifier — a 32-character string — to the roistat_visit cookie. For any target action (form, call, chat), this ID is passed along with the lead, allowing the system to identify the traffic source.
<!-- Basic installation — insert before </head> -->
<script>
(function(w, d, s, r, n) {
w[n] = w[n] || function() { (w[n].q = w[n].q || []).push(arguments) };
var f = d.getElementsByTagName(s)[0];
var j = d.createElement(s);
j.async = true;
j.src = 'https://cdn.roistat.com/px.js';
f.parentNode.insertBefore(j, f);
})(window, document, 'script', null, 'roistat');
roistat('init', { projectId: 'YOUR_PROJECT_ID' });
</script>
Via GTM, this is installed as a "Custom HTML" tag with a "All Pages" trigger.
Sending Visit Data on Form Submission
The form should pass roistat_visit to the backend. The most reliable way is a hidden field filled before submit:
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', () => {
let input = form.querySelector('[name="roistat_visit"]');
if (!input) {
input = document.createElement('input');
input.type = 'hidden';
input.name = 'roistat_visit';
form.appendChild(input);
}
input.value = getCookie('roistat_visit') ?? '';
});
});
function getCookie(name) {
const m = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
return m ? decodeURIComponent(m[1]) : null;
}
On the backend, save this value with the lead — it's then sent to the Roistat API as the visit field.
API for Lead Creation
Roistat accepts leads via REST API. Required fields: projectId, visit, and at least one contact field.
curl -X POST 'https://cloud.roistat.com/api/proxy/1.0/project/lead/create' \
-H 'Content-Type: application/json' \
-d '{
"projectId": "YOUR_PROJECT_ID",
"visit": "abc123def456...",
"fields": [
{ "name": "name", "value": "John Smith" },
{ "name": "phone", "value": "+79001234567" },
{ "name": "email", "value": "[email protected]" },
{ "name": "order_id", "value": "ORDER-789" }
]
}'
Success response:
{ "status": "ok", "lead_id": 123456 }
Integration via PHP (Laravel / Plain PHP)
// app/Services/RoistatService.php
class RoistatService
{
private string $projectId;
private string $apiUrl = 'https://cloud.roistat.com/api/proxy/1.0/project/lead/create';
public function __construct()
{
$this->projectId = config('services.roistat.project_id');
}
public function sendLead(array $fields, string $visitId): bool
{
$payload = [
'projectId' => $this->projectId,
'visit' => $visitId,
'fields' => array_map(
fn($name, $value) => ['name' => $name, 'value' => $value],
array_keys($fields),
array_values($fields)
),
];
$response = Http::timeout(5)->post($this->apiUrl, $payload);
return $response->ok() && $response->json('status') === 'ok';
}
}
In the controller:
public function store(FormRequest $request, RoistatService $roistat)
{
$lead = Lead::create($request->validated());
$roistat->sendLead([
'name' => $lead->name,
'phone' => $lead->phone,
'email' => $lead->email,
'order_id' => (string) $lead->id,
], $request->input('roistat_visit', ''));
return response()->json(['ok' => true]);
}
Call Tracking
Roistat provides built-in call tracking. After adding numbers in your account, the script automatically replaces phone numbers on the page. No additional JS needed — just ensure numbers are in text or in href="tel:..." attributes.
If a phone number is rendered by a JS framework (React, Vue), ensure the Roistat script initializes after components mount:
// React: call after mount
useEffect(() => {
if (window.roistat) {
window.roistat('replacePhones');
}
}, []);
Verification
- Open the site in a browser
- In DevTools → Application → Cookies, verify
roistat_visitis present and not empty - Submit a test form — in Roistat account → "Leads", a record with source should appear (not "direct")
- In console, check for no errors loading
px.js
Timeline
Basic tracker installation + visit transfer from forms — 2–4 hours. API lead creation integration on custom backend — 2–3 more hours. Testing and attribution verification across all channels — 1–2 hours.







