पासपोर्ट की फ़ोटो वह सबसे संवेदनशील फ़ाइल है जो ज़्यादातर ऐप्स को कभी मिलती है। उसमें एक चेहरा, पूरा नाम, जन्मतिथि और दस्तावेज़ संख्या होती है, और यह कहीं और खाता खोलने के लिए काफ़ी है। फिर भी कई अपलोड फ़्लो में यह तस्वीर पाँच-छह बार कॉपी हो जाती है, इससे पहले कि कोई पूछे कि क्या इसका मौजूद रहना ज़रूरी भी है।

यह लेख इस सवाल को इंजीनियरिंग की नज़र से देखता है। यह बताता है कि GDPR डेटा रखने के बारे में क्या कहता है, उन जगहों से गुज़रता है जहाँ पासपोर्ट की इमेज चुपचाप जमा होती जाती है, और एक पैटर्न बताता है जिसे हम पढ़ें, लौटाएँ, भूल जाएँ कहते हैं: इमेज पढ़ी जाती है, नतीजा लौटाया जाता है, और तस्वीर का कुछ भी नहीं रखा जाता। आख़िर में वह हिस्सा है जो आपकी ज़िम्मेदारी बना रहता है, क्योंकि कुछ कारोबारों के लिए कॉपी रखना अनिवार्य है, और जुलाई 2027 से EU एक नए क़ानून में यह साफ़-साफ़ लिखता है।

यह doc.cheap का ब्लॉग है, जो एक पासपोर्ट और आईडी OCR API है और पहचान दस्तावेज़ पढ़कर नतीजा JSON में लौटाता है। प्रोडक्ट वाले हिस्सों को इसी बात को ध्यान में रखकर पढ़ें।

GDPR असल में क्या माँगता है

GDPR यह नहीं कहता कि "पासपोर्ट की इमेज कभी सहेजें ही नहीं"। वह कुछ ज़्यादा काम की बात कहता है: जो चाहिए वही रखें, जितनी देर चाहिए उतनी देर, उससे ज़्यादा नहीं। Reg. (EU) 2016/679 का Art. 5(1) सिद्धांत तय करता है। उनमें से दो सिद्धांत डिज़ाइन का ज़्यादातर हिस्सा तय कर देते हैं।

सिद्धांत Art. 5(1) का पाठ (अंग्रेज़ी संस्करण) इमेज के लिए इसका मतलब
डेटा न्यूनीकरण, बिंदु (c) "adequate, relevant and limited to what is necessary in relation to the purposes for which they are processed" अगर आपकी प्रक्रिया को नाम, जन्मतिथि और दस्तावेज़ संख्या चाहिए, तो इन्हें पढ़ लेने के बाद तस्वीर शायद ज़रूरी न रहे।
भंडारण सीमा, बिंदु (e) "kept in a form which permits identification of data subjects for no longer than is necessary for the purposes for which the personal data are processed" हर कॉपी की एक समाप्ति तिथि होनी चाहिए, और "हम उसे हटाने तक कभी पहुँचे ही नहीं" कोई समाप्ति तिथि नहीं है।

Art. 5(2) जवाबदेही जोड़ता है: आपको यह दिखा पाना चाहिए कि आप इन सिद्धांतों का पालन करते हैं। और Art. 25(1), यानी डिज़ाइन से ही डेटा सुरक्षा, "appropriate technical and organisational measures, such as pseudonymisation, which are designed to implement data-protection principles, such as data minimisation, in an effective manner" माँगता है, यानी छद्मनामकरण जैसे उपयुक्त तकनीकी और संगठनात्मक उपाय, जो डेटा न्यूनीकरण जैसे सिद्धांतों को असरदार ढंग से लागू करने के लिए बनाए गए हों।

साथ पढ़ने पर ये एक क़ानूनी सवाल को इंजीनियरिंग के सवाल में बदल देते हैं। किसी इमेज की जितनी कम कॉपियाँ होंगी, उतनी कम जगहें होंगी जिनका आपको वर्णन करना, सुरक्षित करना, बैकअप लेना और आख़िरकार ख़ाली करना होगा। जो कॉपी कभी लिखी ही नहीं गई, वही अकेली ऐसी कॉपी है जिसे इनमें से किसी काम की ज़रूरत नहीं।

