API DOKÜMANTASYONU

TurkPII API Referansı

TurkPII REST API'si için tam teknik referans. Tüm endpointler, JSON istek ve yanıt örnekleri ile tek sayfada.

Kurulum

TurkPII, Docker imajı olarak dağıtılır ve müşterinin kendi sunucusunda (on-premise) çalışır.

CPU runtime v1.1.1 Kurulumu

CPU kurulumu
docker pull bahadorr/turkpii:1.1.1

docker run --rm \
  -e LICENSE_KEY="your-license-key" \
  -p 8080:8080 \
  bahadorr/turkpii:1.1.1

Opsiyonel benchmark / log hacmi kurulumu

Erişim loglarını kapatmak istediğiniz test ve benchmark koşullarında aşağıdaki opsiyonel değişkeni kullanabilirsiniz:

Benchmark kurulumu
docker run --rm \
  -e LICENSE_KEY="your-license-key" \
  -e TURKPII_ACCESS_LOG=0 \
  -p 8080:8080 \
  bahadorr/turkpii:1.1.1

Mevcut public/customer release yalnızca CPU runtime ile yayınlanır. Performans sonuçları donanım, payload, concurrency ve lisans limitlerine göre değişir.

Deployment notu: TurkPII offline lisans doğrulamasını destekler. Docker imajı ve lisans bilgisi önceden sağlandığında, müşteri politikalarına bağlı olarak internet erişimi olmayan veya kısıtlı ağ ortamlarında çalıştırılabilir. Ağ davranışı müşteri tarafından firewall, IDS/IPS, SIEM, proxy ve network monitoring araçlarıyla doğrulanabilir.

Servisi Kontrol Etme

Sağlık kontrolü
curl http://localhost:8080/health

Gerekli Ortam Değişkenleri

DeğişkenZorunluAçıklama
LICENSE_KEYEvetMüşteri için üretilmiş lisans anahtarı. Geçerli lisans olmadan servis başlamaz.
TURKPII_ACCESS_LOGHayırErişim loglarını kapatmak için 0 olarak ayarlanabilir. Özellikle benchmark veya yüksek log hacmi istemeyen ortamlarda kullanışlıdır.

Genel Bakış

TurkPII için müşteri odaklı HTTP API.

Aşağıdaki örnekler v1.1.1 CPU runtime release esas alınarak güncellendi. Doğrulama kanıtı: 372/372 pass, 0 fail. Yerel CPU benchmark kanıtı, 6-core Intel Core i5 test host uzerinde: 88792 successful request, 0 fail, best RPS 799.76.

TurkPII, konteyner başlangıcında geçerli bir LICENSE_KEY gerektirir. Benchmark sayıları evrensel üretim garantisi değildir; sonuçlar donanım, payload, concurrency ve lisans limitlerine göre değişir.

Endpointler

POST /mask, verilen metindeki kişisel verileri (PII) tespit edip maskeler.

POST /unmask, token yer tutucularından maskelenmiş metni yeniden oluşturur.

GET /health, servis durumunu döndürür.

GET /license/info, lisansa ait güvenli meta verileri ve çalışma zamanı durumunu döndürür.

Format

Belgelenen tüm istek ve yanıt örnekleri JSON formatındadır.

Örnek isteği tam olarak kopyalamak için her kod bloğundaki kopyala butonunu kullanın.

Aşağıdaki tablolar, kaynak dokümandaki alan adlarını, türleri ve açıklamaları eksiksiz korur.

POST

/mask

Verilen metindeki kişisel verileri (PII) tespit edip maskeler.

İstek

POST /mask istek.json
{
  "text": "Ahmet Yılmaz, TC: 10203040506, tel: 0532 123 45 67",
  "options": {
    "detect": ["TC", "IBAN", "CREDIT_CARD", "PASSPORT", "VIN", "IP", "PORT", "PHONE", "EMAIL", "NAME", "PERSON", "ADDRESS", "TAX", "PLATE", "AGE", "AGE_RANGE"],
    "ner": true,
    "min_confidence": 0.0,
    "mode": "pseudonymize"
  }
}

