آی‌فاند هابمستندات API
صفحه‌ی اصلی login ورود
terminal شروع سریع

مستندات API هاب

همه‌ی وب‌سایت‌های ما فقط با یک اندپوینت واحد و یک ساختار استاندارد JSON صحبت می‌کنند. کافی است کلید بسازید و رویدادها را ارسال کنید؛ هاب به‌طور خودکار سایت را می‌شناسد، ثبت می‌کند و تاریخچه نگه می‌دارد.

POST http://hub.idads.ir/api/v1/ingest

info معرفی

هاب آی‌فاند داده‌ی ترافیک، فرم‌های تماس/سفارش و کلیک روی دکمه‌های تماس را از همه‌ی سایت‌ها دریافت می‌کند. هر سایتی که با کلید معتبر و ساختار استاندارد به این اندپوینت وصل شود، به‌صورت خودکار به‌عنوان وب‌سایت ما شناسایی، ثبت و پایش می‌شود.

یک اندپوینت
همه‌ی انواع داده با یک URL ارسال می‌شوند.
یک ساختار
مشترک بین همه‌ی پروژه‌ها و زبان‌ها.
ثبت خودکار سایت
مبنای شناسایی، فیلد site.domain است.

api اندپوینت واحد

تمام درخواست‌ها با متد POST و بدنه‌ی JSON به این آدرس ارسال می‌شوند:

POST /api/v1/ingest
lightbulb
برای ارسال گروهی چند رویداد در یک درخواست، به‌جای بدنه‌ی تک‌رویداد از { "site": {...}, "events": [ {...}, {...} ] } استفاده کنید.

key احراز هویت

همه‌ی درخواست‌ها باید کلید API را در هدر X-Api-Key ارسال کنند. کلید را از بخش «کلیدهای API» بسازید؛ فقط یک‌بار نمایش داده می‌شود.

HTTP Header
X-Api-Key: ifound_live_your_key_here

# یا به‌صورت Bearer:
Authorization: Bearer ifound_live_your_key_here

content_paste_go اسکریپت آماده (کپی‌پیست)

اگر نمی‌خواهید از پرامپت ایجنت استفاده کنید یا با پیاده‌سازی سمت سرور راحت نیستید، همین قطعه‌کد کافی است. کلید API خود را جای YOUR_API_KEY بگذارید و کل بلوک را دقیقاً قبل از </body> در قالب سایت قرار دهید. همین یک اسکریپت به‌طور خودکار این موارد را ارسال می‌کند:

بازدید صفحه
با هر بارگذاری و در تغییر مسیرهای SPA (pushState / popstate).
فرم‌ها و سفارش‌ها
همه‌ی فیلدهای فرم تماس و سفارش را در data.fields می‌فرستد.
دکمه‌های تماس
هر کلیک روی a[href^="tel:"] با شماره و برچسب ثبت می‌شود.
lightbulb
رویداد دلخواه هم پشتیبانی می‌شود: روی هر عنصر data-ifound-event="نام-رویداد" بگذارید تا کلیکِ آن ثبت شود. برای فرم سفارش، ویژگی data-ifound-order را اضافه کنید تا رویداد به‌جای form با نوع order ارسال شود. با data-ifound-ignore روی یک فرم، آن فرم کاملاً نادیده گرفته می‌شود.
DROP-IN SCRIPT
<!-- 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زمان دقیق وقوع رویداد
warning
این اسکریپت با fetch و در صورت پشتیبانی مرورگر با navigator.sendBeacon ارسال می‌کند و همه‌ی خطاها را بی‌صدا مدیریت می‌کند؛ بنابراین هرگز تجربه‌ی کاربر سایت شما را مختل نمی‌کند. توجه داشته باشید در حالت sendBeacon امکان تنظیم هدر وجود ندارد، پس کلید به‌صورت ?api_key= در آدرس فرستاده می‌شود.

data_object ساختار داده

همه‌ی رویدادها یک ساختار مشترک دارند. تنها فیلد اجباری site.domain است؛ سایر فیلدها اختیاری‌اند اما ارسال آن‌ها کیفیت تحلیل را بالا می‌برد.

فیلدنوعاجباریتوضیح
typestringبلهنوع رویداد (جدول پایین)
site.domainstringبلهدامنه‌ی سایت؛ مبنای شناسایی و ثبت خودکار
site.namestringخیرنام نمایشی سایت
site.platformstringخیرپلتفرم (wordpress، laravel، …)
visitor.pathstringخیرمسیر صفحه
visitor.titlestringخیرعنوان صفحه
visitor.referrerstringخیرصفحه‌ی ارجاع‌دهنده
visitor.idstringخیرشناسه‌ی ناشناس بازدیدکننده
visitor.session_idstringخیرشناسه‌ی نشست
visitor.ipstringخیردر صورت نبود، از اتصال گرفته می‌شود
visitor.country / citystringخیرموقعیت جغرافیایی
dataobjectخیرداده‌ی مخصوص هر نوع رویداد
occurred_atstringخیرزمان وقوع (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
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 و بدنه‌ی زیر برگردانده می‌شود:

Response 200 · JSON
{
  "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خطای داخلی سرورچند لحظه بعد دوباره تلاش کنید
warning
همیشه ارسال داده را در try/catch قرار دهید تا در صورت قطع ارتباط با هاب، تجربه‌ی کاربر سایت شما دچار اختلال نشود.

smart_toy پرامپت آماده برای ایجنت سایت

این متن را کپی کنید و به ایجنت/توسعه‌دهنده‌ی هر وب‌سایت بدهید تا اتصال به هاب را به‌صورت کامل پیاده‌سازی کند.

terminal پرامپت پیاده‌سازی اتصال
تو یک مهندس فرانت‌اند/بک‌اند هستی. می‌خواهم این وب‌سایت به «هاب داده‌ی آی‌فاند» وصل شود.

هدف: ارسال ترافیک (بازدید صفحه)، فرم‌های تماس/سفارش و کلیک روی دکمه‌های تماس به یک اندپوینت واحد.

اطلاعات اتصال:
- 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" } }

پس از پیاده‌سازی، نمونه‌کد نهایی را همراه با محل قرارگیری آن در پروژه گزارش بده.