पासपोर्ट की इमेज कहाँ पहुँच जाती हैं

ज़्यादातर टीमें इमेज को जानबूझकर एक जगह सहेजती हैं। मुश्किल वे जगहें हैं जिन्हें किसी ने चुना ही नहीं। यह रही एक सूची, जिसे अपने फ़्लो से मिलाकर देखें।

जगह इमेज वहाँ कैसे पहुँचती है
अपलोड बकेट क्लाइंट पहले ऑब्जेक्ट स्टोरेज पर अपलोड करता है, और बैकएंड उसे वहाँ से पढ़ता है। ऑब्जेक्ट अनुरोध ख़त्म होने के बाद भी बना रहता है।
अनुरोध लॉग एक लॉगिंग middleware अनुरोधों की बॉडी लिखता है, और base64 इमेज भी एक अनुरोध बॉडी ही है।
त्रुटि रिपोर्ट एक exception tracker विफल हुए अनुरोध का payload साथ जोड़ देता है।
क्यू और रीट्राई किसी जॉब का संदेश इमेज लेकर चलता है, और एक dead-letter क्यू विफल संदेशों को हफ़्तों तक रखती है।
बैकअप और स्नैपशॉट उस दिन लिया गया डेटाबेस या डिस्क का स्नैपशॉट उससे पहले लिखी हर इमेज रखता है, पंक्ति हटाए जाने के बहुत बाद तक।
सपोर्ट टिकट कोई उपयोगकर्ता फ़ोटो दोबारा ईमेल कर देता है "क्योंकि अपलोड काम नहीं किया"।
एनालिटिक्स और सेशन रीप्ले कोई टूल पेज रिकॉर्ड करता है, चुनी गई फ़ाइल के प्रीव्यू समेत।
OCR प्रदाता दस्तावेज़ पढ़ने वाली सेवा अपने रिटेंशन नियमों के तहत अपनी कॉपी रखती है।

आख़िरी पंक्ति वही है जिस पर आपका नियंत्रण सबसे कम है। अपने लॉग आप ठीक कर सकते हैं। किसी वेंडर के पास रखी कॉपी उस वेंडर की सेटिंग्स से चलती है, और आपको पूछना होगा कि वे क्या हैं।

पैटर्न: पढ़ें, लौटाएँ, भूल जाएँ

पैटर्न को कहना आसान है। इमेज केवल मेमोरी में रहती है, एक अनुरोध की अवधि तक। अनुरोध से जो बाहर निकलता है वह पढ़ा गया डेटा है: निकाले गए मान। तस्वीर बाहर निकलती ही नहीं।

  1. इमेज सीधे पहचान के लिए भेजें। बीच में कोई अपलोड बकेट नहीं। अगर बड़ी फ़ाइलों के लिए बकेट से बचना संभव न हो, तो ऑब्जेक्ट की उम्र कुछ मिनट रखें और कॉल लौटते ही उसे हटा दें।
  2. रिस्पॉन्स से जो चाहिए, तुरंत इस्तेमाल करें। धारक की फ़ोटो जैसे क्रॉप केवल उसी रिस्पॉन्स में होते हैं। अगर आपका फ़्लो सेल्फ़ी की तुलना पोर्ट्रेट से करता है, तो यह अभी करें।
  3. तस्वीर नहीं, पढ़ा गया डेटा रखें। अपनी प्रक्रिया के लिए ज़रूरी फ़ील्ड अपने रिटेंशन नियम के तहत सहेजें। जन्मतिथि और दस्तावेज़ संख्या भी व्यक्तिगत डेटा ही हैं, इसलिए उनकी भी समाप्ति तिथि होती है।
  4. इमेज को लॉग और त्रुटि रिपोर्ट से बाहर रखें। बॉडी को सीमा पर, एक ही जगह हटाएँ, बजाय इस भरोसे के कि हर कॉल करने वाला यह याद रखेगा।
  5. नतीजा लिखकर रखें। ऊपर की तालिका की हर जगह के लिए नोट करें कि इमेज वहाँ पहुँच सकती है या नहीं, और क्यों नहीं। यही नोट वह जवाबदेही है जो Art. 5(2) माँगता है।