İstek alanları:

AlanTürZorunluAçıklama
textstringEvetPII taraması yapılacak metin.
debugbooleanHayırtrue olduğunda yanıta, eşleşme ayrıntılarını adli inceleme düzeyinde veren bir explanations dizisi eklenir. Canlı ortamda test edilmiştir.
mask_policystringHayırMaskeleme politikası. Mevcut kodda desteklenen değerler valid_only, valid_and_suspicious, everything, all_structured_like ve compliance_strict değerleridir. Varsayılan değer valid_and_suspicious olarak kullanılır.
optionsobjectHayırTespit seçenekleri.
options.detectstring[]HayırAranacak PII tipleri. Geçerli değerler TC, IBAN, CREDIT_CARD, PASSPORT, VIN, IP, PORT, PHONE, EMAIL, NAME, PERSON, ADDRESS, TAX, PLATE, AGE ve AGE_RANGE değerleridir. NAME ile PERSON aynı istekte birlikte kullanılmamalıdır; AGE ile AGE_RANGE de birlikte kullanılmamalıdır.
options.nerbooleanHayırTürkçe isim tespiti için NER katmanını etkinleştirir. Çalışan lisanslı serviste bu alan atlanırsa ner_enabled değeri false kalır; true olarak ayarlanırsa lisans ve çalışma zamanı izin verdiğinde NER etkinleşir. Canlı ortamda test edilmiştir.
options.min_confidencenumberHayırMinimum güven eşiği. 0.0 değeri, güven skoruna göre filtreleme yapılmayacağı anlamına gelir.
options.modestringHayırMaskeleme modu. pseudonymize varsayılandır ve geri döndürülebilir tokens haritası üretir. anonymize geri döndürülemezdir ve token map döndürmez.

Mutual exclusion kuralı: NAME ile PERSON birlikte istenmemelidir. Aynı şekilde AGE ile AGE_RANGE de aynı istekte birlikte gönderilmemelidir.

Yanıt

Çalışan servisten alınmış canlı örnek:

POST /mask yanıt.json
{
  "masked": "{{NAME_1}}, TC: {{TC_1}}, tel: {{PHONE_1}}",
  "tokens": {
    "NAME_1": "Ahmet Yılmaz",
    "PHONE_1": "0532 123 45 67",
    "TC_1": "10203040506"
  },
  "detected": [
    {
      "type": "NAME",
      "family": "Person",
      "semantic_class": "person",
      "parser_context": "natural",
      "assertion_scope": "sentence_scope",
      "profile_used": "TurkishNativeProfile",
      "primary_profile": "TurkishNativeProfile",
      "contributing_profiles": [
        "TurkishNativeProfile"
      ],
      "policy_used": "valid_and_suspicious",
      "masking_reason": "valid or suspicious entity under valid_and_suspicious policy",
      "compliance_relevance": "medium",
      "alert_severity": "low",
      "value": "Ahmet Yılmaz",
      "raw_value": "Ahmet Yılmaz",
      "normalized_value": "Ahmet Yılmaz",
      "canonical_value": "Ahmet Yılmaz",
      "start": 0,
      "end": 13,
      "confidence": 0.7034956909518111,
      "structural_score": 0.45,
      "validation_score": 0.22,
      "context_score": 0,
      "pattern_score": 0.06,
      "neighbor_score": 0,
      "semantic_score": 0,
      "relationship_score": 0,
      "source": "ner.name",
      "validation_state": "valid",
      "reason": "ner_person",
      "scores": {
        "structural": 0.45,
        "validation": 0.22,
        "context": 0,
        "pattern": 0.06,
        "neighbor": 0,
        "semantic": 0,
        "relationship": 0,
        "raw_total": 0.73,
        "normalized": 0.7034956909518111
      }
    },
    {
      "type": "TC",
      "family": "StructuredIdentifiers",
      "semantic_class": "identifier",
      "parser_context": "natural",
      "field_anchor": "tc",
      "ownership_reason": "label strongly indicates TC",
      "assertion_scope": "sentence_scope",
      "profile_used": "TurkishNativeProfile",
      "primary_profile": "TurkishNativeProfile",
      "contributing_profiles": [
        "TurkishNativeProfile"
      ],
      "policy_used": "valid_and_suspicious",
      "masking_reason": "valid or suspicious entity under valid_and_suspicious policy",
      "compliance_relevance": "high",
      "alert_severity": "medium",
      "value": "10203040506",
      "raw_value": "10203040506",
      "normalized_value": "10203040506",
      "canonical_value": "10203040506",
      "start": 19,
      "end": 30,
      "confidence": 0.7231218051243897,
      "structural_score": 0.44,
      "validation_score": -0.24,
      "context_score": 0.16,
      "pattern_score": 0.06,
      "neighbor_score": 0.05,
      "semantic_score": 0.28,
      "relationship_score": 0,
      "source": "regex.tc",
      "validation_state": "suspicious",
      "reason": "checksum10_failed",
      "scores": {
        "structural": 0.44,
        "validation": -0.24,
        "context": 0.16,
        "pattern": 0.06,
        "neighbor": 0.05,
        "semantic": 0.28,
        "relationship": 0,
        "raw_total": 0.75,
        "normalized": 0.7231218051243897
      }
    },
    {
      "type": "PHONE",
      "family": "ContactInfo",
      "semantic_class": "contact_info",
      "parser_context": "natural",
      "field_anchor": "tel",
      "assertion_scope": "sentence_scope",
      "profile_used": "TurkishNativeProfile",
      "primary_profile": "TurkishNativeProfile",
      "contributing_profiles": [
        "TurkishNativeProfile"
      ],
      "policy_used": "valid_and_suspicious",
      "masking_reason": "valid or suspicious entity under valid_and_suspicious policy",
      "compliance_relevance": "high",
      "alert_severity": "high",
      "value": "0532 123 45 67",
      "raw_value": "0532 123 45 67",
      "normalized_value": "0532 123 45 67",
      "canonical_value": "5321234567",
      "start": 37,
      "end": 51,
      "confidence": 0.8009113398157163,
      "structural_score": 0.42,
      "validation_score": 0.3,
      "context_score": 0,
      "pattern_score": 0.07,
      "neighbor_score": 0.05,
      "semantic_score": 0,
      "relationship_score": 0,
      "source": "regex.phone",
      "validation_state": "valid",
      "reason": "ok",
      "scores": {
        "structural": 0.42,
        "validation": 0.3,
        "context": 0,
        "pattern": 0.07,
        "neighbor": 0.05,
        "semantic": 0,
        "relationship": 0,
        "raw_total": 0.8400000000000001,
        "normalized": 0.8009113398157163
      }
    }
  ],
  "summary": {
    "requested_types": [
      "TC",
      "PHONE",
      "EMAIL",
      "IBAN",
      "NAME",
      "ADDRESS",
      "TAX",
      "PLATE"
    ],
    "ner_enabled": true,
    "detected_context": "natural",
    "total_detected": 3,
    "by_type": {
      "NAME": 1,
      "PHONE": 1,
      "TC": 1
    }
  }
}

Yanıt alanları:

AlanTürAçıklama
maskedstringSeçilen tespitlerin {{TOKEN}} yer tutucularıyla değiştirildiği özgün metin.
tokensobjectPseudonymize modunda token adı ile özgün değer arasındaki eşleme döner. Anonymize modunda bu nesne bulunmayabilir veya boş olabilir.
detectedarrayİstek için döndürülen kabul edilmiş tespitlerin listesi.
detected[].typestringTC, IBAN, CREDIT_CARD, PASSPORT, VIN, IP, PORT, PHONE, EMAIL, NAME, PERSON, ADDRESS, TAX, PLATE, AGE veya AGE_RANGE gibi PII kategorisi.
detected[].familystringPerson, StructuredIdentifiers veya ContactInfo gibi daha geniş sınıflandırma ailesi.
detected[].semantic_classstringperson, identifier veya contact_info gibi anlamsal etiket.
detected[].parser_contextstringGirdiden çıkarılan bağlam; örneğin natural, json, xml, sql, logfmt, markdown, csv, chat, yaml veya ocr.
detected[].field_anchorstringTanınan yakın alan etiketi; örneğin tc veya tel.
detected[].valuestringDışarı aktarılan tespit değeri.
detected[].raw_valuestringMevcutsa, özgün istek metninden alınan tam alt dize.
detected[].normalized_valuestringSunum amacıyla kullanılan normalize edilmiş biçim.
detected[].canonical_valuestringKarşılaştırma veya tekilleştirme için kullanılan kanonikleştirilmiş biçim.
detected[].start / detected[].endnumberÖzgün UTF-8 istek metnindeki byte ofsetleri.
detected[].confidencenumber0.0 ile 1.0 arasında güven skoru.
detected[].sourcestringregex.tc, regex.phone veya ner.name gibi tespit kaynağı.
detected[].validation_statestringvalid veya suspicious gibi doğrulama sonucu.
detected[].reasonstringTanıyıcı veya doğrulayıcıdan gelen kısa gerekçe; örneğin ok, ner_person veya checksum10_failed.
detected[].structural_score ve ilgili skor alanlarınumberNihai güven skoruna katkı veren skor bileşenleri.
detected[].scoresobjectNormalize toplamı ve bileşen skorlarını içeren skor kırılım nesnesi.
summaryobjectİstek düzeyindeki özet bilgisi.
summary.requested_typesstring[]İstek ayrıştırıldıktan sonra geçerli olan PII tipleri.
summary.ner_enabledbooleanBu istek için NER yolunun gerçekten çalıştırılıp çalıştırılmadığını gösterir; çalıştıysa true olur.
summary.detected_contextstringİstek için seçilen bağlam; örneğin natural veya ocr.
summary.total_detectednumberdetected içinde döndürülen tespit sayısı.
summary.by_typeobjectPII tipine göre adet dağılımı.
explanationsarrayYalnızca debug=true olduğunda bulunur. Her adayın nasıl değerlendirildiğini anlatan açıklama nesnelerini içerir. Canlı ortamda test edilmiştir.

debug ve mask_policy

Her iki alan da mevcut handler içinde uygulanmıştır ve canlı ortamda test edilmiştir.

debug=true olduğunda yanıt bir explanations dizisi içerir:

POST /mask debug yanıt.json
{
  "masked": "TC kimlik numaram 34567890146.",
  "tokens": {},
  "detected": [
    {
      "type": "TC",
      "validation_state": "suspicious",
      "reason": "checksum10_failed"
    }
  ],
  "summary": {
    "requested_types": ["TC"],
    "ner_enabled": false,
    "detected_context": "natural",
    "total_detected": 1,
    "by_type": {
      "TC": 1
    }
  },
  "explanations": [
    {
      "entity": "TC",
      "validation_state": "suspicious",
      "matched_by": "regex.tc",
      "masking_applied": false,
      "pipeline": [
        "regex.tc matched",
        "bounded candidate",
        "TCValidator suspicious: checksum10_failed",
        "mask policy skipped masking"
      ]
    }
  ]
}

mask_policy, bir tespitin döndürülüp döndürülmeyeceğini değil, neyin maskeleneceğini değiştirir. Şüpheli bir TC adayı için canlı ortamda gözlenen davranış aşağıdadır:

Varsayılan politika (valid_and_suspicious), şüpheli adayı maskeler:

POST /mask varsayılan politika.json
{
  "masked": "TC kimlik numaram {{TC_1}}.",
  "tokens": {
    "TC_1": "34567890146"
  },
  "detected": [
    {
      "type": "TC",
      "validation_state": "suspicious",
      "reason": "checksum10_failed"
    }
  ]
}

valid_only, aynı şüpheli tespiti maskelemeden bırakır; ancak yine de detected içinde döndürür:

POST /mask valid_only.json
{
  "masked": "TC kimlik numaram 34567890146.",
  "tokens": {},
  "detected": [
    {
      "type": "TC",
      "validation_state": "suspicious",
      "reason": "checksum10_failed"
    }
  ]
}

Lisans kısıtlarında görülebilen özet alanları

Mevcut kurumsal test lisansı belgelenen tüm tipleri ve bağlamları izinli hale getirdiği için, aşağıdaki özet alanları kodda doğrulandı ancak bu oturumda canlı bir istekle tetiklenmedi:

- summary.unlicensed_types_skipped

- summary.ner_requested_but_not_licensed

- summary.context_downgraded_to

- summary.reason

Mevcut kod yolundan alınan örnek şekil aşağıdaki gibidir:

POST /mask lisans özeti.json
{
  "summary": {
    "requested_types": ["NAME", "EMAIL"],
    "ner_enabled": false,
    "ner_requested_but_not_licensed": true,
    "unlicensed_types_skipped": ["EMAIL"],
    "detected_context": "ocr",
    "context_downgraded_to": "natural",
    "reason": "ocr not included in license"
  }
}
POST

/unmask

/mask tarafından döndürülen token eşlemesini kullanarak maskelenmiş metni yeniden oluşturur.

İstek

POST /unmask istek.json
{
  "masked": "{{NAME_1}} toplantıya katıldı",
  "tokens": {
    "NAME_1": "Ahmet Yılmaz"
  }
}

İstek alanları:

AlanTürZorunluAçıklama
maskedstringEvet{{TOKEN}} yer tutucuları içeren maskelenmiş metin.
tokensobjectHayır/mask tarafından döndürülen token eşlemesi. Alan gönderilmezse veya boş bırakılırsa yanıt yer tutucuları olduğu gibi bırakır. Canlı ortamda test edilmiştir.

Yanıt

Çalışan servisten alınmış canlı örnek:

POST /unmask yanıt.json
{
  "text": "Ahmet Yılmaz toplantıya katıldı"
}

Yanıt alanları:

AlanTürAçıklama
textstringYer tutucular değiştirildikten sonra yeniden oluşturulan metin.
GET

/health

Temel servis durumunu döndürür.

İstek

Bu endpoint bir istek gövdesi kabul etmez.

İstek alanları:

AlanTürZorunluAçıklama
Yok--İstek gövdesi yoktur.

Yanıt

Çalışan servisten alınmış canlı örnek:

GET /health yanıt.json
{
  "status": "ok",
  "version": "1.0.2"
}

Yanıt alanları:

AlanTürAçıklama
statusstringServis durumu. Gözlenen değer: ok.
versionstringUygulama sürüm bilgisi. Mevcut public/customer release örneğinde bu değer 1.0.2 olarak gösterilir.
GET

/license/info

Ham lisans token'ını açığa çıkarmadan güvenli lisans meta verilerini ve çalışma zamanı durumunu döndürür.

İstek

Bu endpoint bir istek gövdesi kabul etmez.

İstek alanları:

AlanTürZorunluAçıklama
Yok--İstek gövdesi yoktur.

Yanıt

Çalışan servisten alınmış canlı örnek:

GET /license/info yanıt.json
{
  "customer_name": "Evaluation Customer",
  "days_remaining": 30,
  "expires_at": "2026-08-12T00:00:00Z",
  "features": {
    "contexts": [
      "natural",
      "json",
      "xml",
      "sql",
      "logfmt",
      "markdown",
      "csv",
      "ocr",
      "chat",
      "yaml"
    ],
    "detect_types": [
      "TC",
      "IBAN",
      "CREDIT_CARD",
      "PASSPORT",
      "VIN",
      "IP",
      "PORT",
      "PHONE",
      "EMAIL",
      "NAME",
      "PERSON",
      "ADDRESS",
      "TAX",
      "PLATE",
      "AGE",
      "AGE_RANGE"
    ],
    "ner": true,
    "ocr": true,
    "anonymize": true,
    "pseudonymize": true
  },
  "limits": {
    "max_req_per_day": 50000,
    "max_req_per_sec": 25
  },
  "runtime": {
    "mode": "cpu",
    "ner_available": true
  },
  "status": "valid",
  "tier": "evaluation"
}

