Every outbound integration: what it does, when it fires, how auth works, retry behaviour, fallbacks, and all environment variables. Internal-only services (captcha, offline inquiry seed) are included for completeness.
Every blame file starts with a "run-inquiries" call that fetches the
guilty party's insurance policy from an external provider. Which provider
is actually called depends on three factors: the tenant (CLIENT_ID),
the file type (THIRD_PARTY vs CAR_BODY), and whether live API mode is
enabled in system settings. The offline-inquiry seed layer sits in front
of all three providers.
CLIENT_ID=8 (Parsian/ESG tenant) → route to ESG /inquiry/policyByPlate for plates or the two-factor /inquiry/carByChassis for VIN/chassis inquiries./block-inquiry-tejarat (THIRD_PARTY) or /block-inquiry-tejarat/badane (CAR_BODY).system_settings.externalApis.sandHubUseLiveApi = false (default) → return mock response instead of making HTTP calls.CLIENT_ID=8 → ESG /inquiry/person and /inquiry/sheba./personal-inquiry/tejarat-no, /driver-license-check, /ownership, /sheba/sheba-tejaratno.SandHub endpoints are only used in legacy code paths. All active V2+ blame flows go through the Tejarat or ESG providers.
Fanavaran (apimanager.iraneit.com) is the national insurance
damage-case platform. After the damage expert submits their assessment,
the system auto-submits a structured claim to Fanavaran through a
four-step protocol. Fanavaran also serves as the lookup source for
code-lists (accident types, car components, city codes, etc.) used
across the platform.
POST /EITAuthentication/GetAppToken with appname + secret headers. Returns apptoken header.POST /EITAuthentication/Login with appToken + userName + password headers. Returns authenticationToken header.fanavaran_auth_tokens). Valid until midnight Asia/Tehran — the first call after 00:00 fetches a fresh token.authenticationToken, CorpId, ContractId, Location — tenant-specific, hardcoded per FANAVARAN_CLIENT key.A config fingerprint (hash of appName + secret + username + password + corpId + contractId + location) forces a fresh login when any credential changes, even before midnight.
POST /car/third-party-car-financial-claims. Sends owner, driver, insurance, vehicle, and accident data. Returns a Fanavaran claimId and claimNo, which are persisted for panel display.POST /car/third-party-car-financial-claims/{claimId}/dmg-cases. One entry per damaged part with component ID, severity, and price. Cap: total ≤ 53 000 000 Toman.POST /car/third-party-car-financial-claims/{claimId}/files. Documents, car-capture images, and videos referenced by file ID.POST /car/third-party-car-financial-claims/{claimId}/expertise. Expert assessment metadata (expert role, date, result). Finalises the submission.
All four steps are recorded in the fanavaran_audit_logs collection with full request/response bodies, HTTP status, duration, and tracking code for debugging.
All under https://apimanager.iraneit.com/BimeApiManager/api/BimeApi/v2.0/. Results are cached to disk (per client key) and in the lookups MongoDB collection. Parsian tenant reads DB before hitting the API; others go to the API first.
| Path | Used for |
|---|---|
/car/base-info/accident-causes | accidentReason dropdown options (mapped to local IDs) |
/car/code-list/accident-report-type | accidentWay options |
/car/base-info/vehicle-use-types | vehicle usage classification |
/car/code-list/dmg-pay-method | damage payment method |
/car/base-info/driving-licence-types | licence type options |
/car/code-list/accident-culprit-type | guilty-party classification |
/car/code-list/inspection-place | inspection location options |
/car/code-list/drop-amount-status | price-drop status codes |
/car/base-info/car-components | component catalog (maps to outer/inner parts) |
/car/code-list/accident-level | accident severity options |
/common/code-list/insurance-corp | resolve INSURANCE_CORP_ID → Fanavaran corpId |
/common/base-info/cities, /common/base-info/Provinces | city/province pickers |
/car/third-party-car-policies/{policyId} | fetch full policy by ID after inquiry |
/car/vehicles/inquiry-by-vin?vin=… | VIN-based vehicle lookup |
/common/Policies/inquiry-my-policies | list policies for a national code |
/common/customers/{customerId} | fetch customer record by ID |
/common/parties/inquiry-by-unique-identifier | party lookup by national code + birth date |
| Mechanism | Detail |
|---|---|
| Retry | 3 attempts, 500 ms → 1 000 ms exponential backoff on all HTTP calls. |
| Transient backoff | When Fanavaran returns the Persian "try again later" message (or tracking-code 500), a tenant-wide 5-minute pause is activated. All calls during this window get 503 ServiceUnavailable immediately — no hammering. |
| Token invalidation | On 401, token is cleared from memory and MongoDB; next call triggers a fresh GetAppToken + Login. |
| Inflight de-dup | Concurrent login requests for the same tenant are collapsed to a single in-flight Promise. |
| Audit log | Every step (GET_APP_TOKEN, LOGIN, and all four submission steps) is written to fanavaran_audit_logs with STARTED / SUCCESS / FAILURE status, full headers, body, and duration. |
| Timeout | 20–30 s per HTTP call. |
FANAVARAN_CLIENT)Three pre-seeded tenant profiles exist. The active one is chosen by the FANAVARAN_CLIENT env var. Each profile carries its own appName, secret, username, password, CorpId, ContractId, and Location headers, plus payload defaults (AccidentCityId, etc.).
| Key | Insurance company |
|---|---|
parsian | Parsian Insurance |
tejaratno | Tejaratno Insurance |
moallem | Moallem Insurance |
INSURANCE_CORP_ID is a display-caption string (e.g. "بیمه پارسیان") that is resolved against the live Fanavaran insurance-corp list to produce the numeric corpId used in submissions. The resolved ID is cached to disk.
SandHub is the original inquiry gateway. It is still present in the
codebase but all active blame flows (V2+) have been migrated to the
Tejarat inquiry provider. SandHub endpoints remain callable but are
only reached through legacy code paths. Its mock mode is controlled
by the same sandHubUseLiveApi system setting.
POST {SANHUB_BASE_URL}/user/login with username + password JSON body.
Token cached in memory for 55 minutes. On 401, token is cleared and one retry is made.
3 attempts with 1 000 ms → 2 000 ms exponential backoff.
| Method | Path | What it does |
|---|---|---|
| POST | /block-inquiry-tejarat | Plate-based insurance policy inquiry (THIRD_PARTY). Body: leftTwoDigits, serialLetter, threeDigits, rightTwoDigits, nationalCode. |
| POST | /block-inquiry-tejarat/badane | CAR_BODY policy inquiry. Timeout 50 s (longer than standard). |
| POST | /personal-inquiry/tejarat-no | Personal identity check. Body: nationalCode + Gregorian birthDate (converted from Jalali internally). |
| POST | /driver-license-check | Driving licence validation. Returns IsSucceed flag. |
| POST | /ownership | Vehicle ownership check. Returns IsSuccess flag. |
| POST | /sheba/sheba-tejaratno | Sheba / bank account validation. Returns ReturnValue + HasError. |
All endpoints support full mock responses when sandHubUseLiveApi=false in system settings (default). Mock data is deterministic and produced locally without any HTTP calls.
The active block-inquiry gateway for all non-ESG tenants. Used in every
V2+ run-inquiries call where CLIENT_ID ≠ 8.
The base URL is configurable; in production it points to the same host
as SandHub but uses separate credentials.
POST {TEJARAT_INQUIRY_BASE_URL}/user/login with email + password JSON body.
Token cached for 55 minutes. 2 attempts with 500 ms → 1 000 ms backoff.
Separate from SandHub credentials — uses TEJARAT_INQUIRY_EMAIL / TEJARAT_INQUIRY_PASSWORD.
| Method | Path | What it does |
|---|---|---|
| POST | /block-inquiry-tejarat | THIRD_PARTY plate inquiry. Body: plate fields + nationalCode. Offline seed checked first. |
| POST | /block-inquiry-tejarat/badane | CAR_BODY plate inquiry. Body: part1–part4 (numeric) + nationalCode. Always goes live (no mock for badane path). |
When sandHubUseLiveApi=false, the THIRD_PARTY path returns a mock response without HTTP. The CAR_BODY path always calls the live API regardless of this flag.
ESG is an internal insurance API gateway used exclusively by the Parsian
tenant (CLIENT_ID=8). It replaces Tejarat/SandHub for all
inquiry types when this tenant is active. It has a different response
shape, a dynamic token TTL, and expects birth dates in Jalali
format (not Gregorian, unlike SandHub/Tejarat).
POST {ESG_URL}/auth/login with { username, password } JSON body.
Token TTL is read from the response expiresIn field (default 14 min).
2 attempts with 500 ms → 1 000 ms backoff. On 401, token cleared and one retry.
Default URL: http://192.168.20.22:8085 (internal network).
| Method | Path | What it does |
|---|---|---|
| POST | /inquiry/policyByPlate | Plate-based policy lookup (THIRD_PARTY). Body: nationalCode, plk1–plk4. Response is mapped to the old Tejarat format before being stored. |
| POST | /inquiry/carByChassis | Two-factor VIN/chassis alternative to the plate inquiry. Called by run-inquiries-vin endpoints. Body: nationalCode, chassisNo. The one-factor policyByChassis route is not used because it rejects nationalCode. |
| POST | /inquiry/person | Personal identity check. Body: nationalCode, birthDate (Jalali, NOT Gregorian). |
| POST | /inquiry/sheba | Sheba / bank account validation. |
ESG wraps every response as { success: boolean, data: … }. For normalized error envelopes, the backend returns error.messageFa unchanged to the caller; technical fields such as message, providerMessage, and providerCode remain available for logging and classification. A business-level not-found response is not reported as a provider outage.
The offline-inquiry seed check still runs first, before any ESG HTTP call.
Two SMS providers are supported: Kavenegar (default)
and Parsian SMS Gateway. The active provider is chosen
by the SMS_PROVIDER (or SMS) env var. Both
providers implement the same internal gateway interface so the
orchestration layer is provider-agnostic.
| Env var | Value | Active provider |
|---|---|---|
SMS_PROVIDER (or SMS) | kavenegar (default) | Kavenegar — api.kavenegar.com |
SMS_PROVIDER (or SMS) | parsian | Parsian SMS Gateway — PARSIAN_SMS_URL |
Base URL: https://api.kavenegar.com/v1/{SMS_API_KEY}/
| Method | Path | When used |
|---|---|---|
| POST | sms/send.json | Plain-text messages (e.g. key-based notification texts stored in sms_texts collection). |
| GET | verify/lookup.json | All template-based messages (OTPs, invite links, expert notifications). Params: receptor, token[, token2, token3, token10], template. |
Base URL from PARSIAN_SMS_URL. Auth: X-PACKAGE-API-KEY header + Authorization: Basic {PARSIAN_BASIC_TOKEN}. Sends as a GET with URL-encoded ReceiverNumbers and Message query params. Template messages are pre-rendered into a plain text body before sending (no verify/lookup equivalent).
| Template name | Trigger | Tokens |
|---|---|---|
AUTH_SMS_TEMPLATE (env) | User / actor OTP login, forget-password, party OTPs | token = OTP code |
yara724-invite-link | Second party receives blame invite link via SMS | token = publicId, token2 = link |
yara-field-expert-link | Field expert sends link to a party | token = file type, token2 = expert surname, token3 = link |
yara-blame-agreement | Notify party that the other side agreed to the expert verdict | token = publicId, token2 = link |
yara-claim-link | Damaged party notified to open claim flow after blame is complete | token = publicId, token2 = link |
yara-expert-lock | Expert locks a blame or claim file | token = "تصادف"/"خسارت", token2 = publicId, token3 = expert surname |
yara-resend-documents | Expert requests document resend | token = file kind, token2 = publicId, token3 = link |
yara-signature | Party notified to sign the expert's damage assessment | token = file kind, token2 = publicId, token3 = expert surname, token10 = link |
yara-fanavaran-claim | Retained legacy template; automatic dispatch after the final Fanavaran stage is disabled | token = publicId, token2 = Fanavaran claimId, token3 = Fanavaran claimNo |
All SMS calls are fire-and-forget — they never throw. Failures are logged but do not block the main flow.
An sms_send_logs MongoDB collection records every outbound message with its kind (OTP vs TEMPLATE), provider, template name, and success/failure status.
Notification text messages (parties-disagree, one-party-signed, etc.) are seeded into the sms_texts collection on startup and editable at runtime.
An image-based car damage detection service is integrated in the
codebase but its HTTP calls are fully commented out.
The module initialises on startup, attempts a login (silently swallowed
if it fails), and exposes an aiRequestImage method — but
the underlying axios calls are disabled. The service does not affect
any production flow.
| Method | Path | What it does |
|---|---|---|
| POST | {AI_URL_V2}/auth/login | Authenticate with username + password. Returns accessToken. |
| GET | {AI_URL_V2}/auth/profile | Fetch apiKey.key needed as the gateway-api-key request header. |
| POST | {AI_URL_V2}/services/car-damage/detector?version=ai-v7 | Submit a car part image (multipart). Returns downloadLink with annotated result. |
Status: all three calls are wrapped in commented-out axios.request(…) blocks.
CW_URL is not in .env.example. To re-enable, uncomment the login, getApiKey, and aiRequestImage axios calls, and configure AI_URL_V2, AI_USERNAME, AI_PASSWORD.
Used only during damage-expert price-drop calculation. When an expert provides per-part severity values the system fetches real-time market prices for the damaged car model, then computes the price-drop using the formula: carPrice × yearCoefficient × sumOfPartCoefficients ÷ 400. The service has two data sources (endpoints) that are tried in parallel.
| Method | Path | What it does |
|---|---|---|
| GET | {CW_URL}price?akharin | Fetch car market prices from the "Akharin" source. Returns array of { carName, marketPrice }. |
| GET | {CW_URL}price?hamrah | Fetch car market prices from the "Hamrah" source. Same response shape. |
Both endpoints are tried; results are merged and de-duplicated. The best match for the damaged car's name is found using Levenshtein distance (fuzzy string match). If both endpoints fail or return empty, the price-drop calculation is skipped (marked incomplete) — it does not block claim submission.
CW_URL is not documented in .env.example.
This service will silently produce no price-drop if the variable is unset.
The offline inquiry layer intercepts plate-based inquiry calls before any external HTTP is made. It is primarily used for development and testing (pre-seeded known plates) but also acts as a resilience fallback when live inquiry services are unavailable. It is controlled by a runtime database flag, not an env var.
| Aspect | Detail |
|---|---|
| Storage | MongoDB collection offline-inquiries. Documents contain clientKey, normalised plate fields, nationalCode, and the pre-built raw + mapped response to return. |
| Master switch | system_settings.offlineInquiry.enabled — defaults to true. Toggle via PATCH /super-admin/system-settings/offline-inquiry. |
| Lookup order | Normalised plate (digits-only, Arabic→Persian) + national code + Fanavaran client key must all match. If found, returned immediately; no HTTP call is made. |
| Scope | Only applies to plate-based block-inquiry (THIRD_PARTY). CAR_BODY inquiry (/badane) always hits the live API. |
| Live API flag | system_settings.externalApis.sandHubUseLiveApi — when false (default), even if no offline seed matches, a built-in mock response is returned rather than calling Tejarat/ESG. |
All env vars across all integrations, grouped by service.
Variables marked * are not present in .env.example.
| Variable | Description |
|---|---|
FANAVARAN_CLIENT | Active tenant profile key: parsian | tejaratno | moallem |
INSURANCE_CORP_ID | Display caption of the insurer company (e.g. "بیمه پارسیان") — resolved to a numeric corpId at startup against the Fanavaran insurance-corp list. |
Per-tenant credentials (appName, secret, username, password, CorpId, ContractId, Location) are hardcoded in src/core/config/fanavaran-client.config.ts under SEED_FANAVARAN_CLIENT_PROFILES.
| Variable | Description |
|---|---|
SANHUB_BASE_URL | Base URL for SandHub. Default: http://82.99.202.245:3027 |
SANHUB_URL_LOGIN | Full login URL (usually base + /user/login) |
SANHUB_USERNAME | SandHub login email |
SANHUB_PASSWORD | SandHub login password |
| Variable | Description |
|---|---|
TEJARAT_INQUIRY_BASE_URL | Base URL. Default: http://82.99.202.245:3027 |
TEJARAT_INQUIRY_EMAIL | Login email |
TEJARAT_INQUIRY_PASSWORD | Login password |
| Variable | Description |
|---|---|
CLIENT_ID | Set to 8 to activate the ESG inquiry provider for the Parsian tenant. |
ESG_URL | ESG base URL. Default: http://192.168.20.22:8085 (internal network) |
ESG_USERNAME | ESG login username |
ESG_PASSWORD | ESG login password |
| Variable | Description |
|---|---|
SMS_PROVIDER (or SMS) | kavenegar (default) or parsian |
SMS_API_KEY | Kavenegar API key (required when provider = kavenegar) |
AUTH_SMS_TEMPLATE | Kavenegar template name for OTP messages (e.g. yara-otp) |
PARSIAN_SMS_URL | Parsian SMS Gateway base URL (required when provider = parsian) |
PARSIAN_API_KEY | Parsian SMS X-PACKAGE-API-KEY header value |
PARSIAN_BASIC_TOKEN | Base64-encoded credentials for Authorization: Basic … header |
URL | Frontend base URL — used to build all invite + claim links embedded in SMS messages |
| Variable | Description |
|---|---|
AI_URL_V2 | AI gateway base URL. Default: https://ai-gw.ittalie.ir (unused — service is disabled) |
AI_USERNAME | AI service login username (unused) |
AI_PASSWORD | AI service login password (unused) |
| Variable | Description |
|---|---|
CW_URL * | Base URL for car market price API (e.g. https://…/). Not in .env.example. Price-drop silently skipped if unset. |
| Variable | Description |
|---|---|
PORT | HTTP port (default 3000). Used by the Fanavaran insurance-corp fallback to call its own local lookup endpoint. |
CAPTCHA_ENABLED | true / false — enables/disables login CAPTCHA challenge. Internal, no external service. |
EXP_CAPTCHA_TIME | CAPTCHA challenge TTL in minutes. |
EXP_OTP_TIME | OTP TTL in minutes. |