پرش به محتوای اصلی

مستندات توسعه‌دهنده چرتکه

اتصال نرم‌افزارها، سامانه حسابداری و CRM به داده‌های استخراج‌شده‌ی چرتکه

آشنایی با دو قابلیت اصلی

چرتکه فاکتورها و پرونده‌های خسارت را به داده ساختاریافته تبدیل می‌کند. بخش «تنظیمات توسعه‌دهنده» در /app/settings دو راه برای اتصال داده‌های سازمان شما به سایر سیستم‌ها فراهم می‌کند:

۱. توکن API (بیرون‌کشیدن داده)

نرم‌افزار شما در هر زمان می‌تواند لیست اسناد و آمار مصرف را با یک درخواست HTTP دریافت کند؛ مناسب داشبوردها، گزارش‌های دوره‌ای و همگام‌سازی.

۲. وبهوک (جای‌گزاری رویداد)

به محض ذخیره یک فاکتور جدید، چرتکه بلافاصله آن را به آدرس شما POST می‌کند؛ مناسب ثبت خودکار در نرم‌افزار حسابداری و CRM.

دسترسی به ساخت توکن و وبهوک فقط برای مدیر و مالک سازمان در دسترس است. سایر اعضا فقط «کارمند» یا «عضو» هستند و نمی‌توانند کلید بسازند.

اتصال به نرم‌افزارهای حسابداری و CRM ایرانی

چرتکه با ترکیب «توکن API + وبهوک» می‌تواند داده فاکتور را مستقیم به نرم‌افزار حسابداری یا CRM سازمان شما منتقل کند. آدرس پایه، روش احراز هویت و بدنه درخواست ثبت فاکتور در هر نرم‌افزار متفاوت است؛ از فهرست زیر راهنمای نرم‌افزار خودتان را دنبال کنید:

نرم‌افزارهای دسکتاپ (مانند برخی نسخه‌های هلو یا سپیدار) به نسخه دارای «سرویس وب / API» یا نصب روی سرور نیاز دارند؛ در صورت نبود API، از مسیر GET /api/accounting/export خروجی Excel/CSV بگیرید یا از GET /api/v1/documents استفاده کنید.
نمونه‌کدهای زیر را قبل از استفاده بخوانید. این‌ها اسکریپت گیرنده هستند که خودتان روی سرورتان می‌نویسید، و فیلدهایی مثل buyerErpCode یا customerRef که در آن‌ها می‌بینید، کدهای داخلی نرم‌افزار مقصد هستند — نه فیلدی که چرتکه می‌فرستد. چرتکه contactId خودش را می‌فرستد و تبدیل آن به کد طرف‌حساب در هلو یا سپیدار، وظیفهٔ شماست: یا با GET /api/v1/accounting/contacts جدول نگاشت بسازید و کنارش نگه دارید، یا در چرتکه برای هر طرف‌حساب یک کد یکتا ثبت کنید و همان را مبنای تطبیق بگذارید. بدون این نگاشت، اسکریپت شما هر بار یک طرف‌حساب تکراری در نرم‌افزار مقصد می‌سازد.

هلو (Holoo) — وب‌سرویس REST روی شبکه داخلی

هلو یک وب‌سرویس REST را روی همان سرور نصب نرم‌افزار (پیش‌فرض پورت 8080) فعال می‌کند. ابتدا با Login توکن Bearer می‌گیرید و بقیه متدها را با آن صدا می‌زنید:

متدEndpointتوضیح
POST{Address}/Loginاحراز هویت؛ نتیجه Login.State و Login.Token (Bearer).
POST{Address}/Invoice/Invoiceثبت فاکتور؛ Type: 1 فروش، 2 برگشت فروش، 3 خرید، 4 برگشت خرید.
POST{Address}/Invoice/PreInvoiceثبت پیش‌فاکتور (نوع‌ها مثل Invoice).
POST{Address}/Invoice/Orderثبت سفارش (نوع‌ها مثل Invoice).
GET{Address}/Invoiceدریافت فاکتورها با فیلتر.
POST{Address}/Customerثبت مشتری.
GET{Address}/Customerدریافت مشتری‌ها.
POST{Address}/Productثبت کالا/خدمت.
GET{Address}/Productدریافت کالاها.
POST{Address}/webhook/subscribeوبهوک هلو برای گزارش رویدادها (tables: customer / product / mainGroup / sideGroup / resInvoice).
js۱) لاگین هلو و مبانی متدها
# آدرس پایه هلو: http://[IP]:8080/TncHoloo/api  (نام دیتابیس هلو را از پیکربندی بگیرید)

# ۱) لاگین — رمز عبور را Base64 کنید
curl -X POST "http://[IP-Server-Hello]:8080/TncHoloo/api/Login" \
  -H "Content-Type: application/json" \
  -d '{"userinfo":{"username":"admin","userpass":"<Base64 رمز>","dbname":"Holoo1"}}'

