ACORD standartları ve Sigorta API Entegrasyonu
Sigorta ekosistemi doğası gereği parçalıdır: sigorta şirketleri, reasürörler, komisyoncular, acenteler, bankalar (banka sigortacılığı), çevrimiçi karşılaştırıcılar, tedarikçiler hizmetlerin (atölyeler, hastaneler, avukatlar) — tüm bu aktörlerin veri alışverişinde bulunması gerekir verimli, doğru ve denetlenebilir bir şekilde. Paylaşılan standartlar olmadan her entegrasyon maliyeti ve riskleri yüksek özel bir proje haline gelir.
Uzlaşı (Kooperatif Yöneylem Araştırma ve Geliştirme Derneği) ve sigorta sektörü için veri standartlarını belirleyen küresel kuruluş 1970'den beri. Standartları — Politikaları, talepleri yönetmek için XML ve JSON mesajları, reasürans ve muhasebe düzenlemesi - baştan sona benimsenmiştir 36.000 kuruluş 100 ülkede. 2025'te ACORD i'yi piyasaya sürdü GRLC Nesil 2.0 Veri Standartları, reasürans standartlarının ve büyük ticari risklerin kapsamlı bir incelemesi yerel JSON desteği ve API öncelikli yönlendirme.
Bu kılavuz ACORD standartlarını somut kodlara çeviriyor: mesajların nasıl yapılandırılacağı Standartlarla uyumlu XML/JSON, özel sistemler arasında eşleme katmanının nasıl oluşturulacağı ve ACORD ve sektördeki en iyi uygulamalarla uyumlu RESTful API'lerin nasıl kullanıma sunulacağı.
Ne Öğreneceksiniz
- ACORD Standartlarına Genel Bakış: Farklı İş Kolları için XML ve JSON
- Politikalar ve talepler için ACORD mesajlarının yapısı
- Tescilli veri modelleri ve ACORD arasında eşleme
- Python/FastAPI'de ACORD uyumlu bir API ağ geçidinin uygulanması
- XML Şeması (XSD) ve JSON Şeması ile mesaj doğrulama
- Entegrasyon modelleri: noktadan noktaya, ESB, olay odaklı
- ACORD Sertifikasyonu: gereksinimler ve test süreci
ACORD Standartlarına Genel Bakış
ACORD, sigorta sektörünün her alanı için farklı standartlar yayınlamaktadır. Ana olanlar Sektörde çalışan bir geliştiricinin bilmesi gerekenler şunlardır:
Alanlara Göre ACORD Standartları
| Standart | Alan | Biçim | Notlar |
|---|---|---|---|
| ACORD XML (P&C) | Hasar: araba, mülk, sorumluluk | XML | Kuzey Amerika'daki en yaygın standart |
| AKOR GRLC | Reasürans ve büyük ticari riskler | XML + JSON (Gen 2.0) | API öncelikli yaklaşımla 2025'te yenilendi |
| ACORD EBOT | Muhasebe düzenlemesi (muhasebe ve ödeme) | XML | Gen 2.0 güncellemesi planlandı |
| ACORD ECOT | Talep hareketleri | XML | Gen 2.0 güncellemesi planlandı |
| ACORD CRP | Sözleşme, risk ve ön muhasebe | XML + JSON | Yeni GRLC Gen 2.0 standardı |
| ACORD Yaşam XML'i | Hayat ve sağlık dalı | XML | ACORD Standardı 103 ve ilgili |
ACORD XML Mesajının Yapısı
ACORD XML mesajının iyi tanımlanmış bir hiyerarşik yapısı vardır. Blokları anlama Temel bilgiler ve herhangi bir entegrasyonun ilk adımı. İşte örnek bir mesaj bir otomobil sigortası poliçesi için teklif istemek için (ACORD 165 Kişisel Otomobil Teklif Talebi).
<?xml version="1.0" encoding="UTF-8"?>
<ACORD>
<!-- Header obbligatorio: identifica mittente, destinatario, versione -->
<SignonRq>
<SignonTransport>
<SignonRoleCd>Customer</SignonRoleCd>
<CustId>
<SPName>it.federicocalo.insurtech</SPName>
<CustPermId>broker-001</CustPermId>
</CustId>
</SignonTransport>
<ClientDt>2025-03-10T14:30:00</ClientDt>
<CustLangPref>it-IT</CustLangPref>
<ClientApp>
<Org>ACME InsurTech</Org>
<Name>PolicyPortal</Name>
<Version>2.1.0</Version>
</ClientApp>
</SignonRq>
<!-- Corpo del messaggio: richiesta di quotazione auto -->
<InsuranceSvcRq>
<RqUID>a1b2c3d4-e5f6-7890-abcd-ef1234567890</RqUID>
<SPName>it.federicocalo.insurtech</SPName>
<RqDt>2025-03-10</RqDt>
<PersAutoPolicyQuoteInqRq>
<RqUID>quote-2025-001234</RqUID>
<CurCd>EUR</CurCd>
<QuoteInfo>
<EffectiveDt>2025-04-01</EffectiveDt>
<ExpirationDt>2026-03-31</ExpirationDt>
</QuoteInfo>
<!-- Informazioni polizza -->
<PersPolicy>
<LOBCd>AUTO</LOBCd>
<ContractTerm>
<DurationPeriod>
<NumUnits>12</NumUnits>
<UnitMeasurementCd>MON</UnitMeasurementCd>
</DurationPeriod>
</ContractTerm>
</PersPolicy>
<!-- Assicurato principale -->
<InsuredOrPrincipal>
<GeneralPartyInfo>
<NameInfo>
<PersonName>
<GivenName>Mario</GivenName>
<Surname>Rossi</Surname>
</PersonName>
</NameInfo>
<Addr>
<Addr1>Via Roma 1</Addr1>
<City>Milano</City>
<StateProvCd>MI</StateProvCd>
<PostalCode>20121</PostalCode>
<CountryCd>IT</CountryCd>
</Addr>
</GeneralPartyInfo>
<InsuredOrPrincipalInfo>
<InsuredOrPrincipalRoleCd>Insured</InsuredOrPrincipalRoleCd>
<PersonInfo>
<BirthDt>1985-06-15</BirthDt>
<GenderCd>M</GenderCd>
<LicensedDt>2003-09-20</LicensedDt>
</PersonInfo>
</InsuredOrPrincipalInfo>
</InsuredOrPrincipal>
<!-- Veicolo -->
<PersAutoLineBusiness>
<PersVeh>
<VehNumber>1</VehNumber>
<ModelYr>2022</ModelYr>
<Manufacturer>FIAT</Manufacturer>
<Model>Tipo</Model>
<VehIdentificationNumber>ZFA35600006G12345</VehIdentificationNumber>
<EstimatedAnnualDistance>
<NumUnits>15000</NumUnits>
<UnitMeasurementCd>KMT</UnitMeasurementCd>
</EstimatedAnnualDistance>
<Coverage>
<CoverageCd>RCA</CoverageCd>
<CoverageDesc>Responsabilità Civile Auto</CoverageDesc>
</Coverage>
<Coverage>
<CoverageCd>KASKO</CoverageCd>
<CoverageDesc>Kasko Completo</CoverageDesc>
<Deductible>
<FormatInteger>500</FormatInteger>
<CurCd>EUR</CurCd>
</Deductible>
</Coverage>
</PersVeh>
</PersAutoLineBusiness>
</PersAutoPolicyQuoteInqRq>
</InsuranceSvcRq>
</ACORD>
JSON Formatında ACORD Mesajı (GRLC Gen 2.0)
Yeni GRLC Generation 2.0 (2025) standardı, yerel JSON temsilini sunar. reasürans mesajları ve büyük ticari riskler. JSON formatı daha hafiftir, modern mimarilerde işlenmesi daha kolaydır (REST API, olay akışı) ve evet OpenAPI ve JSON Schema gibi araçlarla yerel olarak entegre olur.
{
"acordVersion": "GRLC-2.0",
"messageId": "msg-2025-03-10-001234",
"messageType": "RiskSubmission",
"timestamp": "2025-03-10T14:30:00Z",
"sender": {
"organizationId": "broker-ACME-001",
"name": "ACME Broker SpA",
"role": "Broker"
},
"receiver": {
"organizationId": "insurer-XYZ-IT",
"name": "XYZ Assicurazioni SpA",
"role": "Insurer"
},
"risk": {
"riskId": "RISK-2025-IT-00456",
"lineOfBusiness": "Marine",
"inceptionDate": "2025-04-01",
"expiryDate": "2026-03-31",
"currency": "EUR",
"insuredObject": {
"type": "Vessel",
"name": "MV Adriatico",
"imoNumber": "9876543",
"vesselType": "BulkCarrier",
"grossTonnage": 45000,
"yearBuilt": 2018,
"flag": "IT",
"value": 25000000.00
},
"coverages": [
{
"coverageType": "HullAndMachinery",
"insuredValue": 25000000.00,
"deductible": 100000.00,
"conditions": "Institute Cargo Clauses (A)"
},
{
"coverageType": "ProtectionAndIndemnity",
"limit": 100000000.00,
"conditions": "Standard P&I Terms"
}
],
"locations": [
{
"locationType": "PrimaryPort",
"portName": "Genova",
"country": "IT",
"coordinates": {
"latitude": 44.4071,
"longitude": 8.9342
}
}
]
},
"submission": {
"requestedCapacity": 10000000.00,
"requestedShare": 0.4,
"expiringPremium": 185000.00,
"desiredPremium": 190000.00,
"submissionNotes": "Rinnovo con storico sinistri pulito. Certificato classe 1A Lloyd's Register."
}
}
Eşleme Katmanı: Tescilli Modelden ACORD'a
Şirketin iç sistemleri nadiren doğrudan ACORD terminolojisini kullanır. Eşleme katmanı, tescilli veri modeli ile ACORD standardı arasında çeviri yapar, çift yönlü. Bu katmanın standartların versiyonları ile güncel tutulması gerekmektedir.
from dataclasses import dataclass
from datetime import date
from typing import Dict, List, Optional, Any
from enum import Enum
import xml.etree.ElementTree as ET
from xml.dom import minidom
import json
# ============ Modello dati INTERNO (proprietario) ============
class CoverageType(str, Enum):
LIABILITY = "RCA"
COMPREHENSIVE = "KASKO"
FIRE_THEFT = "INCENDIO_FURTO"
WINDSHIELD = "CRISTALLI"
DRIVER_ACCIDENT = "INFORTUNI_CONDUCENTE"
@dataclass
class InternalVehicle:
"""Modello veicolo del sistema interno."""
plate: str
vin: str
brand: str
model: str
year: int
engine_cc: int
fuel_type: str # benzina, diesel, elettrico, ibrido
annual_km: int
@dataclass
class InternalPolicyholder:
"""Modello assicurato del sistema interno."""
tax_code: str # codice fiscale
first_name: str
last_name: str
birth_date: date
gender: str # M/F
address: str
city: str
province: str
postal_code: str
license_date: date
claims_3yr: int
@dataclass
class InternalQuoteRequest:
"""Richiesta di quotazione nel formato del sistema interno."""
request_id: str
policyholder: InternalPolicyholder
vehicle: InternalVehicle
coverages: List[CoverageType]
effective_date: date
annual_mileage: int
garage_type: str # "privato", "condominiale", "pubblico"
# ============ Mapper ACORD ============
class ACORDMapper:
"""
Mappa i modelli dati interni ai messaggi ACORD XML/JSON.
Supporta:
- ACORD 165: Personal Auto Quote Request (XML)
- ACORD GRLC 2.0: Risk Submission (JSON)
"""
# Mapping codici interni -> codici ACORD standard
GENDER_MAP: Dict[str, str] = {
"M": "M", "F": "F", "ALTRO": "O", "NON_SPECIFICATO": "U"
}
FUEL_MAP: Dict[str, str] = {
"benzina": "GAS", "diesel": "DIE", "elettrico": "ELE",
"ibrido": "HYB", "gpl": "LPG", "metano": "CNG"
}
GARAGE_MAP: Dict[str, str] = {
"privato": "PRIV", "condominiale": "COND", "pubblico": "PUB",
"nessuno": "NONE"
}
COVERAGE_MAP: Dict[CoverageType, Dict[str, str]] = {
CoverageType.LIABILITY: {"code": "BI", "desc": "Bodily Injury Liability"},
CoverageType.COMPREHENSIVE: {"code": "COMP", "desc": "Comprehensive"},
CoverageType.FIRE_THEFT: {"code": "FT", "desc": "Fire and Theft"},
CoverageType.WINDSHIELD: {"code": "GLAS", "desc": "Glass Coverage"},
CoverageType.DRIVER_ACCIDENT: {"code": "PACC", "desc": "Personal Accident"},
}
def to_acord_xml(self, request: InternalQuoteRequest) -> str:
"""
Converte una richiesta interna nel formato ACORD 165 XML.
Returns: stringa XML formattata e pretty-printed.
"""
root = ET.Element("ACORD")
# Header
signon_rq = ET.SubElement(root, "SignonRq")
transport = ET.SubElement(signon_rq, "SignonTransport")
role = ET.SubElement(transport, "SignonRoleCd")
role.text = "Customer"
client_dt = ET.SubElement(signon_rq, "ClientDt")
client_dt.text = date.today().isoformat()
# Body
svc_rq = ET.SubElement(root, "InsuranceSvcRq")
rq_uid = ET.SubElement(svc_rq, "RqUID")
rq_uid.text = request.request_id
quote_rq = ET.SubElement(svc_rq, "PersAutoPolicyQuoteInqRq")
# Valuta
cur = ET.SubElement(quote_rq, "CurCd")
cur.text = "EUR"
# Info quotazione
quote_info = ET.SubElement(quote_rq, "QuoteInfo")
eff_dt = ET.SubElement(quote_info, "EffectiveDt")
eff_dt.text = request.effective_date.isoformat()
# Assicurato
self._add_insured_xml(quote_rq, request.policyholder)
# Linea Auto
auto_line = ET.SubElement(quote_rq, "PersAutoLineBusiness")
self._add_vehicle_xml(auto_line, request.vehicle, request.coverages)
# Pretty print
xml_string = ET.tostring(root, encoding="unicode")
dom = minidom.parseString(xml_string)
return dom.toprettyxml(indent=" ", encoding=None)
def _add_insured_xml(self, parent: ET.Element, ph: InternalPolicyholder) -> None:
"""Aggiunge il blocco InsuredOrPrincipal al messaggio XML."""
insured = ET.SubElement(parent, "InsuredOrPrincipal")
party_info = ET.SubElement(insured, "GeneralPartyInfo")
name_info = ET.SubElement(party_info, "NameInfo")
person_name = ET.SubElement(name_info, "PersonName")
given = ET.SubElement(person_name, "GivenName")
given.text = ph.first_name
surname = ET.SubElement(person_name, "Surname")
surname.text = ph.last_name
addr = ET.SubElement(party_info, "Addr")
addr1 = ET.SubElement(addr, "Addr1")
addr1.text = ph.address
city_el = ET.SubElement(addr, "City")
city_el.text = ph.city
state = ET.SubElement(addr, "StateProvCd")
state.text = ph.province
postal = ET.SubElement(addr, "PostalCode")
postal.text = ph.postal_code
country = ET.SubElement(addr, "CountryCd")
country.text = "IT"
insured_info = ET.SubElement(insured, "InsuredOrPrincipalInfo")
role = ET.SubElement(insured_info, "InsuredOrPrincipalRoleCd")
role.text = "Insured"
person_info = ET.SubElement(insured_info, "PersonInfo")
birth = ET.SubElement(person_info, "BirthDt")
birth.text = ph.birth_date.isoformat()
gender = ET.SubElement(person_info, "GenderCd")
gender.text = self.GENDER_MAP.get(ph.gender, "U")
licensed = ET.SubElement(person_info, "LicensedDt")
licensed.text = ph.license_date.isoformat()
def _add_vehicle_xml(
self, parent: ET.Element, vehicle: InternalVehicle, coverages: List[CoverageType]
) -> None:
"""Aggiunge il blocco PersVeh al messaggio XML."""
veh = ET.SubElement(parent, "PersVeh")
num = ET.SubElement(veh, "VehNumber")
num.text = "1"
yr = ET.SubElement(veh, "ModelYr")
yr.text = str(vehicle.year)
manuf = ET.SubElement(veh, "Manufacturer")
manuf.text = vehicle.brand
model_el = ET.SubElement(veh, "Model")
model_el.text = vehicle.model
vin = ET.SubElement(veh, "VehIdentificationNumber")
vin.text = vehicle.vin
ann_dist = ET.SubElement(veh, "EstimatedAnnualDistance")
num_units = ET.SubElement(ann_dist, "NumUnits")
num_units.text = str(vehicle.annual_km)
unit_meas = ET.SubElement(ann_dist, "UnitMeasurementCd")
unit_meas.text = "KMT"
for cov_type in coverages:
cov_data = self.COVERAGE_MAP.get(cov_type, {"code": "UNKN", "desc": "Unknown"})
cov_el = ET.SubElement(veh, "Coverage")
cov_cd = ET.SubElement(cov_el, "CoverageCd")
cov_cd.text = cov_data["code"]
cov_desc = ET.SubElement(cov_el, "CoverageDesc")
cov_desc.text = cov_data["desc"]
def to_acord_json(self, request: InternalQuoteRequest) -> Dict[str, Any]:
"""
Converte la richiesta interna nel formato ACORD JSON (semplificato).
Per GRLC 2.0: usa il formato esteso con risk/coverage/submission structure.
"""
ph = request.policyholder
veh = request.vehicle
return {
"acordVersion": "PersonalLines-1.0",
"messageId": request.request_id,
"messageType": "AutoQuoteRequest",
"timestamp": date.today().isoformat(),
"policyholder": {
"taxCode": ph.tax_code,
"firstName": ph.first_name,
"lastName": ph.last_name,
"birthDate": ph.birth_date.isoformat(),
"gender": self.GENDER_MAP.get(ph.gender, "U"),
"licenseDate": ph.license_date.isoformat(),
"claimsLast3Years": ph.claims_3yr,
"address": {
"street": ph.address,
"city": ph.city,
"province": ph.province,
"postalCode": ph.postal_code,
"countryCode": "IT",
},
},
"vehicle": {
"registrationPlate": veh.plate,
"vin": veh.vin,
"manufacturer": veh.brand,
"model": veh.model,
"year": veh.year,
"engineCC": veh.engine_cc,
"fuelType": self.FUEL_MAP.get(veh.fuel_type, "UNKN"),
"annualMileageKm": veh.annual_km,
},
"requestedCoverages": [
self.COVERAGE_MAP.get(c, {"code": "UNKN", "desc": "Unknown"})["code"]
for c in request.coverages
],
"effectiveDate": request.effective_date.isoformat(),
"currency": "EUR",
}
FastAPI ile ACORD API Ağ Geçidi
REST API aracılığıyla ACORD entegrasyon yeteneklerinin ortaya çıkarılması aracıların, acenteleri ve üçüncü taraf sistemleri yönetmek zorunda kalmadan sigorta sistemine bağlanmak XML standartlarının dahili olarak karmaşıklığı. Ağ geçidi, REST çağrılarını mesajlara çevirir ACORD ve tam tersi.
from fastapi import FastAPI, HTTPException, Depends, Header
from fastapi.responses import Response
from pydantic import BaseModel, Field
from typing import List, Optional
from datetime import date
import uuid
# ============ Pydantic Models per l'API REST ============
class VehicleRequest(BaseModel):
plate: str = Field(..., description="Targa del veicolo (formato italiano)")
vin: str = Field(..., min_length=17, max_length=17, description="VIN (17 caratteri)")
brand: str
model: str
year: int = Field(..., ge=1990, le=2030)
engine_cc: int = Field(..., ge=50, le=10000)
fuel_type: str = Field(..., description="benzina|diesel|elettrico|ibrido|gpl|metano")
annual_km: int = Field(..., ge=1000, le=150000)
class PolicyholderRequest(BaseModel):
tax_code: str = Field(..., min_length=16, max_length=16)
first_name: str
last_name: str
birth_date: date
gender: str = Field(..., pattern="^[MFO]$")
address: str
city: str
province: str = Field(..., min_length=2, max_length=2)
postal_code: str = Field(..., pattern=r"^\d{5}$")
license_date: date
claims_3yr: int = Field(default=0, ge=0, le=10)
class QuoteRequestBody(BaseModel):
policyholder: PolicyholderRequest
vehicle: VehicleRequest
coverages: List[str] = Field(..., min_items=1)
effective_date: date
response_format: str = Field(default="json", description="json|xml")
class QuoteResponse(BaseModel):
quote_id: str
request_id: str
status: str
estimated_annual_premium: Optional[float] = None
currency: str = "EUR"
valid_until: Optional[date] = None
breakdown: Optional[dict] = None
acord_message_id: Optional[str] = None
# ============ FastAPI Application ============
app = FastAPI(
title="Insurance ACORD API Gateway",
description="REST API layer per integrazione ACORD con sistemi assicurativi",
version="2.1.0",
docs_url="/api/v1/docs",
)
def verify_api_key(x_api_key: str = Header(...)) -> str:
"""Dependency per autenticazione API key."""
valid_keys = {"broker-001": "ACME Broker", "broker-002": "XYZ Agency"}
if x_api_key not in valid_keys:
raise HTTPException(status_code=401, detail="API key non valida")
return valid_keys[x_api_key]
@app.post(
"/api/v1/quotes/personal-auto",
response_model=QuoteResponse,
summary="Richiesta quotazione polizza auto personale",
description="Genera una quotazione per polizza RC auto e garanzie accessorie",
)
async def create_auto_quote(
request: QuoteRequestBody,
client_name: str = Depends(verify_api_key),
) -> QuoteResponse:
"""
Endpoint per richiesta di quotazione polizza auto.
Converte la richiesta REST nel formato ACORD appropriato,
invia al motore di pricing interno e restituisce la quotazione.
"""
request_id = str(uuid.uuid4())
# Converti il body REST nel modello interno
internal_request = _build_internal_request(request, request_id)
# Genera il messaggio ACORD
mapper = ACORDMapper()
if request.response_format == "xml":
acord_message = mapper.to_acord_xml(internal_request)
else:
acord_message = mapper.to_acord_json(internal_request)
# Invia al motore di pricing (simulato)
pricing_result = await _call_pricing_engine(internal_request)
return QuoteResponse(
quote_id=str(uuid.uuid4()),
request_id=request_id,
status="QUOTED",
estimated_annual_premium=pricing_result.get("annual_premium"),
currency="EUR",
valid_until=date.today().replace(month=date.today().month % 12 + 1) if date.today().month < 12 else date.today().replace(year=date.today().year + 1, month=1),
breakdown=pricing_result.get("breakdown"),
acord_message_id=f"ACORD-{request_id[:8]}",
)
@app.post(
"/api/v1/quotes/personal-auto/xml",
response_class=Response,
summary="Richiesta quotazione in formato ACORD XML",
)
async def create_auto_quote_xml(
request: QuoteRequestBody,
client_name: str = Depends(verify_api_key),
) -> Response:
"""Restituisce direttamente il messaggio ACORD XML della quotazione."""
request_id = str(uuid.uuid4())
internal_request = _build_internal_request(request, request_id)
mapper = ACORDMapper()
xml_content = mapper.to_acord_xml(internal_request)
return Response(content=xml_content, media_type="application/xml")
@app.get(
"/api/v1/health",
summary="Health check del gateway",
)
async def health_check() -> dict:
return {
"status": "healthy",
"version": "2.1.0",
"acord_version": "ACORD-XML-165 / GRLC-2.0",
}
def _build_internal_request(request: QuoteRequestBody, request_id: str) -> InternalQuoteRequest:
"""Converte il body REST nel modello interno."""
from datetime import timedelta
ph = request.policyholder
veh = request.vehicle
return InternalQuoteRequest(
request_id=request_id,
policyholder=InternalPolicyholder(
tax_code=ph.tax_code,
first_name=ph.first_name,
last_name=ph.last_name,
birth_date=ph.birth_date,
gender=ph.gender,
address=ph.address,
city=ph.city,
province=ph.province,
postal_code=ph.postal_code,
license_date=ph.license_date,
claims_3yr=ph.claims_3yr,
),
vehicle=InternalVehicle(
plate=veh.plate,
vin=veh.vin,
brand=veh.brand,
model=veh.model,
year=veh.year,
engine_cc=veh.engine_cc,
fuel_type=veh.fuel_type,
annual_km=veh.annual_km,
),
coverages=[CoverageType(c) for c in request.coverages if c in CoverageType.__members__.values()],
effective_date=request.effective_date,
annual_mileage=veh.annual_km,
garage_type="privato",
)
async def _call_pricing_engine(request: InternalQuoteRequest) -> dict:
"""Simulazione chiamata al motore di pricing interno."""
# In produzione: chiamata HTTP al microservizio di pricing
base_premium = 450.0
if request.policyholder.claims_3yr > 0:
base_premium *= (1 + request.policyholder.claims_3yr * 0.3)
if request.vehicle.year < 2015:
base_premium *= 1.1
return {
"annual_premium": round(base_premium, 2),
"breakdown": {
"RCA": round(base_premium * 0.65, 2),
"KASKO": round(base_premium * 0.25, 2),
"accessories": round(base_premium * 0.10, 2),
},
}
ACORD Mesajlarını JSON Şeması ile Doğrulama
Herhangi bir ACORD mesajını göndermeden veya işlemeden önce mesajın doğrulanması çok önemlidir. Yanlış biçimlendirilmiş bir mesaj, alıcı sistemin sessizce reddedilmesine veya daha kötüsü, politika veya talep verilerindeki hatalar. JSON Schema doğrulama için standart araçtır JSON ACORD mesajlarının sayısı.
import jsonschema
from jsonschema import validate, ValidationError
import json
from typing import Dict, List, Any, Tuple
# Schema JSON per una Quote Request ACORD semplificata
ACORD_AUTO_QUOTE_SCHEMA: Dict[str, Any] = {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://acord.org/schemas/personal-auto-quote-request-v1",
"title": "ACORD Personal Auto Quote Request",
"type": "object",
"required": [
"acordVersion", "messageId", "messageType", "timestamp",
"policyholder", "vehicle", "requestedCoverages", "effectiveDate", "currency"
],
"properties": {
"acordVersion": {
"type": "string",
"pattern": "^[A-Za-z0-9.-]+$"
},
"messageId": {"type": "string", "minLength": 1},
"messageType": {
"type": "string",
"enum": ["AutoQuoteRequest", "AutoPolicyIssue", "ClaimNotification"]
},
"timestamp": {"type": "string", "format": "date-time"},
"policyholder": {
"type": "object",
"required": ["firstName", "lastName", "birthDate", "gender", "licenseDate"],
"properties": {
"firstName": {"type": "string", "minLength": 1},
"lastName": {"type": "string", "minLength": 1},
"birthDate": {"type": "string", "format": "date"},
"gender": {"type": "string", "enum": ["M", "F", "O", "U"]},
"licenseDate": {"type": "string", "format": "date"},
"claimsLast3Years": {"type": "integer", "minimum": 0, "maximum": 10},
"address": {
"type": "object",
"required": ["city", "countryCode"],
"properties": {
"street": {"type": "string"},
"city": {"type": "string"},
"postalCode": {"type": "string"},
"countryCode": {"type": "string", "pattern": "^[A-Z]{2}$"},
}
}
}
},
"vehicle": {
"type": "object",
"required": ["manufacturer", "model", "year", "fuelType", "annualMileageKm"],
"properties": {
"vin": {"type": "string", "pattern": "^[A-HJ-NPR-Z0-9]{17}$"},
"manufacturer": {"type": "string"},
"model": {"type": "string"},
"year": {"type": "integer", "minimum": 1980, "maximum": 2030},
"fuelType": {
"type": "string",
"enum": ["GAS", "DIE", "ELE", "HYB", "LPG", "CNG", "UNKN"]
},
"annualMileageKm": {"type": "integer", "minimum": 100, "maximum": 200000}
}
},
"requestedCoverages": {
"type": "array",
"items": {"type": "string"},
"minItems": 1
},
"effectiveDate": {"type": "string", "format": "date"},
"currency": {"type": "string", "pattern": "^[A-Z]{3}$"}
}
}
class ACORDMessageValidator:
"""Validatore di messaggi ACORD con JSON Schema."""
def __init__(self) -> None:
self.validator = jsonschema.Draft202012Validator(ACORD_AUTO_QUOTE_SCHEMA)
def validate(self, message: Dict) -> Tuple[bool, List[str]]:
"""
Valida un messaggio ACORD.
Returns:
(is_valid, error_messages): tuple con risultato e lista di errori
"""
errors: List[str] = []
try:
self.validator.validate(message)
return True, []
except ValidationError as e:
# Raccogli tutti gli errori, non solo il primo
for error in self.validator.iter_errors(message):
path = " -> ".join(str(p) for p in error.absolute_path)
errors.append(f"Campo '{path}': {error.message}")
return False, errors
def validate_strict(self, message_json: str) -> Tuple[bool, List[str]]:
"""Valida da stringa JSON con parsing incluso."""
try:
message = json.loads(message_json)
except json.JSONDecodeError as e:
return False, [f"JSON malformato: {e}"]
return self.validate(message)
# Esempio di utilizzo
if __name__ == "__main__":
validator = ACORDMessageValidator()
valid_message = {
"acordVersion": "PersonalLines-1.0",
"messageId": "msg-001",
"messageType": "AutoQuoteRequest",
"timestamp": "2025-03-10T14:30:00Z",
"policyholder": {
"firstName": "Mario",
"lastName": "Rossi",
"birthDate": "1985-06-15",
"gender": "M",
"licenseDate": "2003-09-20",
"address": {"city": "Milano", "countryCode": "IT"},
},
"vehicle": {
"manufacturer": "Fiat",
"model": "Tipo",
"year": 2022,
"fuelType": "GAS",
"annualMileageKm": 15000,
},
"requestedCoverages": ["BI", "COMP"],
"effectiveDate": "2025-04-01",
"currency": "EUR",
}
is_valid, errors = validator.validate(valid_message)
print(f"Valido: {is_valid}, Errori: {errors}")
# Output: Valido: True, Errori: []
Sigorta Entegrasyon Modeli
Entegrasyon modelinin seçimi mesajların hacmine ve gerekli gecikme süresine bağlıdır ve ekosistemin karmaşıklığı. Sigorta sektöründeki ana modeller şunlardır:
Entegrasyon Modellerinin Karşılaştırılması
| Desenler | Ne zaman kullanılmalı? | Avantajları | Dezavantajları |
|---|---|---|---|
| Noktadan Noktaya API | Az sayıda entegrasyon, düşük hacim | Basitlik, düşük gecikme | Ölçeklenmeyen, sıkı bağlantı |
| ESB (Kurumsal Hizmet Veri Yolu) | Birçok heterojen sistem, büyük şirketler | Merkezi orkestrasyon, dönüşümler | Tek başarısızlık noktası, karmaşıklık |
| Olay Odaklı (Kafka) | Yüksek hacimli, gerçek zamanlı, mikro hizmetler | Ölçeklenebilirlik, ayrıştırma, denetim takibi | Operasyonel karmaşıklık, nihai tutarlılık |
| API Ağ Geçidi + Adaptör | Modern, bulut tabanlı InsurTech | Standardizasyon, versiyonlama, güvenlik | Ek gecikme, darboğaz olarak ağ geçidi |
En İyi Uygulamalar ve Anti-kalıplar
ACORD Entegrasyonu için En İyi Uygulamalar
- Sürüm mesajları: mesaja her zaman ACORD standart versiyonunu ekleyin (örn. "acordVersion": "GRLC-2.0"); Geriye dönük uyumluluk kritik öneme sahiptir
- Zorunlu idempotence: Yinelenen mesajların yinelenen işlemler oluşturmamasını sağlamak için RqUID'yi (benzersiz kimlik isteği) kullanır
- Göndermeden önce geçerlidir: İletiyi göndermeden önce her zaman gönderen tarafında XSD/JSON Şeması ile doğrulayın; alıcının hatasına bağlı kalmayın
- Tam mesaj günlüğü: Gönderilen ve alınan tüm ACORD mesajlarının bir kopyasını en az 10 yıl süreyle saklayın (sigorta düzenlemeleri için zorunludur)
- Üstel geri çekilmeyle zaman aşımı ve yeniden deneme: sigorta entegrasyonları geçici arızalarla başa çıkmalıdır; sürünün gürlemesini önlemek için titreşimle yeniden denemeyi uygulayın
Kaçınılması Gereken Anti-örüntüler
- Sabit kodlanmış eşlemeler: ACORD kod haritalarını uygulama kodunuza sabit olarak kodlamayın; bunları dağıtıma gerek kalmadan sürümlendirilmiş ve güncellenebilir yapılandırma dosyalarında tutun
- Yanıt kodlarını dikkate almayın: ACORD standartlaştırılmış yanıt mesajları sağlar; Yalnızca HTTP kodlarını değil, her zaman hata kodlarını (MsgStatusCd) dikkate alın
- BSE'deki dönüşümler: veri dönüşümlerini ESB'de değil uygulama katmanında tutun; ESB bir veri işlemcisi değil, bir yönlendirici olmalıdır
- Yeni sistemler için ACORD XML'i kullanın: yeni entegrasyonlar için ACORD JSON'u (GRLC 2.0) veya alıcı şirketin ACORD üzerinde sunduğu bir REST API'yi tercih edin
Sonuçlar ve Sonraki Adımlar
ACORD standartları sigortanın birlikte çalışabilirliğinin ortak dilidir. Onları anlayın ve bunların nasıl doğru şekilde uygulanacağını bilmek her geliştirici için temel bir beceridir InsurTech alanında hem şirket tarafında hem de komisyoncu veya toplayıcı tarafında çalışan kişiler.
2025'in GRLC Generation 2.0, oyunun kurallarını değiştiriyor: yerel JSON desteği ve API öncelikli yaklaşım nihayet ACORD standartlarını geliştiriciler için erişilebilir hale getiriyor REST ve JSON'a alışkın olan modern kullanıcılar, giriş engelini önemli ölçüde azaltır.
Serinin bir sonraki ve son makalesi bu konuyu ele alıyor. Uyumluluk Mühendisliği: Solvency II ve UFRS 17 — veri altyapısı ve raporlama ardışık düzenlerinin nasıl oluşturulacağı Sigorta sektörünün en zorlu düzenleyici yükümlülüklerini karşılamak için gerekli.
InsurTech Mühendislik Serisi
- 01 - Geliştiriciler için Sigorta Alanı: Ürünler, Aktörler ve Veri Modelleri
- 02 - Bulutta Yerel Politika Yönetimi: API Öncelikli Mimari
- 03 - Telematik İşlem Hattı: Geniş Ölçekte UBI Veri İşleme
- 04 - Yapay Zeka Sigortacılığı: Özellik Mühendisliği ve Risk Puanlaması
- 05 - Talep Otomasyonu: Bilgisayarla Görme ve NLP
- 06 - Dolandırıcılık Tespiti: Grafik Analizi ve Davranışsal Sinyal
- 07 - ACORD Standardı ve Sigorta API Entegrasyonu (bu makale)
- 08 - Uyumluluk Mühendisliği: Solvency II ve UFRS 17







