وبهوکها
اعلان HTTPS رایگان هنگام بهروزرسانی جداول کدال، صندوق یا گزارشها — با JWT؛ امضای HMAC.
اعلانهای HTTPS خروجی وقتی جداول همگامسازی کدال، صندوق یا گزارشها بهروز میشوند. رایگان است (بدون کسر از کیف پول). برای دریافت ردیفها همچنان باید GET /external/codal/data، GET /external/funds/data یا GET /external/reports/data را با کلید API (مثلاً updated_at_start) فراخوانی کنید — آن فراخوانیها هزینهدارند.
Auth: JWT Authorization: Bearer <access_token> (همان کیف پول و کلیدها)
| Item | Value |
|---|---|
| حداکثر endpoint فعال | ۳ بهازای هر کاربر (EXTERNAL_API_WEBHOOK_MAX_PER_USER) |
| رویدادها (v1) | codal.table.updated, funds.table.updated, reports.table.updated |
| غیرفعالسازی خودکار | پس از ۲۰ شکست تحویل متوالی |
/api/v1/external/webhooksساخت endpoint جدید.
Body:
{
"url": "https://partner.example.com/hooks/codal",
"events": ["codal.table.updated", "funds.table.updated", "reports.table.updated"],
"description": "Prod ingest"
}پاسخ شامل signing_secret یکبار است (امن نگه دارید). در لیست بعدی فقط secret_last4 نمایش داده میشود. URL باید HTTPS باشد.
Example:
curl -X POST "https://api.codal724.ir/api/v1/external/webhooks" \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d "{\"url\":\"https://partner.example.com/hooks/codal\",\"events\":[\"codal.table.updated\",\"funds.table.updated\",\"reports.table.updated\"]}"مدیریت endpoint
همه مسیرها زیر /api/v1/external و با JWT.
| Method | Path | توضیح |
|---|---|---|
| GET | /webhooks | فهرست endpointها |
| PATCH | /webhooks/{id} | فقط جایگزینی events. signing_secret را عوض نمیکند. URL در ساخت باید HTTPS باشد |
| DELETE | /webhooks/{id} | حذف |
| POST | /webhooks/{id}/rotate-secret | signing_secret جدید (یکبار) |
| POST | /webhooks/{id}/test | رویداد آزمایشی در صف — اگر اشتراک کدال وجود داشته باشد همان را ترجیح میدهد؛ تست تحویل گزارشها نیست |
| GET | /webhooks/{id}/deliveries | گزارش تحویلهای اخیر |
برای اضافه کردن reports.table.updated نیازی به حذف/ساخت مجدد یا rotate-secret نیست؛ PATCH کافی است.
PATCH body:
{
"events": ["codal.table.updated", "funds.table.updated", "reports.table.updated"]
}قرارداد تحویل (Delivery)
POST به URL شریک با بدنه JSON. فیلدهای data همیشه: service, table_name, last_updated_at.
{
"id": "…",
"type": "codal.table.updated",
"created_at": "…",
"api_version": "2026-07-25",
"data": {
"service": "codal",
"table_name": "income_statement",
"last_updated_at": "…"
}
}فقط برای گزارشها، فیلدهای اضافه (اختیاری):
type | data.table_name | Extra |
|---|---|---|
reports.table.updated | symbol_reports | optional symbol |
reports.table.updated | market_daily_reports | optional trade_date (YYYY-MM-DD) |
{
"id": "…",
"type": "reports.table.updated",
"created_at": "…",
"api_version": "2026-07-25",
"data": {
"service": "reports",
"table_name": "symbol_reports",
"last_updated_at": "…",
"symbol": "فولاد"
}
}table_name ناشناخته را با HTTP 200 نادیده بگیرید (نه 4xx) تا از retry و غیرفعالسازی خودکار جلوگیری شود.
Headers:
| Header | Meaning |
|---|---|
X-Webhook-Id | همان id بدنه (dedupe) |
X-Webhook-Timestamp | Unix seconds |
X-Webhook-Signature | sha256=<hex> از {timestamp}.{raw_body} با signing secret |
Verify (Node.js):
import crypto from "crypto";
export function verifyWebhook(
secret: string,
timestamp: string,
rawBody: Buffer,
signatureHeader: string
): boolean {
const expected =
"sha256=" +
crypto.createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader.trim());
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
}پس از تأیید امضا: با کلید API شریک، مثلاً
GET https://api.codal724.ir/api/v1/external/codal/data?table_name=income_statement&updated_at_start=<last_updated_at>
(یا معادل صندوقها / گزارشها) — آن درخواست pull هزینهدار است.
متغیرهای محیط (backend)
| Variable | Default | Role |
|---|---|---|
EXTERNAL_API_WEBHOOK_MAX_PER_USER | 3 | Max enabled endpoints |
EXTERNAL_API_WEBHOOK_TIMEOUT_SECONDS | 10 | Outbound POST timeout |
EXTERNAL_API_WEBHOOK_MAX_RETRIES | 8 | Delivery retries |
EXTERNAL_API_WEBHOOK_DISABLE_AFTER_FAILURES | 20 | Auto-disable endpoint |