راهنمای API و وبهوک
پاسخهای فرمهایتان را به CRM، سیستم حسابداری یا هر برنامه دیگری وصل کنید.
دو راه اتصال
- وبهوک (همه پلنها): به محض ثبت هر پاسخ، فرمیت آن را به آدرس شما میفرستد.
- API (پلن حرفهای و سازمانی): برنامه شما هر وقت بخواهد فهرست فرمها و پاسخها را میخواند.
ساخت کلید API
از تنظیمات حساب ← کلیدهای API یک کلید بسازید. کلید فقط یک بار نمایش داده میشود و با fk_ شروع میشود. آن را مثل رمز عبور نگه دارید و هرگز در کد سمت مرورگر قرار ندهید. هر کلید به فرمهای خودتان و فرمهایی که با شما به اشتراک گذاشته شده دسترسی فقطخواندنی دارد.
کلید را در هدر Authorization بفرستید:
Authorization: Bearer fk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxسقف درخواست: ۱۲۰ درخواست در دقیقه برای هر کلید. پس از آن پاسخ 429 با هدر Retry-After برمیگردد.
فهرست فرمها
GET https://formit.ir/api/v1/forms{
"data": [
{ "id": "8f1c…", "title": "ثبتنام همایش", "slug": "k3m9x2ab",
"published": true, "responses": 214,
"createdAt": "2026-09-01T08:12:00.000Z", "updatedAt": "…" }
]
}پاسخهای یک فرم
GET https://formit.ir/api/v1/forms/{formId}/responses?limit=50&since=2026-09-01T00:00:00Z| پارامتر | توضیح |
|---|---|
limit | تعداد در هر صفحه، ۱ تا ۱۰۰ (پیشفرض ۵۰) |
cursor | مقدار nextCursor از پاسخ قبلی، برای صفحه بعد |
since | فقط پاسخهای ثبتشده از این زمان به بعد (ISO 8601) |
{
"form": {
"id": "8f1c…", "title": "ثبتنام همایش",
"fields": [ { "id": "name", "type": "text", "label": "نام و نام خانوادگی" }, … ]
},
"data": [
{
"id": "bd7e412a-…", "createdAt": "2026-09-14T10:03:11.000Z",
"status": "PAID", "score": null, "amount": 0, "paymentRefId": null,
"utmSource": "instagram", "utmMedium": null, "utmCampaign": null,
"answers": {
"name": "علی رضایی",
"resume": [ { "name": "cv.pdf", "size": 182044, "url": "https://…signed…" } ]
}
}
],
"nextCursor": "a91f…"
}کلیدهای answers شناسه سوالها هستند؛ عنوان هر سوال را از form.fields بخوانید. لینک فایلهای آپلودشده امضاشده است و پس از ۷ روز منقضی میشود؛ اگر فایل را لازم دارید، همان موقع دانلود و ذخیره کنید. پاسخها از جدید به قدیم مرتباند؛ تا وقتی nextCursor خالی نشده صفحه بعد را بخوانید.
نمونه با curl
curl -H "Authorization: Bearer $FORMIT_KEY" \
"https://formit.ir/api/v1/forms/FORM_ID/responses?limit=100"خطاها
401کلید ارسال نشده یا نامعتبر است.403پلن حساب، دسترسی API ندارد.404فرم وجود ندارد یا متعلق به شما نیست.429سقف درخواست پر شده است.
وبهوک
در فرمساز، بخش «تنظیمات ← اتصالها» آدرس https سرور خود را وارد کنید. با هر پاسخ جدید یک درخواست POST با بدنه JSON ارسال میشود. اگر سرور شما پاسخ 2xx ندهد، تا دو بار دیگر (پس از ۵ و ۳۰ ثانیه) تلاش میشود و نتیجه هر ارسال در همان بخش قابل مشاهده است.
رویدادها:
response.submittedپاسخ جدید ثبت شدpayment.succeededوpayment.failedنتیجه پرداخت داخل فرمwebhook.testبا دکمه «ارسال آزمایشی»
POST /your-endpoint
content-type: application/json; charset=utf-8
x-formit-event: response.submitted
x-formit-delivery: 5b0e…
x-formit-timestamp: 1789203791
x-formit-signature: sha256=3f9a…
{
"event": "response.submitted",
"deliveryId": "5b0e…",
"form": { "id": "8f1c…", "slug": "k3m9x2ab", "title": "ثبتنام همایش" },
"response": { "id": "bd7e…", "status": "PAID", "createdAt": "…", "answers": { … } }
}بررسی امضا
هر فرم یک کلید امضای اختصاصی دارد که در همان بخش اتصالها نمایش داده میشود. امضا برابر است با HMAC-SHA256 روی رشته {timestamp}.{body}. قبل از استفاده از داده، امضا را بررسی کنید و درخواستهای با زمان قدیمیتر از ۵ دقیقه را رد کنید. با x-formit-delivery هم از پردازش دوباره یک ارسال جلوگیری کنید.
// Node.js (Express): use the raw body, not the parsed JSON
const crypto = require("crypto");
function verify(rawBody, headers, secret) {
const ts = headers["x-formit-timestamp"];
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = "sha256=" + crypto.createHmac("sha256", secret)
.update(ts + "." + rawBody).digest("hex");
const got = String(headers["x-formit-signature"] || "");
return got.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
}<?php // PHP
$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_FORMIT_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_FORMIT_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, $secret);
if (abs(time() - (int)$ts) > 300 || !hash_equals($expected, $sig)) {
http_response_code(401); exit;
}سوالی دارید؟ با پشتیبانی در تماس باشید.