External Integrations Reference

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.

Contents
  1. Inquiry routing decision tree
  2. Fanavaran — insurance claims platform
  3. SandHub — legacy inquiry gateway
  4. Tejarat inquiry — block-inquiry gateway (V2+)
  5. ESG — Parsian-tenant inquiry provider
  6. SMS — Kavenegar and Parsian gateways
  7. AI service — car damage detection
  8. Car pricing service — market value lookup
  9. Offline inquiry — fallback seed data
  10. Environment variable reference

1 — Inquiry Routing Decision Tree

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.

Provider selection

For every plate-based block inquiry:
  • The request always uses the submitted current plate and the resolved policyholder for that policy type. Recent-transfer metadata never triggers a previous-plate or previous-policyholder lookup.
  • 1. Check offline-inquiry seeds (MongoDB) — if a matching seed exists, return it and skip all HTTP.
  • 2. If CLIENT_ID=8 (Parsian/ESG tenant) → route to ESG /inquiry/policyByPlate for plates or the two-factor /inquiry/carByChassis for VIN/chassis inquiries.
  • 3. Otherwise → route to Tejarat inquiry /block-inquiry-tejarat (THIRD_PARTY) or /block-inquiry-tejarat/badane (CAR_BODY).
  • 4. If system_settings.externalApis.sandHubUseLiveApi = false (default) → return mock response instead of making HTTP calls.

For personal-identity, driving-licence, ownership, and Sheba checks:
  • If CLIENT_ID=8 → ESG /inquiry/person and /inquiry/sheba.
  • Otherwise → Tejarat/SandHub /personal-inquiry/tejarat-no, /driver-license-check, /ownership, /sheba/sheba-tejaratno.

Key difference — birth date format: SandHub/Tejarat expect a Gregorian birth date (converted internally from Jalali). ESG expects the Jalali date directly.

SandHub endpoints are only used in legacy code paths. All active V2+ blame flows go through the Tejarat or ESG providers.

2 — Fanavaran live

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.

Authentication lifecycle

1
GET AppToken — POST /EITAuthentication/GetAppToken with appname + secret headers. Returns apptoken header.
2
Login — POST /EITAuthentication/Login with appToken + userName + password headers. Returns authenticationToken header.
3
Cache — token is cached in memory and persisted to MongoDB (fanavaran_auth_tokens). Valid until midnight Asia/Tehran — the first call after 00:00 fetches a fresh token.
4
All subsequent calls include four headers: 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.

Claim submission protocol (4 steps)

1
Base claim (GEN.03) — 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.
2
Damage cases (GEN.05) — 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.
3
Attachments (GEN.07) — POST /car/third-party-car-financial-claims/{claimId}/files. Documents, car-capture images, and videos referenced by file ID.
4
Expertise (GEN.08) — 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.

Lookup endpoints

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.

PathUsed for
/car/base-info/accident-causesaccidentReason dropdown options (mapped to local IDs)
/car/code-list/accident-report-typeaccidentWay options
/car/base-info/vehicle-use-typesvehicle usage classification
/car/code-list/dmg-pay-methoddamage payment method
/car/base-info/driving-licence-typeslicence type options
/car/code-list/accident-culprit-typeguilty-party classification
/car/code-list/inspection-placeinspection location options
/car/code-list/drop-amount-statusprice-drop status codes
/car/base-info/car-componentscomponent catalog (maps to outer/inner parts)
/car/code-list/accident-levelaccident severity options
/common/code-list/insurance-corpresolve INSURANCE_CORP_ID → Fanavaran corpId
/common/base-info/cities, /common/base-info/Provincescity/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-policieslist policies for a national code
/common/customers/{customerId}fetch customer record by ID
/common/parties/inquiry-by-unique-identifierparty lookup by national code + birth date

Error handling & resilience