# پاسخ موفق:
# {"Login":{"State":"True","Token":"Bearer <token>","ErrorCode":0,"Error":""}}

# ۲) برای همه متدها لازم است:
#    Authorization: Bearer <token>
#    Content-Type: application/json
js۲) ثبت فاکتور در هلو از وبهوک چرتکه
// وبهوک چرتکه ← درج فاکتور در هلو (Node.js / Next.js)
export async function POST(req) {
  // ۱) تأیید امضای X-Chortke-Signature — کد بخش «وبهوک» بالا
  const payload = await req.json();
  if (payload.event !== "document.created") return Response.json({ ok: true });
  const d = payload.data;

  const holo = "http://[IP-Server-Hello]:8080/TncHoloo/api";
  const token = "<Token-از-لاگین>"; // با لاگین گرفته و کش کنید

  const body = {
    invoiceinfo: [{
      Type: "3", // 1=فروش  2=برگشت از فروش  3=خرید  4=برگشت از خرید
      customererpcode: d.buyerErpCode, // ErpCode طرف‌حساب ثبت‌شده در هلو
      date: "2026-09-16",              // میلادی YYYY-MM-DD (تاریخ شمسی را تبدیل کنید)
      time: "10:30",
      Cash: Number(d.finalAmount),
      Discount: 0,
      detailinfo: {
        ProductErpCode: d.itemErpCode, // ErpCode کالای ثبت‌شده در هلو
        few: Number(d.quantity ?? 1),
        price: Number(d.finalAmount),
        levy: 0,
        scot: 0,
      },
    }],
  };

  await fetch(`${holo}/Invoice/Invoice`, {
    method: "POST",
    headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
    body: JSON.stringify(body),
  });
  return Response.json({ ok: true });
}
bash۳) وبهوک خروجی هلو (اختیاری)
POST http://[IP]:8080/TncHoloo/api/webhook/subscribe
Content-Type: application/json

{"subscriptions":[{"url":"https://server.com/hook","tablename":"product","apikey":"<کلید ثابت>"}]}

# tablename مجاز: customer | product | mainGroup | sideGroup | resInvoice
# اعلان دریافتی: {"Dbname":"Holoo1","Table":"product","operation":"create|update|delete","changedfields":"[...]"}
مستند کامل وب‌سرویس هلو را از سایت هلو دریافت کنید.

سپیدار (Sepidar) — وب‌سرویس محلی

سپیدار سیستم یک وب‌سرویس محلی (پیش‌فرض http://localhost:7373) فراهم می‌کند. پس از ثبت دستگاه (کلید RSA) و لاگین (JWT)، فاکتورها را پست می‌کنید:

متدEndpointتوضیح
POST/api/Devices/Registerثبت یک‌بار دستگاه؛ پاسخ شامل کلید عمومی (XML/RSA) برای رمزنگاری مقادیر.
POST/api/users/loginلاگین؛ Credentials: UserName + PasswordHash (MD5).
POST/api/invoices/ثبت فاکتور فروش با GUID و Items.
GET/api/invoices/:idدریافت جزئیات فاکتور.
bash۱) ثبت دستگاه و لاگین سپیدار
# آدرس پایه سپیدار: http://localhost:7373 (از تنظیمات «حالت سرویس‌دهنده» قابل تغییر است)

# ۱) ثبت دستگاه (یک‌بار) — پاسخ شامل کلید عمومی (قالب XML / RSA) برای رمزنگاری است
curl -X POST "http://[IP-Sepidar]:7373/api/Devices/Register" \
  -H "Content-Type: application/json" \
  -d '{"DeviceName":"Chortke"}'

# ۲) لاگین
#    برای هر درخواست یک GUID جدید به‌عنوان ArbitraryCode بسازید:
#    EncArbitraryCode = Base64( RSA-PKCS1-v1_5(ArbitraryCode, کلید عمومی) )
curl -X POST "http://[IP-Sepidar]:7373/api/users/login" \
  -H "Authorization: Bearer <JWT>" \
  -H "GenerationVersion: 1" \
  -H "IntegrationID: <شناسه-دستگاه-Registered>" \
  -H "ArbitraryCode: <GUID-جدید>" \
  -H "EncArbitraryCode: <Base64-RSA>" \
  -H "Content-Type: application/json" \
  -d '{"UserName":"<نام کاربری>","PasswordHash":"<MD5-رمز>"}'