हमारा API इमेज के साथ क्या करता है

doc.cheap इसी सवाल को कैसे संभालता है, जैसा उसके डेटा रिटेंशन और प्राइवेसी पेज पर बताया गया है:

  • इमेज कभी सहेजी नहीं जाती। वह अनुरोध के दौरान मेमोरी में रहती है, पहचान इंजन को सौंपी जाती है, और रिस्पॉन्स लिखे जाते ही ख़त्म हो जाती है। कोई डिस्क, ऑब्जेक्ट स्टोर या लॉग उसे नहीं पाता।
  • क्रॉप भी नहीं सहेजे जाते। दस्तावेज़ का क्रॉप, धारक की फ़ोटो और हस्ताक्षर उसी कॉल के रिस्पॉन्स में लौटते हैं जिसने उन्हें बनाया। बाद में GET /v1/scans/{id} से दोबारा पढ़े गए स्कैन में हर इमेज स्लॉट null होता है।
  • जो रखा जा सकता है वह पढ़ा गया डेटा है, और सिर्फ़ उतनी अवधि के लिए जितनी आप माँगें। retain_hours विकल्प इसे हर अनुरोध के लिए तय करता है, 0 से 8760 घंटे (एक साल) तक। स्पष्ट रूप से दिया गया मान हमेशा खाते की सेटिंग पर भारी पड़ता है।
  • retain_hours: 0 कुछ भी नहीं लिखता। ऐसी पंक्ति नहीं जो तुरंत समाप्त हो जाए: कोई पंक्ति ही नहीं। साफ़ करने को कुछ नहीं, बैकअप में कुछ नहीं, एक्सपोर्ट करने को कुछ नहीं। फिर भी स्कैन एक स्कैन के रूप में गिना जाता है।
  • बाक़ी को खाते की डिफ़ॉल्ट सेटिंग संभालती है। जब कोई अनुरोध अवधि नहीं बताता, तो खाते की अपनी हिस्ट्री सेटिंग लागू होती है: 24 घंटे, 7 दिन, 1 महीना या 1 साल। नए खाते 1 साल से शुरू होते हैं, ताकि डैशबोर्ड में हिस्ट्री दिखे। सेटिंग छोटी करने पर वह पहले से सहेजी पंक्तियों पर भी लागू होती है, हर पंक्ति अपने बनने के समय से गिनी जाती है।
  • सहेजी गई पंक्ति एक छोटी तस्वीर रखती है: एक थंबनेल, जिसकी सबसे लंबी भुजा अधिकतम 96 px और आकार अधिकतम 16 KiB है, डैशबोर्ड के ऑपरेशन लॉग में दिखाया जाता है ताकि पंक्ति पहचानी जा सके। इसे API से पढ़ा नहीं जा सकता। पंक्ति हटने पर थंबनेल भी हट जाता है।
  • एक स्कैन को पहले भी हटाया जा सकता है। एक live कुंजी DELETE /v1/scans/{id} भेजती है, जो नतीजा, हिस्ट्री की पंक्ति और थंबनेल हटा देता है। यह अंतिम है।

हिस्ट्री रिटेंशन नियंत्रित करें गाइड सेटिंग्स को क़दम-दर-क़दम समझाती है, और हम डेटा कैसे प्रोसेस करते हैं वाला हमारा पेज सारांश देता है।

यह रहा Python में requests के साथ ज़ीरो रिटेंशन वाला एक कॉल। सार्वजनिक सैंडबॉक्स कुंजी sk_sandbox_public दस्तावेज़ में छपी है और इसके लिए साइनअप की ज़रूरत नहीं: यह हर पते पर कुल 10 मुफ़्त पहचाने गए दस्तावेज़ देती है, और एक घंटे में अधिकतम 10 अनुरोध। ज़ीरो रिटेंशन आपके अपने खाते की सेटिंग है, इसलिए इसके लिए आपकी लाइव कुंजी चाहिए। सार्वजनिक सैंडबॉक्स कोई खाता नहीं है: यह हर स्कैन का रिकॉर्ड, उसकी छोटी तस्वीर के साथ, सेवा के अपने लॉग में रखता है, इसलिए इसे केवल परीक्षण छवि भेजें, कभी असली दस्तावेज़ नहीं।