MechanismDetail
Retry3 attempts, 500 ms → 1 000 ms exponential backoff on all HTTP calls.
Transient backoffWhen 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 invalidationOn 401, token is cleared from memory and MongoDB; next call triggers a fresh GetAppToken + Login.
Inflight de-dupConcurrent login requests for the same tenant are collapsed to a single in-flight Promise.
Audit logEvery 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.
Timeout20–30 s per HTTP call.

Tenant profiles (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.).

KeyInsurance company
parsianParsian Insurance
tejaratnoTejaratno Insurance
moallemMoallem 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.

3 — SandHub legacy

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.

Auth

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.

Endpoints

MethodPathWhat it does
POST/block-inquiry-tejaratPlate-based insurance policy inquiry (THIRD_PARTY). Body: leftTwoDigits, serialLetter, threeDigits, rightTwoDigits, nationalCode.
POST/block-inquiry-tejarat/badaneCAR_BODY policy inquiry. Timeout 50 s (longer than standard).
POST/personal-inquiry/tejarat-noPersonal identity check. Body: nationalCode + Gregorian birthDate (converted from Jalali internally).
POST/driver-license-checkDriving licence validation. Returns IsSucceed flag.
POST/ownershipVehicle ownership check. Returns IsSuccess flag.
POST/sheba/sheba-tejaratnoSheba / 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.

4 — Tejarat Inquiry live

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.

Auth

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.

Endpoints

MethodPathWhat it does
POST/block-inquiry-tejaratTHIRD_PARTY plate inquiry. Body: plate fields + nationalCode. Offline seed checked first.
POST/block-inquiry-tejarat/badaneCAR_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.

5 — ESG live (CLIENT_ID=8)

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).

Auth

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).

Endpoints

MethodPathWhat it does
POST/inquiry/policyByPlatePlate-based policy lookup (THIRD_PARTY). Body: nationalCode, plk1–plk4. Response is mapped to the old Tejarat format before being stored.
POST/inquiry/carByChassisTwo-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/personPersonal identity check. Body: nationalCode, birthDate (Jalali, NOT Gregorian).
POST/inquiry/shebaSheba / 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.

6 — SMS live

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.

Provider selection

Env varValueActive provider
SMS_PROVIDER (or SMS)kavenegar (default)Kavenegar — api.kavenegar.com
SMS_PROVIDER (or SMS)parsianParsian SMS Gateway — PARSIAN_SMS_URL

Kavenegar endpoints

Base URL: https://api.kavenegar.com/v1/{SMS_API_KEY}/

MethodPathWhen used
POSTsms/send.jsonPlain-text messages (e.g. key-based notification texts stored in sms_texts collection).
GETverify/lookup.jsonAll template-based messages (OTPs, invite links, expert notifications). Params: receptor, token[, token2, token3, token10], template.

Parsian SMS Gateway

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).

SMS templates in use

Template nameTriggerTokens
AUTH_SMS_TEMPLATE (env)User / actor OTP login, forget-password, party OTPstoken = OTP code
yara724-invite-linkSecond party receives blame invite link via SMStoken = publicId, token2 = link
yara-field-expert-linkField expert sends link to a partytoken = file type, token2 = expert surname, token3 = link
yara-blame-agreementNotify party that the other side agreed to the expert verdicttoken = publicId, token2 = link
yara-claim-linkDamaged party notified to open claim flow after blame is completetoken = publicId, token2 = link
yara-expert-lockExpert locks a blame or claim filetoken = "تصادف"/"خسارت", token2 = publicId, token3 = expert surname
yara-resend-documentsExpert requests document resendtoken = file kind, token2 = publicId, token3 = link
yara-signatureParty notified to sign the expert's damage assessmenttoken = file kind, token2 = publicId, token3 = expert surname, token10 = link
yara-fanavaran-claimRetained legacy template; automatic dispatch after the final Fanavaran stage is disabledtoken = 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.

7 — AI Service disabled (code present)

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.

Intended interface (when re-enabled)