# ۳) برای همه متدها: Authorization: Bearer JWT + GenerationVersion + IntegrationID + ArbitraryCode + EncArbitraryCode
js۲) ثبت فاکتور در سپیدار از وبهوک چرتکه
// وبهوک چرتکه ← ثبت فاکتور در سپیدار (Node.js / Next.js)
// توابع Register/Login و encryptRSA را طبق مستندات رسمی سپیدار پیاده کنید.
export async function POST(req) {
  // ۱) تأیید امضای X-Chortke-Signature — کد بخش «وبهوک» بالا
  const payload = await req.json();
  if (payload.event !== "document.created") return Response.json({ ok: true });
  const d = payload.data;

  const headers = {
    "Content-Type": "application/json",
    Authorization: "Bearer <JWT-از-لاگین>",
    GenerationVersion: "1",
    IntegrationID: "<IntegrationID-از-Register>",
    ArbitraryCode: crypto.randomUUID(),
    EncArbitraryCode: encryptRSA(crypto.randomUUID()), // RSA PKCS#1 v1.5 + Base64
  };

  const body = {
    GUID: crypto.randomUUID(),     // جلوگیری از ثبت تکراری (Idempotency)
    CustomerRef: d.customerRef,    // شناسه مشتری ثبت‌شده در سپیدار
    SaleTypeRef: d.saleTypeRef ?? 1,
    DiscountOnCustomer: 0,
    Price: Number(d.finalAmount),  // مبلغ ناخالص
    Discount: 0,
    Tax: 0,
    Duty: 0,
    Addition: 0,
    Items: [{
      ItemRef: d.itemRef,          // شناسه کالا در سپیدار
      StockRef: 0,
      Quantity: Number(d.quantity ?? 1),
      SecondaryQuantity: 0,
      Fee: Number(d.finalAmount),
      Description: d.invoiceNumber,
    }],
  };

  await fetch("http://[IP-Sepidar]:7373/api/invoices/", {
    method: "POST",
    headers,
    body: JSON.stringify(body),
  });
  return Response.json({ ok: true });
}
لاگین و همه متدها به هدرهای GenerationVersion، IntegrationID،ArbitraryCode و EncArbitraryCode نیاز دارند وArbitraryCode باید برای هر درخواست جدید ساخته شود. بدون لایسنس ماژول وب‌سرویس، سرور پاسخ نمی‌دهد.

دیدار (Didar) — CRM ابری

دیدار یک CRM ابری با API عمومی است؛ کلید API را از تنظیمات توسعه‌دهنده دیدار بگیرید و در کوئری بفرستید:

متدEndpointتوضیح
POST/api/AccountingInvoice/Createایجاد فاکتور حسابداری؛ نیازمند SellerInfo.BizDomainId و Items.
POST/api/AccountingInvoice/Listفهرست فاکتورهای حسابداری.
POST/api/AccountingInvoice/Deleteحذف فاکتور.
GET/api/Product/GetProductsدریافت کالاها برای یافتن ProductId.
GET/api/Product/GetProductsByCodeدریافت کالا با کد.
jsایجاد فاکتور در دیدار از وبهوک چرتکه
// وبهوک چرتکه ← ایجاد فاکتور در دیدار (Node.js / Next.js)
const API_KEY = "<کلید-API-دیدار>";

