مستندات API هاب
همهی وبسایتهای ما فقط با یک اندپوینت واحد و یک ساختار استاندارد JSON صحبت میکنند. کافی است کلید بسازید و رویدادها را ارسال کنید؛ هاب بهطور خودکار سایت را میشناسد، ثبت میکند و تاریخچه نگه میدارد.
info معرفی
هاب آیفاند دادهی ترافیک، فرمهای تماس/سفارش و کلیک روی دکمههای تماس را از همهی سایتها دریافت میکند. هر سایتی که با کلید معتبر و ساختار استاندارد به این اندپوینت وصل شود، بهصورت خودکار بهعنوان وبسایت ما شناسایی، ثبت و پایش میشود.
site.domain است.api اندپوینت واحد
تمام درخواستها با متد POST و بدنهی JSON به این آدرس ارسال میشوند:
{ "site": {...}, "events": [ {...}, {...} ] } استفاده کنید.key احراز هویت
همهی درخواستها باید کلید API را در هدر X-Api-Key ارسال کنند. کلید را از بخش «کلیدهای API» بسازید؛ فقط یکبار نمایش داده میشود.
X-Api-Key: ifound_live_your_key_here # یا بهصورت Bearer: Authorization: Bearer ifound_live_your_key_here
content_paste_go اسکریپت آماده (کپیپیست)
اگر نمیخواهید از پرامپت ایجنت استفاده کنید یا با پیادهسازی سمت سرور راحت نیستید، همین قطعهکد کافی است. کلید API خود را جای YOUR_API_KEY بگذارید و کل بلوک را دقیقاً قبل از </body> در قالب سایت قرار دهید. همین یک اسکریپت بهطور خودکار این موارد را ارسال میکند:
data.fields میفرستد.a[href^="tel:"] با شماره و برچسب ثبت میشود.data-ifound-event="نام-رویداد" بگذارید تا کلیکِ آن ثبت شود.
برای فرم سفارش، ویژگی data-ifound-order را اضافه کنید تا رویداد بهجای form با نوع order ارسال شود.
با data-ifound-ignore روی یک فرم، آن فرم کاملاً نادیده گرفته میشود.
<!-- iFound Hub — drop-in tracker. Replace YOUR_API_KEY, then place before </body> -->
<script>
(function () {
"use strict";
var API_KEY = "YOUR_API_KEY";
var ENDPOINT = "http://hub.idads.ir/api/v1/ingest";
var DOMAIN = location.hostname;
function uid(p) {
return p + Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
}
function store(k, v) { try { localStorage.setItem(k, v); } catch (e) {} return v; }
function read(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }
var VID = read("ifound_vid") || uid("v");
store("ifound_vid", VID);
var SID = uid("s");
store("ifound_sid", SID);
function send(type, data, visitor) {
try {
var payload = {
type: type,
site: { domain: DOMAIN, name: document.title, platform: "custom" },
visitor: {
id: VID,
session_id: SID,
path: location.pathname + location.search,
title: document.title,
referrer: document.referrer || "",
user_agent: navigator.userAgent,
language: navigator.language || ""
},
data: data || {},
occurred_at: new Date().toISOString()
};
if (visitor) { for (var k in visitor) { payload.visitor[k] = visitor[k]; } }
fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json", "X-Api-Key": API_KEY },
body: JSON.stringify(payload),
keepalive: true,
mode: "cors"
}).catch(function () {});
} catch (e) {}
}
if (!API_KEY || API_KEY === "YOUR_API_KEY") {
/* Remove this guard once the key is set; it prevents junk from reaching the hub. */
return;
}
/* 1. Page views (including SPA route changes) */
var lastPath = "";
function pageview() {
var p = location.pathname + location.search;
if (p === lastPath) return;
lastPath = p;
send("pageview");
}
pageview();
["pushState", "replaceState"].forEach(function (m) {
var orig = history[m];
history[m] = function () {
var r = orig.apply(this, arguments);
try { setTimeout(pageview, 0); } catch (e) {}
return r;
};
});
window.addEventListener("popstate", function () { setTimeout(pageview, 0); });
/* 2. Forms — contact + order */
document.addEventListener("submit", function (ev) {
try {
var form = ev.target;
if (!form || form.tagName !== "FORM") return;
if (form.hasAttribute("data-ifound-ignore")) return;
var fields = {};
var fd = new FormData(form);
fd.forEach(function (v, k) {
if (v instanceof File) { fields[k] = v.name; }
else if (fields[k] === undefined) { fields[k] = String(v).slice(0, 500); }
else { fields[k] = fields[k] + ", " + String(v).slice(0, 500); }
});
var isOrder = form.hasAttribute("data-ifound-order") ||
/order|checkout|cart|سفارش|خرید/i.test(form.id + " " + (form.className || "") + " " +
(form.getAttribute("name") || "") + " " + (form.getAttribute("action") || ""));
send(isOrder ? "order" : "form", {
form_name: form.getAttribute("data-ifound-name") ||
form.getAttribute("name") || form.id ||
(isOrder ? "فرم سفارش" : "فرم تماس"),
fields: fields,
page_path: location.pathname
});
} catch (e) {}
}, true);
/* 3. Call buttons */
document.addEventListener("click", function (ev) {
try {
var el = ev.target && ev.target.closest ? ev.target.closest('a[href^="tel:"]') : null;
if (el) {
send("call", {
button_label: (el.textContent || "").trim().slice(0, 120) || "تماس",
phone: (el.getAttribute("href") || "").replace(/^tel:/i, ""),
page_path: location.pathname
});
return;
}
var custom = ev.target && ev.target.closest ? ev.target.closest("[data-ifound-event]") : null;
if (custom) {
send("event", {
event_name: custom.getAttribute("data-ifound-event"),
label: custom.getAttribute("data-ifound-label") || "",
page_path: location.pathname
});
}
} catch (e) {}
}, true);
/* 4. Reliability: flush on the way out + a heartbeat */
function beacon(payload) {
try {
var body = JSON.stringify(payload);
if (!navigator.sendBeacon) return;
/* sendBeacon cannot set headers AND cannot perform a CORS preflight, so:
- the key travels as ?api_key= (the endpoint accepts it), and
- the Blob must use text/plain, which is CORS-safelisted. A
application/json Blob would require a preflight that a beacon cannot
send, and the request would be dropped by the browser.
The server still parses the body as JSON. */
var blob = new Blob([body], { type: "text/plain;charset=UTF-8" });
navigator.sendBeacon(ENDPOINT + "?api_key=" + encodeURIComponent(API_KEY), blob);
} catch (e) {}
}
document.addEventListener("visibilitychange", function () {
if (document.visibilityState === "hidden") {
beacon({ type: "ping", site: { domain: DOMAIN },
visitor: { id: VID, session_id: SID, path: location.pathname,
title: document.title, referrer: document.referrer || "",
user_agent: navigator.userAgent, language: navigator.language || "" } });
}
});
document.addEventListener("click", function (ev) {
try {
var el = ev.target;
if (!el || !el.closest) return;
var tel = el.closest('a[href^="tel:"]');
if (tel) {
beacon({ type: "call", site: { domain: DOMAIN }, visitor: { id: VID, session_id: SID },
data: { button_label: (tel.textContent || "").trim().slice(0, 120) || "تماس",
phone: (tel.getAttribute("href") || "").replace(/^tel:/i, "") } });
}
} catch (e) {}
}, true);
})();
</script>
| فیلد | مقدار | توضیح |
|---|---|---|
YOUR_API_KEY | کلید API شما | فقط این یک مقدار را عوض کنید |
site.domain | خودکار از location.hostname | مبنای ثبت خودکار وبسایت در هاب |
visitor.id | خودکار در localStorage | شناسهی ناشناس و پایدار بازدیدکننده |
visitor.session_id | خودکار در هر نشست | برای گروهبندی رویدادهای یک بازدید |
occurred_at | زمان ISO 8601 | زمان دقیق وقوع رویداد |
fetch و در صورت پشتیبانی مرورگر با navigator.sendBeacon ارسال میکند و همهی خطاها را بیصدا مدیریت میکند؛ بنابراین هرگز تجربهی کاربر سایت شما را مختل نمیکند.
توجه داشته باشید در حالت sendBeacon امکان تنظیم هدر وجود ندارد، پس کلید بهصورت ?api_key= در آدرس فرستاده میشود.
data_object ساختار داده
همهی رویدادها یک ساختار مشترک دارند. تنها فیلد اجباری site.domain است؛ سایر فیلدها اختیاریاند اما ارسال آنها کیفیت تحلیل را بالا میبرد.
| فیلد | نوع | اجباری | توضیح |
|---|---|---|---|
type | string | بله | نوع رویداد (جدول پایین) |
site.domain | string | بله | دامنهی سایت؛ مبنای شناسایی و ثبت خودکار |
site.name | string | خیر | نام نمایشی سایت |
site.platform | string | خیر | پلتفرم (wordpress، laravel، …) |
visitor.path | string | خیر | مسیر صفحه |
visitor.title | string | خیر | عنوان صفحه |
visitor.referrer | string | خیر | صفحهی ارجاعدهنده |
visitor.id | string | خیر | شناسهی ناشناس بازدیدکننده |
visitor.session_id | string | خیر | شناسهی نشست |
visitor.ip | string | خیر | در صورت نبود، از اتصال گرفته میشود |
visitor.country / city | string | خیر | موقعیت جغرافیایی |
data | object | خیر | دادهی مخصوص هر نوع رویداد |
occurred_at | string | خیر | زمان وقوع (ISO 8601) |
category انواع رویداد
| type | کاربرد | فیلدهای مهم در data |
|---|---|---|
pageview | بازدید صفحه (ترافیک) | — |
form | ارسال فرم تماس | form_name، fields{} |
order | ثبت سفارش | order_id، amount، fields{} |
call | کلیک روی دکمهی تماس | button_label، phone |
ping | ضربان/سلامت سایت | — |
event | رویداد دلخواه | event_name، label |
contact، lead→form
call_click→call
visit→pageview
heartbeat→ping
code نمونهکدها
ارسال داده در زبانها و پلتفرمهای مختلف. مقدار کلید را با کلید واقعی خود جایگزین کنید.
curl -X POST 'http://hub.idads.ir/api/v1/ingest' \
-H 'X-Api-Key: ifound_live_your_key_here' \
-H 'Content-Type: application/json' \
-d '{
"type": "pageview",
"site": { "domain": "my-shop.ir", "name": "فروشگاه من" },
"visitor": { "path": "/services", "referrer": "https://google.com" }
}'
check_circle پاسخ سرور
در صورت موفقیت، کد وضعیت 200 و بدنهی زیر برگردانده میشود:
{
"ok": true,
"data": {
"received": true,
"count": 1,
"event_id": 128,
"event_type": "pageview",
"website": { "id": 3, "domain": "my-shop.ir" },
"server_time": "۱۴۰۳/۰۳/۱۲ ۱۰:۲۲",
"timestamp": 1718000000
}
}
error کدهای وضعیت و خطا
| کد | معنا | اقدام پیشنهادی |
|---|---|---|
200 | دریافت موفق | — |
401 | کلید API نامعتبر یا غیرفعال | کلید را بررسی یا کلید جدید بسازید |
405 | متد درخواست غیرمجاز | فقط از متد POST استفاده کنید |
422 | دادهی نامعتبر | وجود site.domain را بررسی کنید |
500 | خطای داخلی سرور | چند لحظه بعد دوباره تلاش کنید |
try/catch قرار دهید تا در صورت قطع ارتباط با هاب، تجربهی کاربر سایت شما دچار اختلال نشود.smart_toy پرامپت آماده برای ایجنت سایت
این متن را کپی کنید و به ایجنت/توسعهدهندهی هر وبسایت بدهید تا اتصال به هاب را بهصورت کامل پیادهسازی کند.
تو یک مهندس فرانتاند/بکاند هستی. میخواهم این وبسایت به «هاب دادهی آیفاند» وصل شود.
هدف: ارسال ترافیک (بازدید صفحه)، فرمهای تماس/سفارش و کلیک روی دکمههای تماس به یک اندپوینت واحد.
اطلاعات اتصال:
- Endpoint: POST http://hub.idads.ir/api/v1/ingest
- هدر احراز هویت: X-Api-Key: <API_KEY>
- Content-Type: application/json
ساختار استاندارد بدنه (JSON):
{
"type": "pageview | form | order | call | ping | event",
"site": { "domain": "my-site.ir", "name": "نام سایت", "platform": "wordpress" },
"visitor": { "id": "...", "session_id": "...", "path": "/", "title": "...",
"referrer": "...", "user_agent": "...", "language": "fa-IR" },
"data": { ...فیلدهای مخصوص هر نوع رویداد... },
"occurred_at": "2026-01-01T12:00:00+03:30"
}
وظایف پیادهسازی:
۱. یک ماژول به نام ifound.js بساز شامل تابع ifoundSend(payload) که با fetch داده را به اندپوینت ارسال میکند، خطاها را بیصدا مدیریت میکند و هرگز تجربهی کاربر را مختل نمیکند.
۲. یک شناسهی بازدیدکنندهی ناشناس در localStorage با کلید ifound_vid بساز و در هر ارسال بفرست.
۳. در بارگذاری هر صفحه، رویداد type="pageview" را با path، title و referrer ارسال کن.
۴. همهی فرمهای تماس/سفارش را رهگیری کن؛ هنگام submit رویداد type="form" یا "order" با فیلدهای فرم در data.fields بفرست.
۵. هر دکمهی تماس (a[href^="tel:"]) را رهگیری کن؛ هنگام کلیک رویداد type="call" با شماره و برچسب بفرست.
۶. برای اطمینان از ارسال، از navigator.sendBeacon در رویداد visibilitychange/beforeunload استفاده کن.
۷. مقدار API_KEY را از تنظیمات سرور یا متغیر محیطی بخوان؛ آن را در کد سمت کلاینت hardcode نکن (در صورت نیاز از یک پروکسی سمت سرور عبور بده).
۸. هیچ خطایی نباید به کاربر نمایش داده شود؛ ارسال داده باید کاملاً غیرمسدودکننده باشد.
نمونه بدنهی «بازدید صفحه»:
{ "type": "pageview", "site": { "domain": "my-site.ir" },
"visitor": { "path": "/services/web-design", "title": "طراحی سایت", "referrer": "https://google.com" } }
نمونه بدنهی «فرم تماس»:
{ "type": "form", "site": { "domain": "my-site.ir" },
"visitor": { "path": "/contact" },
"data": { "form_name": "فرم تماس", "fields": { "نام": "علی", "موبایل": "09120000000", "پیام": "سلام" } } }
نمونه بدنهی «دکمهی تماس»:
{ "type": "call", "site": { "domain": "my-site.ir" },
"visitor": { "path": "/" },
"data": { "button_label": "تماس فوری", "phone": "09120000000" } }
پس از پیادهسازی، نمونهکد نهایی را همراه با محل قرارگیری آن در پروژه گزارش بده.