MethodPathWhat it does
POST{AI_URL_V2}/auth/loginAuthenticate with username + password. Returns accessToken.
GET{AI_URL_V2}/auth/profileFetch apiKey.key needed as the gateway-api-key request header.
POST{AI_URL_V2}/services/car-damage/detector?version=ai-v7Submit 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.

8 — Car Pricing Service partially active

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.

Endpoints

MethodPathWhat it does
GET{CW_URL}price?akharinFetch car market prices from the "Akharin" source. Returns array of { carName, marketPrice }.
GET{CW_URL}price?hamrahFetch 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.

9 — Offline Inquiry internal / fallback

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.

How it works

AspectDetail
StorageMongoDB collection offline-inquiries. Documents contain clientKey, normalised plate fields, nationalCode, and the pre-built raw + mapped response to return.
Master switchsystem_settings.offlineInquiry.enabled — defaults to true. Toggle via PATCH /super-admin/system-settings/offline-inquiry.
Lookup orderNormalised plate (digits-only, Arabic→Persian) + national code + Fanavaran client key must all match. If found, returned immediately; no HTTP call is made.
ScopeOnly applies to plate-based block-inquiry (THIRD_PARTY). CAR_BODY inquiry (/badane) always hits the live API.
Live API flagsystem_settings.externalApis.sandHubUseLiveApi — when false (default), even if no offline seed matches, a built-in mock response is returned rather than calling Tejarat/ESG.

10 — Environment Variable Reference

All env vars across all integrations, grouped by service. Variables marked * are not present in .env.example.

Fanavaran

VariableDescription
FANAVARAN_CLIENTActive tenant profile key: parsian | tejaratno | moallem
INSURANCE_CORP_IDDisplay 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.

SandHub (legacy)

VariableDescription
SANHUB_BASE_URLBase URL for SandHub. Default: http://82.99.202.245:3027
SANHUB_URL_LOGINFull login URL (usually base + /user/login)
SANHUB_USERNAMESandHub login email
SANHUB_PASSWORDSandHub login password

Tejarat inquiry

VariableDescription
TEJARAT_INQUIRY_BASE_URLBase URL. Default: http://82.99.202.245:3027
TEJARAT_INQUIRY_EMAILLogin email
TEJARAT_INQUIRY_PASSWORDLogin password

ESG (CLIENT_ID=8 only)

VariableDescription
CLIENT_IDSet to 8 to activate the ESG inquiry provider for the Parsian tenant.
ESG_URLESG base URL. Default: http://192.168.20.22:8085 (internal network)
ESG_USERNAMEESG login username
ESG_PASSWORDESG login password

SMS

VariableDescription
SMS_PROVIDER (or SMS)kavenegar (default) or parsian
SMS_API_KEYKavenegar API key (required when provider = kavenegar)
AUTH_SMS_TEMPLATEKavenegar template name for OTP messages (e.g. yara-otp)
PARSIAN_SMS_URLParsian SMS Gateway base URL (required when provider = parsian)
PARSIAN_API_KEYParsian SMS X-PACKAGE-API-KEY header value
PARSIAN_BASIC_TOKENBase64-encoded credentials for Authorization: Basic … header
URLFrontend base URL — used to build all invite + claim links embedded in SMS messages

AI service

VariableDescription
AI_URL_V2AI gateway base URL. Default: https://ai-gw.ittalie.ir (unused — service is disabled)
AI_USERNAMEAI service login username (unused)
AI_PASSWORDAI service login password (unused)

Car pricing service

VariableDescription
CW_URL *Base URL for car market price API (e.g. https://…/). Not in .env.example. Price-drop silently skipped if unset.

General / app

VariableDescription
PORTHTTP port (default 3000). Used by the Fanavaran insurance-corp fallback to call its own local lookup endpoint.
CAPTCHA_ENABLEDtrue / false — enables/disables login CAPTCHA challenge. Internal, no external service.
EXP_CAPTCHA_TIMECAPTCHA challenge TTL in minutes.
EXP_OTP_TIMEOTP TTL in minutes.