export async function POST(req) {
  // تأیید امضا — کد بخش «وبهوک» بالا
  const payload = await req.json();
  if (payload.event !== "document.created") return Response.json({ ok: true });
  const d = payload.data;

  const body = {
    Items: [{
      ProductId: d.productId,           // از Search Products / Get Products By Code
      Title: d.title ?? "فاکتور چرتکه",
      Quantity: Number(d.quantity ?? 1),
      Unit: d.unit ?? "عدد",
      UnitPrice: Number(d.finalAmount),
      Discount: 0,
      DiscountPercent: 0,
      DiscountType: "Amount",
      TaxPercent: d.taxPercent ?? 0,
    }],
    SellerInfo: {
      SellerName: "<نام فروشنده>",
      BizDomainId: "<BizDomainId فروشنده>", // الزامی
    },
    BuyerInfo: {
      BuyerType: "Company",
      BuyerName: d.buyer,
      BuyerEconomicCode: d.buyerEconomicCode ?? "",
    },
    IssueDate: "2026-09-16T10:30:00.000Z",
  };

  await fetch(`https://app.didar.me/api/AccountingInvoice/Create?apikey=${API_KEY}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  return Response.json({ ok: true });
}
مستندات توسعه‌دهندهٔ دیدار را از سایت دیدار دریافت کنید.

حسابفا (Hesabfa) — حسابداری ابری

حسابفا یک حسابداری ابری با وب‌سرویس عمومی (همه متدها POST) است. اعتبارها (apiKey + loginToken یا userId+password) در بدنه درخواست ارسال می‌شوند و پاسخ همیشه در قالب{ Success, ErrorCode, ErrorMessage, Result } است:

متدEndpointتوضیح
POST/v1/accounting/GetSettingتنظیمات حساب (از جمله TokenKey / LoginToken).
POST/v1/invoice/saveثبت/ویرایش فاکتور.
POST/v1/invoice/getدریافت فاکتور با id.
POST/v1/Setting/Hookهوک تغییرات (Hook) برای دریافت رویدادها.
jsثبت فاکتور در حسابفا از وبهوک چرتکه
// وبهوک چرتکه ← ثبت فاکتور در حسابفا (Node.js / Next.js)
export async function POST(req) {
  // تأیید امضا — کد بخش «وبهوک» بالا
  const payload = await req.json();
  if (payload.event !== "document.created") return Response.json({ ok: true });
  const d = payload.data;

  const res = await fetch("https://api.hesabfa.com/v1/invoice/save", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      apiKey: "<apiKey>",        // تنظیمات حسابفا (بخش API)
      loginToken: "<loginToken>", // یا userId + password
      invoice: {
        number: d.invoiceNumber ?? "",
        date: "2026-09-16 10:30:00",      // میلادی
        dueDate: "2026-09-16 10:30:00",
        contactCode: d.buyerContactCode,  // کد شخص در حسابفا
        contactTitle: d.buyer,
        invoiceType: 1,  // نوع فاکتور از جدول TypesTable (۰=فروش، ۱=خرید، …)
        status: 2,
        invoiceItems: [{
          itemCode: d.itemCode,
          description: d.title ?? "فاکتور چرتکه",
          unit: "عدد",
          quantity: Number(d.quantity ?? 1),
          unitPrice: Number(d.finalAmount),
          discount: 0,
          tax: 0,
        }],
      },
    }),
  });

  const data = await res.json();
  if (!data.Success) console.error(data.ErrorCode, data.ErrorMessage);
  return Response.json({ ok: true });
}
مستندات API حسابفا را از سایت حسابفا دریافت کنید.

سایر نرم‌افزارهای حسابداری

برخی نرم‌افزارهای دیگر نیز API / وب‌سرویس عمومی دارند (یا مستندات آن را باز کرده‌اند)؛ اما برای بیشتر آن‌ها اتصال نیازمند هماهنگی با فروشنده نرم‌افزار است. به‌روزترین وضعیت را همیشه از مستندات رسمی هر نرم‌افزار بررسی کنید:

نرم‌افزارنوعمسیر اتصال
محکحسابداری ابریاز سایت محک دریافت کنید.
تدبیرERP دسکتاپاز سایت تدبیر دریافت کنید.
پیوست، رافع، دشت، اوراش، پارمیس، پارسیانمتغیربا فروشنده نرم‌افزار هماهنگ کنید.
برای نرم‌افزارهایی که API عمومی ندارند، GET /api/v1/documents را با خروجی Excel/CSV ترکیب کنید و با ابزارهای داخلی همان نرم‌افزار وارد کنید.

توکن API

برای ساخت توکن به /app/settings بروید و در بخش «توکن‌های API» نامی برای کلید تعریف کنید (مثلاً «سامانه مالی»). توکن به این شکل ساخته می‌شود:

tokenقالب توکن
chtk_abc123def456_4f3d…(48 کاراکتر hex)

نکات امنیتی مهم:

  • اصل توکن فقط یک‌بار نمایش داده می‌شود؛ در سیستم چرتکه فقط هش SHA-256 آن ذخیره می‌شود. بلافاصله آن را کپی و نزد خود نگه دارید.
  • هر توکن فقط به سازمان شما دسترسی دارد؛ با ‌توکن نمی‌توان به سازمان‌های دیگر دسترسی گرفت.
  • اگر توکن افشا شد یا دیگر لازم نیست، بلافاصله در صفحه تنظیمات آن را لغو (Revoke) کنید.

در هر درخواست، توکن را در هدر استاندارد بفرستید:

httpHeader احراز هویت
Authorization: Bearer chtk_abc123def456_<48-char-secret>

Endpoint‌های عمومی (v1)

دریافت اسناد — GET /api/v1/documents

لیست اسناد استخراج‌شده سازمان را با صفحه‌بندی و فیلتر برمی‌گرداند.

پارامترنوعپیش‌فرضتوضیح
pagenumber1شماره صفحه نتایج (حداقل ۱).
limitnumber20تعداد نتایج در هر صفحه؛ بین ۱ تا ۱۰۰.
searchstring—جست‌وجوی متنی. در اسناد: شماره فاکتور، فروشنده و خریدار. در حسابداری: نام یا کد همان موجودیت.
fromJstring (شمسی)—تاریخ شمسی شروع بازه (مثلاً 1404/01/01).
toJstring (شمسی)—تاریخ شمسی پایان بازه.
sellerstring—فیلتر فروشنده با مطابقت دقیق. فقط در مسیرهای اسناد.
statusstring—وضعیت سند. در اسناد مقدارِ متنِ آزادِ فارسی است (پیش‌فرض «آماده ثبت»)، نه یک enum لاتین.
bashcURL
curl -G "https://chortkey.ir/api/v1/documents" \
  -H "Authorization: Bearer chtk_abc123def456_<48-char-secret>" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=20" \
  --data-urlencode "search=فاکتور شماره";
jsNode.js / Next.js
// Node.js یا Next.js (Route Handler / Server Action)
const TOKEN = "chtk_abc123def456_<48-char-secret>";

const res = await fetch("https://chortkey.ir/api/v1/documents?page=1&limit=20&search=فاکتور", {
  headers: { Authorization: "Bearer " + TOKEN },
});

const result = await res.json();
console.log(result.data.items);
pyPython
# Python 3 (با نصب: pip install requests)
import requests

TOKEN = "chtk_abc123def456_<48-char-secret>"

res = requests.get(
    "https://chortkey.ir/api/v1/documents",
    headers={"Authorization": "Bearer " + TOKEN},
    params={"page": 1, "limit": 20},
)
data = res.json()
print(data["data"]["items"])
phpPHP / WordPress
<?php
// PHP یا افزونه WordPress
$TOKEN = "chtk_abc123def456_<48-char-secret>";

$args = array(
  "timeout" => 15,
  "headers" => array("Authorization" => "Bearer " . $TOKEN),
);
$response = wp_remote_get(
  "https://chortkey.ir/api/v1/documents?page=1&limit=20",
  $args
);
$body = json_decode(wp_remote_retrieve_body($response), true);
print_r($body["data"]["items"]);

آمار مصرف — GET /api/v1/usage

یکی از پارامترها ندارد و آمار کلی سازمان را برمی‌گرداند:

bashcURL
curl "https://chortkey.ir/api/v1/usage" \
  -H "Authorization: Bearer chtk_abc123def456_<48-char-secret>";
jsonنمونه پاسخ
{
  "success": true,
  "data": {
    "totalDocuments": 42,
    "currentPeriodDocuments": 7,
    "byStatus": [
      { "status": "draft", "count": 20 },
      { "status": "approved", "count": 15 },
      { "status": "rejected", "count": 7 }
    ],
    "byThreeWayMatch": [
      { "threeWayMatch": "تأیید شد", "count": 30 },
      { "threeWayMatch": "-", "count": 12 }
    ]
  },
  "meta": {
    "organizationId": "org-xxxx",
    "apiVersion": "v1"
  }
}

currentPeriodDocuments تعداد اسناد دوره جاری (شمسی) را نشان می‌دهد و برای کنترل سهمیه و بودجه دوره کاربرد دارد. فیلد periodIsJalali همیشه true است تا مصرف‌کننده مجبور نباشد حدس بزند دوره بر مبنای تقویم ایرانی است یا میلادی.

داده‌های حسابداری — GET /api/v1/accounting/<resource>

خواندن داده‌های حسابداری با همان توکن. این مسیر فقط خواندنی است؛ نوشتن (ثبت فاکتور، سند، رسید) از مسیر نشست و با مجوزِ داخلی انجام می‌شود و عمداً از API عمومی باز نشده تا یک توکنِ لو رفته نتواند در دفتر کل بنویسد.

این مسیر فقط برای کاربری پاسخ می‌دهد که در آن سازمان عضو است و مجوز صفحهٔ «حسابداری» را دارد. عضوِ بدون این مجوز پاسخ ۴۰۳ می‌گیرد، حتی اگر توکنش معتبر باشد.
متدEndpointتوضیح
GET/api/v1/accounting/invoicesفهرست فاکتورها؛ فیلترهای docType، status، contactId، search و include=items برای گرفتن ردیف‌های کالا. صفحه‌بندی دارد.
GET/api/v1/accounting/contactsفهرست طرف‌حساب‌ها؛ فیلترهای type و search.
GET/api/v1/accounting/itemsفهرست کالا و خدمات؛ فیلترهای type و search.
GET/api/v1/accounting/ledgerفهرست اسناد دوبل به‌همراه ردیف‌ها؛ فیلترهای docType، fromJ، toJ، search.
GET/api/v1/accounting/trial-balanceتراز آزمایشی کامل، بدون صفحه‌بندی (ترازِ ناقص بی‌معناست). شامل balanced.
GET/api/v1/accounting/profit-lossسود و زیان بازه؛ فیلترهای اختیاری fromJ و toJ.
پارامترنوعپیش‌فرضتوضیح
pagenumber1شماره صفحه نتایج (حداقل ۱).
limitnumber20تعداد نتایج در هر صفحه؛ بین ۱ تا ۱۰۰.
searchstring—جست‌وجوی متنی. در اسناد: شماره فاکتور، فروشنده و خریدار. در حسابداری: نام یا کد همان موجودیت.
fromJstring (شمسی)—تاریخ شمسی شروع بازه (مثلاً 1404/01/01).
toJstring (شمسی)—تاریخ شمسی پایان بازه.
statusstring—وضعیت. در مسیر invoices فقط دو مقدار معتبر دارد: draft و final.
docTypestring—نوع سند: sale، sale_return، sale_quote، purchase، purchase_return، purchase_quote.
contactIdstring—شناسه طرف حساب. همیشه در محدودهٔ همان سازمانِ توکن بررسی می‌شود.
includestring—فقط در invoices: با مقدار items، ردیف‌های کالا هم برمی‌گردد. پیش‌فرض خالی است تا پاسخ سبک بماند.
bashcURL — فاکتورهای قطعی یک ماه
# فاکتورهای قطعی فروشِ یک ماه
curl "https://chortkey.ir/api/v1/accounting/invoices?docType=sale&status=final&fromJ=1404/01/01&toJ=1404/01/31&limit=100" \
  -H "Authorization: Bearer chtk_abc123def456_<48-char-secret>"

# همان، ولی با ردیف‌های کالا — برای ساخت فاکتور در هلو/سپیدار/حسابفا لازم است.
# بدون این پارامتر، پاسخ فقط سرِ فاکتور است و items خالی می‌ماند.
curl "https://chortkey.ir/api/v1/accounting/invoices?docType=sale&status=final&limit=100&include=items" \
  -H "Authorization: Bearer chtk_abc123def456_<48-char-secret>"

# تراز آزمایشی (بدون صفحه‌بندی — همهٔ سطرها برمی‌گردد)
curl "https://chortkey.ir/api/v1/accounting/trial-balance" \
  -H "Authorization: Bearer chtk_abc123def456_<48-char-secret>"

# پاسخ هر دو: { "success": true, "data": ..., "meta": { "organizationId": "<uuid>", ... } }

پاسخ همهٔ این مسیرها همان پوشش { success, data, meta } را دارد و meta.organizationId همیشه یک UUID است.

برای بردن داده به نرم‌افزاری که API ندارد (خروجی اکسل/CSV)، مسیر GET /api/accounting/export را با نشست ببینید: ۱۳ موجودیت به‌صورت CSV یا XLSX و به‌شکلی که دوباره قابل ورود به چرتکه است. دکمهٔ «دانلود فایل قالب» هم در مسیر /app/accounting/data همان ستون‌ها را می‌دهد. توجه: /api/v1/documents فقط JSON برمی‌گرداند و خروجی جدولی ندارد.

وبهوک‌ها (Webhook)

وبهوک یک «قلاب» است: به محض رخ‌دادن رویداد، چرتکه یک درخواست HTTP را با محتوای JSON به آدرس شما می‌فرستد و دیگر نیازی به چک‌کردن دوره‌ای نیست. این روش برای ثبت خودکار فاکتور در نرم‌افزار حسابداری، ارسال به ربات تلگرام، داشبورد زنده و CRM مناسب است.

رویدادهای موجود

رویدادیعنی چه
document.createdسند جدید (ذخیره فاکتور)
invoice.createdفاکتور پیش‌نویس ثبت شد
invoice.finalizedفاکتور قطعی شد (آمادهٔ ارسال به نرم‌افزار حسابداری)
payment.receivedدریافت وجه
payment.paidپرداخت وجه
check.passedچک پاس شد
check.clearedچک وصول شد
check.returnedچک برگشت خورد
journal.postedسند دوبل دستی ثبت شد
contact.createdطرف حساب با ماندهٔ افتتاحیه ثبت شد
برای اتصال به نرم‌افزار حسابداری، رویداد invoice.finalized همان چیزی است که لازم دارید: این رویداد فقط وقتی می‌آید که فاکتور واقعاً قطعی شده و ردیف‌های کالا با جزئیات کامل داخل data.items آمده‌اند. رویداد invoice.created فقط پیش‌نویس است و ممکن است بعداً تغییر کند، پس مبنای همگام‌سازی نیست.

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

jsonWebhook Payload — رویداد سند
{
  "event": "document.created",
  "data": {
    "id": "uuid",
    "invoiceNumber": "1234",
    "seller": "شرکت الف",
    "buyer": "شرکت ب",
    "issueDateJ": "1404/01/15",
    "finalAmount": 12500000,
    "threeWayMatch": "-"
  },  "sentAt": "2026-09-16T10:30:00.000Z"
}

و برای invoice.finalized، فیلد data چنین است (items آرایهٔ ردیف‌هاست و همیشه می‌آید، حتی اگر خالی باشد):

jsonWebhook Payload — فاکتور قطعی
{
  "event": "invoice.finalized",
  "data": {
    "id": "uuid",
    "docType": "sale",
    "number": "1404-12",
    "issueDateJ": "1404/01/15",
    "dueDateJ": "1404/02/15",
    "contactId": "uuid",
    "currency": "ریال",
    "subtotal": 11363636,
    "discount": 0,
    "vat": 1136364,
    "finalAmount": 12500000,
    "paidAmount": 0,
    "items": [
      {
        "description": "خدمات پشتیبانی",
        "itemId": "uuid",
        "quantity": 1,
        "unitPrice": 11363636,
        "discount": 0,
        "total": 11363636,
        "taxRate": 10,
        "vat": 1136364
      }
    ]
  },
  "sentAt": "2026-09-16T10:30:00.000Z"
}

تأیید امضای درخواست (الزامی)

هر درخواست وبهوک با هدر امضا می‌شود:

httpHeader امضا
X-Chortke-Signature: t=1770000000,v1=<64-char-hmac-sha256>

t زمان ارسال (ثانیه Unix) و v1 مقدار HMAC-SHA256 بدنه درخواست با «کلید محرمانه» وبهوک است. کلید محرمانه هنگام ساخت وبهوک فقط یک‌بار نمایش داده می‌شود. همیشه پیش از پردازش، امضا را بررسی کنید؛ این کار از ارسال‌های جعلی و درخواست‌های بازپخش جلوگیری می‌کند.

jsتأیید امضا — Node.js / Next.js
// تأیید امضای وبهوک در Node.js / Next.js
import { createHmac, timingSafeEqual } from "crypto";

export function verifyWebhook(rawBody, signatureHeader, secret) {
  const parts = signatureHeader.split(",");
  const timestamp = String(parts.find((p) => p.startsWith("t="))?.slice(2) ?? "");
  const signature = parts.find((p) => p.startsWith("v1="))?.slice(3) ?? "";

  // ضد بازپخش: بازه زمانی ۵ دقیقه
  const now = Math.floor(Date.now() / 1000);
  if (now - Number(timestamp) > 300) return false;

  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(signature, "hex");
  const b = Buffer.from(expected, "hex");
  return a.length === b.length && timingSafeEqual(a, b);
}

// در Route Handler:
export async function POST(req) {
  const rawBody = await req.text();
  const header = req.headers.get("x-chortke-signature") ?? "";
  const ok = verifyWebhook(rawBody, header, "YOUR_WEBHOOK_SECRET");
  if (!ok) return new Response("invalid signature", { status: 401 });
  const payload = JSON.parse(rawBody);
  // حالا payload.event === "document.created"
  return new Response("ok", { status: 200 });
}
pyتأیید امضا — Python (FastAPI)
# دریافت‌کننده وبهوک با FastAPI / Flask
import hashlib
import hmac
import time
from fastapi import FastAPI, Request, Header

app = FastAPI()
WEBHOOK_SECRET = "YOUR_WEBHOOK_SECRET"

@app.post("/webhook")
async def webhook(request: Request, x_chortke_signature: str = Header("")):
    raw_body = (await request.body()).decode()
    parts = dict(p.split("=", 1) for p in x_chortke_signature.split(","))
    ts = int(parts.get("t", "0"))
    signature = parts.get("v1", "")

    if int(time.time()) - ts > 300:
        return {"error": "expired"}

    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        raw_body.encode(),
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return {"error": "invalid"}, 401

    payload = await request.json()
    # payload["event"] == "document.created"
    return {"ok": True}
phpتأیید امضا — PHP / WordPress
<?php
// دریافت‌کننده وبهوک در PHP / WordPress
define("WEBHOOK_SECRET", "YOUR_WEBHOOK_SECRET");

$rawBody = file_get_contents("php://input");
$signatureHeader = $_SERVER["HTTP_X_CHORTKE_SIGNATURE"] ?? "";
$secret = constant("WEBHOOK_SECRET");

$parts = [];
foreach (explode(",", $signatureHeader) as $item) {
    [$k, $v] = explode("=", $item, 2);
    $parts[$k] = $v;
}
if (time() - (int)($parts["t"] ?? 0) > 300) {
    http_response_code(401);
    exit;
}
$expected = hash_hmac("sha256", $rawBody, $secret);
if (!hash_equals($expected, $parts["v1"] ?? "")) {
    http_response_code(401);
    exit;
}
$payload = json_decode($rawBody, true);
// $payload["event"] === "document.created"
http_response_code(200);
echo "ok";

نکات فنی

  • حداکثر زمان پاسخ‌دهی شما ۱۰ ثانیه است. آدرس مقصد باید http یا https باشد، ولی چون محتوای وبهوک شامل داده‌های مالی است، HTTPS را اکیداً توصیه می‌کنیم: روی HTTP، امضا و محتوا در هر مسیری که بین شما و سرور جابه‌جا می‌شود قابل خواندن و دست‌کاری است.
  • در حال حاضر تلاش مجدد (Retry) خودکار انجام نمی‌شود؛ سرور شما باید سریع و پایدار باشد.
  • برای قطع موقت، وبهوک را از صفحه تنظیمات غیرفعال کنید؛ بدون حذف آدرس.
  • اگر ارسال به یک وبهوک با خطا مواجه شود، بقیه وبهوک‌ها و مسیر اصلی پردازش سند آسیبی نمی‌بینند.

بهترین روش‌های امنیتی

  1. توکن و کلید محرمانه وبهوک را در متغیرهای محیطی (Env) نگه دارید؛ هرگز در کد یا صفحه وب آشکار نکنید.
  2. برای هر نرم‌افزار، یک توکن جداگانه بسازید تا در صورت نیاز فقط همان‌ها را لغو کنید.
  3. همیشه پیش از پردازش وبهوک، امضای X-Chortke-Signature را با timingSafeEqual/hash_equals بررسی و بازه زمانی t را محدود کنید (مثال: ۵ دقیقه).
  4. آدرس وبهوک را ترجیحاً فقط HTTPS ثبت کنید و اطمینان حاصل کنید سرور شما پاسخ سریع (زیر ۱۰ ثانیه) می‌دهد.
  5. دسترسی ساخت توکن را فقط به مدیر/مالک سازمان محدود نگه دارید.

محدودیت‌هایی که باید بدانید

محدودیتجزئیات
توکن، همه‌ی کار را می‌کندتوکن سطح‌دسترسی (scope) ندارد: یا همهٔ مسیرهای مجازِ آن سازمان را می‌بیند یا هیچ‌کدام را. اگر برای یک نرم‌افزار توکن جدا می‌سازید، دلیلش فقط امکان لغو جداگانه است، نه محدودکردن دسترسی.
سقف نرخ بر اساس IP است، نه توکنمحدودیت نرخ روی آی‌پی هر درخواست اعمال می‌شود (پیش‌فرض ۱۲۰ درخواست در دقیقه، قابل تنظیم توسط مدیر). اگر چند سرویس پشت یک IP مشترک اجرا شوند، سهمیهٔ همهٔ آن‌ها یکی است و ممکن است به ۴۲۹ بخورید.
نیازمند پلن متوسط یا بزرگهمهٔ مسیرهای توسعه‌دهنده — توکن، وبهوک و /api/v1 — در پلن‌های پایین‌تر با ۴۰۳ بسته‌اند.
تلاش مجدد انجام نمی‌شودوبهوک یک‌بار فرستاده می‌شود و در صورت خطا فقط لاگ می‌شود؛ نه صف دارد، نه retry. اگر همگام‌سازی برایتان حیاتی است، در سرور مقصد وضعیت هر دریافت را نگه دارید و در صورت نیاز از /api/v1/accounting دوباره بگیرید.
امضا را سرور ما کنترل نمی‌کندپارامتر t فقط یک راهنماست؛ ما بازپخش (replay) را مسدود نمی‌کنیم. بررسی پنجرهٔ زمانی و ذخیرهٔ شناسهٔ رویداد برای جلوگیری از پردازش دوباره، بر عهدهٔ سرور شماست.

عیب‌یابی رایج

مشکلعلت و راه‌حل
پاسخ HTTP 401 «توکن نامعتبر یا لغو شده»توکن اشتباه، دارای کاراکتر فضای خالی، یا قبلاً لغو شده است. توکن جدید بسازید و دقیقاً در هدر Authorization قرار دهید.
وبهوک ارسال می‌شود اما دیتا ثبت نمی‌شودامضای هدر را بررسی کنید؛ متداول‌ترین علت، خطا در کپی کلید محرمانه یا اشتباه در HMAC است.
وبهوک پاسخ نمی‌گیرد (Timeout)سرور شما باید زیر ۱۰ ثانیه پاسخ دهد؛ وبهوک را موقتاً غیرفعال کنید و سرور را بررسی کنید.
تاریخ ازJ / تاJ کار نمی‌کنداین پارامترها به شمسی (Jalali) هستند مثل 1404/01/01؛ نه MILADI.
چرا اصل توکن دوباره نمایش داده نمی‌شود؟برای امنیت، فقط در لحظه ساخت نمایش داده می‌شود. اگر گم شد، توکن قبلی را لغو و توکن جدید بسازید.

آماده اتصال هستید؟ ابتدا در تنظیمات توسعه‌دهنده توکن یا وبهوک بسازید و در صورت نیاز به کمک با مرکز پشتیبانی در تماس باشید.