Yanıt alanları:

AlanTürAçıklama
customer_namestringAktif lisanstaki müşteri adı.
tierstringLisans seviyesi; örneğin starter, professional veya enterprise.
expires_atstringRFC 3339 formatındaki lisans bitiş zamanı.
days_remainingnumberİstek anında lisans bitimine kalan tam gün sayısı.
statusstringEndpoint tarafından döndürülen güncel lisans durumu. Canlı ortamda gözlenen değer valid olmuştur. Mevcut handler kodu, 30 gün veya daha az süre kaldığında ayrıca expiring_soon da döndürebilir.
featuresobjectAktif lisanstaki özellik bayrakları ve izin listeleri.
features.nerbooleanLisansın NER tabanlı isim tespitine izin verip vermediğini gösterir.
features.ocrbooleanLisansın OCR farkındalıklı işlemeye izin verip vermediğini gösterir.
features.anonymize / features.pseudonymizebooleanLisansın ilgili maskeleme modlarına izin verip vermediğini gösterir.
features.contextsstring[]Aktif lisansın izin verdiği bağlamlar.
features.detect_typesstring[]Aktif lisansın izin verdiği PII tipleri.
limitsobjectAktif lisanstaki oran limiti bilgileri.
limits.max_req_per_secnumberİzin verilen saniye başına maksimum istek sayısı.
limits.max_req_per_daynumber or nullİsteğe bağlı günlük maksimum istek sayısı. null değeri günlük limit tanımlanmadığını gösterir.
runtimeobjectÇalışan sürecin canlı çalışma zamanı durumu.
runtime.modestringMevcut public/customer release için çalışma zamanı modu cpu olarak dokümante edilir.
runtime.ner_availablebooleanNER modelinin başlangıçta başarıyla yüklenip yüklenmediğini ve isteklere hazır olup olmadığını gösterir; hazırsa true olur.

runtime.mode ve runtime.ner_available, çalışma zamanı durumunu yansıtır. Mevcut release CPU-only olarak yayınlanır.

Hatalar

Kaynak referanstan alınan hata örnekleri, endpoint bölümleriyle aynı dokümantasyon düzeni kullanılarak aşağıda sunulmuştur.

Geçersiz veya eksik lisans (başlangıç düzeyi)

Bu, istek başına dönen bir HTTP hatası değildir. LICENSE_KEY eksik veya geçersizse konteyner başlayamaz ve bir log mesajıyla çıkar.

LICENSE_KEY olmadan imaj çalıştırılarak alınmış canlı örnek:

başlangıç hatası.json
{"time":"2026-07-05T14:24:18.75247706Z","level":"ERROR","msg":"application init failed","error":"[LICENSE] invalid license: LICENSE_KEY is required"}

Bozuk istek gövdesi

Bozuk JSON ile POST /mask üzerinde canlı ortamda test edilmiştir.

HTTP durumu: 400 Bad Request

bozuk gövde yanıt.json
{
  "error": "invalid request body"
}

Aynı istek ayrıştırma yolu POST /unmask için de kullanılır.

Bilinmeyen detect türü

Geçersiz bir detect türü gönderildiğinde servis geçerli tür listesini de döndürür.

HTTP durumu: 400 Bad Request

bilinmeyen detect türü yanıt.json
{
  "error": "unknown PII type: NATIONAL_ID",
  "valid_types": [
    "TC",
    "IBAN",
    "CREDIT_CARD",
    "PASSPORT",
    "VIN",
    "IP",
    "PORT",
    "PHONE",
    "EMAIL",
    "NAME",
    "PERSON",
    "ADDRESS",
    "TAX",
    "PLATE",
    "AGE",
    "AGE_RANGE"
  ]
}