import base64
import uuid

import requests

API = "https://api.doc.cheap/v1/scans"
KEY = "sk_sandbox_public"  # प्रोडक्शन में आपकी अपनी live कुंजी


def read_and_forget(path):
    with open(path, "rb") as f:
        image = base64.b64encode(f.read()).decode("ascii")
    response = requests.post(
        API,
        headers={
            "Authorization": f"Bearer {KEY}",
            "Idempotency-Key": str(uuid.uuid4()),
        },
        json={
            "image": image,
            # 0: लाइव कुंजी के साथ, API की ओर इस स्कैन के बारे में कुछ भी नहीं लिखा जाता।
            # False: पोर्ट्रेट क्रॉप नहीं, क्योंकि यह फ़्लो उसका इस्तेमाल नहीं करता।
            "options": {"retain_hours": 0, "return_portrait": False},
        },
        timeout=30,
    )
    response.raise_for_status()
    scan = response.json()
    del image  # कॉल लौटते ही लोकल कॉपी हट जाती है
    if scan["meta"]["status"] != "recognized":
        return None
    # अपनी प्रक्रिया के लिए ज़रूरी डेटा अपने रिटेंशन नियम के तहत रखें।
    return {
        "scan_id": scan["meta"]["id"],
        "document_number": scan["document"]["number"],
        "expiry_date": scan["document"]["expiry_date"],
        "birth_date": scan["holder"]["birth_date"],
        "mrz_status": scan["mrz"]["status"],
    }

ज़ीरो रिटेंशन की एक क़ीमत है, जिसे चौंकाने से पहले जान लेना अच्छा है। आम तौर पर Idempotency-Key से रीट्राई पहला नतीजा लौटा देता है। retain_hours: 0 के साथ लौटाने के लिए कोई सहेजा हुआ नतीजा नहीं होता, इसलिए 24 घंटे तक उसी कुंजी वाला रीट्राई दोबारा जवाब पाने के बजाय HTTP 409 और कोड idempotency_replay_unavailable के साथ ठुकरा दिया जाता है। इस जवाब को "पहला कॉल सफल रहा" मानें और जो नतीजा आपके पास पहले से है, उसी का इस्तेमाल करें।

स्कैन को दोबारा पढ़ना डिज़ाइन का दूसरा पहलू दिखाता है। सैंडबॉक्स कुंजी कुछ भी दोबारा नहीं पढ़ती, id चाहे जो हो। हमने यह अनुरोध 5 अक्टूबर 2026 को sk_sandbox_public से भेजा:

curl https://api.doc.cheap/v1/scans/<SCAN_ID> \
  -H "Authorization: Bearer sk_sandbox_public"

जवाब HTTP 404 के साथ आया (संदेश छोटा किया गया है):

{
  "error": {
    "code": "not_found",
    "message": "No scan with id …",
    "docs_url": "https://doc.cheap/docs/errors/not_found"
  }
}

live कुंजी को भी retain_hours: 0 से किए गए स्कैन के लिए, और अवधि बीत जाने के बाद किसी भी स्कैन के लिए, वही 404 मिलता है। अगर आप इस मुद्दे पर सेवाओं की तुलना कर रहे हैं, तो पासपोर्ट OCR API तुलना शुरुआत के लिए अच्छी जगह है; हर सेवा से पूछें कि इमेज कहाँ जाती है, सिर्फ़ यह नहीं कि वह क्या लौटाती है।

जो आपकी ज़िम्मेदारी बनी रहती है

पढ़ें, लौटाएँ, भूल जाएँ API की ओर की कॉपियाँ हटा देता है। यह तय नहीं करता कि आपके कारोबार को क्या रखना है। कुछ कारोबारों के लिए जवाब है "एक कॉपी", और यह क़ानून कहता है।

EU का नया एंटी-मनी-लॉन्ड्रिंग क़ानून, Reg. (EU) 2024/1624, 10 जुलाई 2027 से लागू होता है। Art. 90 कहता है: "It shall apply from 10 July 2027, except in relation to obliged entities referred to in Article 3, points (3)(n) and (o), to which it shall apply from 10 July 2029." यानी यह 10 जुलाई 2027 से लागू होता है, सिवाय Art. 3 के बिंदु (3)(n) और (o) में बताई गई बाध्य संस्थाओं के, जिन पर यह 10 जुलाई 2029 से लागू होगा। रिकॉर्ड रिटेंशन पर इसका Art. 77 बैंकों और अन्य वित्तीय कंपनियों जैसी बाध्य संस्थाओं से यह रखवाता है:

"a copy of the documents and information obtained in the performance of customer due diligence pursuant to Chapter III, including information obtained through electronic identification means;"

दूसरे शब्दों में, ग्राहक की उचित जाँच (customer due diligence) के दौरान मिले दस्तावेज़ों और जानकारी की एक कॉपी, जिसमें इलेक्ट्रॉनिक पहचान साधनों से मिली जानकारी भी शामिल है।

Art. 77(3) अवधि तय करता है: रिकॉर्ड "retained for a period of 5 years commencing on the date of the termination of the business relationship", यानी कारोबारी संबंध ख़त्म होने की तारीख़ से 5 साल तक रखे जाते हैं, और फिर "obliged entities shall delete personal data upon expiry of the five-year period", यानी पाँच साल पूरे होने पर बाध्य संस्थाएँ व्यक्तिगत डेटा हटा देती हैं। Art. 77(2) कुछ शर्तों के तहत कॉपियों की जगह "a retention of the references to such information", यानी उस जानकारी के संदर्भ रखने की अनुमति देता है।

तो अगर आप एक बाध्य संस्था हैं, तो API पर ज़ीरो रिटेंशन रिकॉर्ड रखने का आपका कर्तव्य ख़त्म नहीं करता। यह बदलता है कि रिकॉर्ड कहाँ रहता है। आपका अपना स्टोर एकमात्र कॉपी बन जाता है, और ऊपर बताया गया भंडारण सीमा का सिद्धांत उस पर भी लागू होता है: संबंध ख़त्म होने के पाँच साल बाद वह हट जाता है। डिज़ाइन का काम उस स्टोर को सोच-समझकर बनाना है: एक जगह, एक ज़िम्मेदार व्यक्ति, एन्क्रिप्शन, एक्सेस कंट्रोल और हटाने वाला एक जॉब, न कि ऊपर की तालिका वाला अनचाहा ढेर।

अगर आप बाध्य संस्था नहीं हैं, तो पहले सीधा सवाल पूछें: फ़ील्ड पढ़ लिए जाने के बाद क्या आपकी प्रक्रिया में किसी चीज़ को तस्वीर की ज़रूरत है? अक्सर ईमानदार जवाब है: नहीं।

एक चेकलिस्ट

  • "इमेज कहाँ पहुँच जाती हैं" तालिका की हर जगह आपके फ़्लो से मिलाकर जाँची गई है।
  • इमेज सीधे पहचान के लिए जाती है, या कुछ मिनट की उम्र वाले बकेट से होकर।
  • क्रॉप रिस्पॉन्स हैंडलर के अंदर इस्तेमाल होते हैं और कहीं लिखे नहीं जाते।
  • OCR कॉल अपना रिटेंशन सोच-समझकर तय करता है, retain_hours: 0 जब कुछ भी दोबारा पढ़ने की ज़रूरत न हो।
  • रीट्राई 409 idempotency_replay_unavailable जवाब को संभालते हैं।
  • लॉग और त्रुटि रिपोर्ट अनुरोध बॉडी को एक ही सीमा पर हटाते हैं।
  • आप जो फ़ील्ड रखते हैं उनकी समाप्ति तिथि है, और कोई चीज़ उन्हें हटाती है।
  • अगर कोई क़ानून कॉपी माँगता है, तो वह अपनी हटाने की तारीख़ वाले एक सोचे-समझे स्टोर में रहती है।

यह एक इंजीनियरिंग सारांश है, क़ानूनी सलाह नहीं। अगर आपको कोई ऐसी जगह मिले जहाँ से इमेज लीक हो सकती है और जो इस लेख में छूट गई है, तो admin@doc.cheap पर लिखें।

आपसे एक सवाल: आपको आख़िरी बार किसी पहचान दस्तावेज़ की ऐसी कॉपी कहाँ मिली थी जिसे कोई रखना नहीं चाहता था? नीचे टिप्पणियों में बताइए।