> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# लेन-देन निगरानी webhook: KYT इवेंट और हस्ताक्षर

> Didit के webhook इवेंट: लेन-देन ट्रिगर, payload संरचना और HMAC हस्ताक्षर सत्यापन। हर महीने 1,000 लेन-देन शामिल, फिर प्रति लेन-देन $0.001।

## इवेंट कैटलॉग

लेन-देन निगरानी तीन webhook इवेंट भेजता है, जो मिलकर पूरे लेन-देन के जीवनचक्र को कवर करते हैं, और एक वैकल्पिक चौथा इवेंट, जो नए नियम सुझावों की घोषणा करता है। ये सभी Didit के मानक webhook इंफ़्रास्ट्रक्चर पर चलते हैं और HMAC-SHA256 साइनिंग का उपयोग करते हैं। गंतव्यों, साइनिंग और परीक्षण के लिए [webhook संदर्भ](https://docs.didit.me/hi/integration/webhooks.md) देखें।

| इवेंट | कब फ़ायर होता है |
| - | - |
| `transaction.created` | एक नया लेन-देन बना है और प्रारंभिक मूल्यांकन पूरा हो गया है। यह उन इनबाउंड लेन-देन के लिए भी फ़ायर होता है जो Didit आपके लिए बनाता है: जमा के बाद का [Travel Rule sunrise रजिस्ट्रेशन](https://docs.didit.me/hi/transaction-monitoring/travel-rule.md#डिपॉज़िट-के-बाद-सनराइज़-पंजीकरण), इनबाउंड `TRP` पूछताछ, या किसी दूसरे Didit ग्राहक से `INTERNAL` रेल पर आया ट्रांसफ़र। |
| `transaction.status.updated` | लेन-देन का स्टेटस बदला है: विश्लेषक की समीक्षा से, रीमेडिएशन सत्र के समाधान से, या जब Travel Rule ट्रांज़िशन के बाद (उदाहरण के लिए वॉलेट के स्वामित्व का प्रमाण मिलने के बाद) Didit आपके नियमों का पुनः मूल्यांकन करता है और नियम कार्रवाई होती है। जब ट्रांसफ़र की समय-सीमा समाप्त होने पर आपका `timeout_outcome` लागू होता है, तो वह स्टेटस बदलाव यह इवेंट नहीं भेजता; इसके बजाय `EXPIRED` के साथ `travel_rule.status.updated` पर प्रतिक्रिया दें। |
| `travel_rule.status.updated` | एक [Travel Rule](https://docs.didit.me/hi/transaction-monitoring/travel-rule.md) एक्सचेंज ने ट्रांज़िशन किया है, आउटबाउंड और इनबाउंड दोनों ट्रांसफ़र पर (`INTERNAL` रेल का इनबाउंड लेग, sunrise रजिस्ट्रेशन, और इनबाउंड `TRP` पूछताछ), जिसमें थ्रेशोल्ड से नीचे वाला `NOT_APPLICABLE` ट्रांज़िशन भी शामिल है। Didit द्वारा बनाए गए लेन-देन के लिए, ये इवेंट उसके `transaction.created` के बाद भेजे जाते हैं। |
| `transaction_rule_suggestions.created` | एक नियम सुझाव रन ने आपके एप्लिकेशन के लिए नए नियम बदलाव प्रस्तावित किए। यह इवेंट तभी फ़ायर होता है जब आप नियम सुझाव की सूचना सेटिंग्स में **webhook** चालू करते हैं, और केवल उन रन के लिए जिन्होंने सुझाव बनाए हों। |

इकाई-स्तर के इवेंट (`user.status.updated`, `business.status.updated`) भी लेन-देन की गतिविधि के परिणामस्वरूप फ़ायर हो सकते हैं। देखें [इकाई webhook](https://docs.didit.me/hi/entities/webhooks.md)।

> [!NOTE]
>
> सैंडबॉक्स एप्लिकेशन `transaction.created` या `transaction.status.updated` webhooks नहीं भेजते। इन इवेंट्स का परीक्षण करने के लिए लाइव एप्लिकेशन का उपयोग करें, या कंसोल में **API & Webhooks → Try Webhook** का उपयोग करें। Travel Rule सैंडबॉक्स एक्सचेंज `environment: sandbox` के साथ `travel_rule.status.updated` webhooks भेजते हैं; लाइव जाने से पहले किसी एक्सचेंज का परीक्षण करने के लिए [Didit Test VASP](https://docs.didit.me/hi/transaction-monitoring/travel-rule-sandbox.md) का उपयोग करें।

## एनवेलप

Didit webhooks एक **फ़्लैट** बॉडी का उपयोग करते हैं: इवेंट फ़ील्ड्स एनवेलप फ़ील्ड्स के साथ सबसे ऊपरी स्तर पर रहते हैं, और कोई नेस्टेड `data` ऑब्जेक्ट नहीं होता।
हर डिलीवरी में ये फ़ील्ड्स होते हैं:

| फ़ील्ड | विवरण |
| - | - |
| `webhook_type` | इवेंट का नाम, जैसे `transaction.created`। |
| `event_id` | इवेंट का स्थिर पहचानकर्ता। पुनः प्रयास पर वही `event_id` दोबारा इस्तेमाल होता है, इसलिए डुप्लिकेट हटाने के लिए इसी का उपयोग करें। |
| `timestamp` / `created_at` | Unix epoch सेकंड, जब डिलीवरी भेजी गई। |
| `application_id` | वह एप्लिकेशन जिससे इवेंट संबंधित है। |
| `environment` | `live` या `sandbox`। |

## Payload के स्वरूप

### `transaction.created` और `transaction.status.updated`

दोनों इवेंट एक ही payload साझा करते हैं: एक संक्षिप्त सार, साथ में [`action_required`](https://docs.didit.me/hi/transaction-monitoring/sdk-transaction-submission#आवश्यक-उपयोगकर्ता-क्रियाएँ) ब्लॉक (या `null`):

```json theme={null}
{
  "webhook_type": "transaction.created",
  "event_id": "9c0c8b8a-1111-4222-9333-666666666666",
  "timestamp": 1774970000,
  "created_at": 1774970000,
  "application_id": "11111111-2222-3333-4444-555555555555",
  "environment": "live",
  "transaction_id": "9c7d2a30-...",
  "txn_id": "tx-0001",
  "status": "IN_REVIEW",
  "score": 65,
  "severity": "MEDIUM",
  "amount": "500.00",
  "currency": "EUR",
  "direction": "OUTBOUND",
  "action_required": null
}
```

Payload जानबूझकर हल्का रखा गया है। प्राप्त होने पर, पूरा रिकॉर्ड [`GET /v3/transactions/{transaction_id}/`](https://docs.didit.me/hi/management-api/transactions/get) से लाएँ। यह पूरा विवरण लौटाता है (पक्ष, `rule_runs`, `provider_results`, `travel_rule`, `cost_breakdown`, `decision_reason_code`), जिसमें स्टेटस बदलने का कारण भी शामिल है।

### `transaction_rule_suggestions.created`

यह इवेंट आपके नियम सेट के बारे में है, किसी एक लेन-देन के बारे में नहीं, इसलिए इसमें कोई लेन-देन ID नहीं होता।
इसमें कभी व्यक्तिगत डेटा नहीं होता: केवल सुझाव ID, प्रकार, शीर्षक और गिनती।

```json theme={null}
{
  "webhook_type": "transaction_rule_suggestions.created",
  "run_id": "5b2f0c1e-0000-0000-0000-000000000000",
  "open_count": 3,
  "suggestions": [
    {
      "suggestion_id": "9a7d3e22-0000-0000-0000-000000000000",
      "kind": "...",
      "title": "...",
      "rule_id": "...",
      "expected_impact": {}
    }
  ]
}
```

| फ़ील्ड | विवरण |
| - | - |
| `run_id` | वह सुझाव रन जिसने नए सुझाव बनाए। |
| `open_count` | इस रन के बाद एप्लिकेशन के लिए कितने सुझाव खुले हैं। |
| `suggestions[]` | इस रन द्वारा बनाए गए सुझाव, जिनमें से हर एक में `suggestion_id`, `kind`, `title`, `rule_id` (जब सुझाव नया नियम प्रस्तावित करता है तो null) और `expected_impact` होता है। |

सामान्य एनवेलप फ़ील्ड्स (`event_id`, `timestamp`, `application_id`, `environment`) बॉडी में जोड़े जाते हैं।

### `travel_rule.status.updated`

```json theme={null}
{
  "webhook_type": "travel_rule.status.updated",
  "event_id": "9c0c8b8a-1111-4222-9333-777777777777",
  "timestamp": 1774970000,
  "created_at": 1774970000,
  "application_id": "11111111-2222-3333-4444-555555555555",
  "environment": "live",
  "transaction_id": "22222222-3333-4444-5555-666666666666",
  "txn_id": "trv-2026-07-05-001",
  "travel_rule_status": "COMPLETED",
  "rail": "INTERNAL",
  "direction": "OUTBOUND"
}
```

यह हर ट्रांज़िशन पर भेजा जाता है, जिसमें `FINISHED`, `CANCELLED`, या `EXPIRED` में जाना भी शामिल है। एक बार ट्रांसफ़र इनमें से किसी स्थिति तक पहुँच जाए, तो उसके लिए आगे कोई Travel Rule इवेंट नहीं भेजा जाता।
कोई इवेंट पिछले इवेंट का स्टेटस दोहरा सकता है, उदाहरण के लिए जब दोबारा भेजने पर भी कोई रेल नहीं मिलती और ट्रांसफ़र `COUNTERPARTY_VASP_NOT_FOUND` पर ही रहता है।
`rail` इनमें से कोई एक होता है: `INTERNAL`, `TRP`, `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`, `EMAIL`, `TEST_VASP` (एक सैंडबॉक्स ट्रांसफ़र जिसका जवाब Didit Test VASP ने दिया, और जिसमें `test_vasp: true` और `test_vasp_scenario` भी होते हैं), या `""` जब तक कोई रेल नहीं चुनी गई हो (sunrise रजिस्ट्रेशन पर यह हमेशा `""` होता है)।

लेन-देन बनने पर, sunrise रजिस्ट्रेशन पर, इनबाउंड `TRP` पूछताछ पर, या `INTERNAL` रेल के इनबाउंड लेग पर, Didit पहले `transaction.created` भेजता है और उसके बाद लेन-देन के `travel_rule.status.updated` इवेंट।
उस चरण के दौरान कोई नियम लेन-देन का स्टेटस बदले, तो `transaction.status.updated` भी `transaction.created` से पहले भेजा जा सकता है।
जो sunrise रजिस्ट्रेशन उस डिपॉज़िट लेन-देन से जुड़ता है जिसे आप पहले ही बना चुके हैं, वह कोई `transaction.created` नहीं भेजता।
हर webhook अलग से डिलीवर और पुनः प्रयास किया जाता है, इसलिए दोबारा भेजा गया इवेंट बाद के इवेंट के बाद भी पहुँच सकता है। अपना हैंडलर `transaction_id` के आधार पर रखें, और जब भी वर्तमान स्थिति चाहिए हो, लेन-देन को दोबारा पढ़ें।

## स्टेटस के उदाहरण क्रम

### सीधी स्वीकृति

```
transaction.created (APPROVED, score 0)
```

### समीक्षा के बाद स्वीकृति

```
transaction.created (IN_REVIEW, score 65)
...analyst reviews...
transaction.status.updated (status: APPROVED)
```

### स्टेप-अप सत्यापन

```
transaction.created (AWAITING_USER, action_required.type: verification_session)
...user completes the biometric re-check...
transaction.status.updated (status: APPROVED, action_required: null)
```

### Travel Rule ट्रांसफ़र

```
transaction.created (APPROVED)
travel_rule.status.updated (AWAITING_COUNTERPARTY, rail: EMAIL)
...counterparty accepts the pickup...
travel_rule.status.updated (COMPLETED)
...you broadcast on-chain and PATCH the hash...
travel_rule.status.updated (FINISHED)
```

पहले दो इवेंट एक ही अनुरोध से आते हैं, और `transaction.created` पहले कतार में डाला जाता है। हर webhook अलग से डिलीवर होता है, इसलिए आगमन के क्रम पर भरोसा न करें।
TRUST के ज़रिए भेजे गए ट्रांसफ़र को कभी `COMPLETED` इवेंट नहीं मिलता, क्योंकि TRUST कुछ वापस नहीं भेजता जिसे Didit पढ़ सके। ट्रांसफ़र पुष्टि की समय-सीमा तक `AWAITING_COUNTERPARTY` पर रहता है और फिर `EXPIRED` पर समाप्त होता है। GTR, VerifyVASP और Sygna Bridge पर Didit नेटवर्क से प्रतिपक्ष का जवाब पढ़ता है, और इवेंट उसके बाद आता है।

## हस्ताक्षर सत्यापन

हर डिलीवरी को webhook गंतव्य के साझा सीक्रेट से HMAC-SHA256 से साइन किया जाता है, और उसके साथ तीन सिग्नेचर हेडर और एक टाइमस्टैम्प आता है:

| हेडर | स्कीम |
| - | - |
| `X-Signature-V2` | क्रमबद्ध कुंजियों और Unicode को संरक्षित रखते हुए कैनोनिकल JSON पर HMAC - **अनुशंसित**। |
| `X-Signature` | ASCII-escaped कैनोनिकल JSON पर HMAC (पुराना तरीका)। |
| `X-Signature-Simple` | `"{timestamp}:{session_id}:{status}:{webhook_type}"` पर HMAC - **अप्रचलित**। ट्रांज़ैक्शन पेलोड में कोई `session_id` नहीं होता, इसलिए यह `"{timestamp}::{status}:{webhook_type}"` पर साइन करता है: इससे न ट्रांज़ैक्शन की पहचान होती है, न उसकी राशि या स्कोर की। `travel_rule.status.updated` पेलोड में भी कोई `status` नहीं होता, इसलिए वह `"{timestamp}:::travel_rule.status.updated"` पर साइन करता है। सत्यापन `X-Signature-V2` से करें, और [अप्रचलन सूचना](https://docs.didit.me/hi/integration/webhooks.md#x-signature-simple-अप्रचलित-है) देखें। |
| `X-Timestamp` | Unix epoch सेकंड - 5 मिनट से पुरानी डिलीवरी अस्वीकार करें। |

```typescript theme={null}
import { createHmac, timingSafeEqual } from 'crypto';

function verify(rawBody: string, signatureV2: string, secret: string) {
  const canonical = JSON.stringify(
    Object.keys(JSON.parse(rawBody)).sort()
      .reduce((acc, k) => ({ ...acc, [k]: JSON.parse(rawBody)[k] }), {})
  );
  const expected = createHmac('sha256', secret).update(canonical).digest('hex');
  return timingSafeEqual(Buffer.from(signatureV2), Buffer.from(expected));
}
```

टाइमस्टैम्प की ताज़गी जाँच सहित पूरा सत्यापन कोड देखने के लिए [webhooks संदर्भ](https://docs.didit.me/hi/integration/webhooks.md#हस्ताक्षर-सत्यापन) देखें।

## पुनः प्रयास और डिलीवरी की गारंटी

* `5xx`, `404`, टाइमआउट, या कनेक्शन विफलता पर Didit **अधिकतम 2 बार** पुनः प्रयास करता है: पहली विफलता के लगभग 1 मिनट बाद, और उसके लगभग 4 मिनट बाद।
* पुनः प्रयास की गई डिलीवरी वही `event_id` इस्तेमाल करती हैं - अपने हैंडलर में उसी के आधार पर डुप्लिकेट की पहचान करें।
* अंतिम विफलता के बाद उस इवेंट की डिलीवरी रुक जाती है और आपके संगठन को सूचित किया जाता है; गंतव्यों को कभी चुपचाप निष्क्रिय नहीं किया जाता।
* `2xx` जल्दी लौटाएँ (पहले कतार में डालें, फिर प्रोसेस करें) - धीमे ACK से टाइमआउट और डुप्लिकेट डिलीवरी का जोखिम रहता है।

## आइडेम्पोटेंसी

हर webhook को ठीक एक बार प्रोसेस करने के लिए `event_id` को अपने डुप्लिकेट-जाँच स्टोर में सहेजें। जब आपका एंडपॉइंट धीरे ACK करता है या अनुरोध के बीच में डिस्कनेक्ट हो जाता है, तो उसी इवेंट की फिर से डिलीवरी हो सकती है।

## सब्सक्राइब करना

```bash theme={null}
curl -X POST https://verification.didit.me/v3/webhook/destinations/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "TM events",
    "url": "https://yourapp.com/webhooks/didit-tm",
    "subscribed_events": [
      "transaction.created",
      "transaction.status.updated",
      "travel_rule.status.updated"
    ]
  }'
```

## अगले चरण

> [card] [webhook संदर्भ](https://docs.didit.me/hi/integration/webhooks.md)
>
> गंतव्य, हस्ताक्षर, पुनः प्रयास।

> [card] [स्थितियाँ](https://docs.didit.me/hi/transaction-monitoring/statuses.md)
>
> पूरी स्थिति मशीन।

> [card] [इंटीग्रेशन गाइड](https://docs.didit.me/hi/transaction-monitoring/integration-guide.md)
>
> प्रोडक्शन में webhook को संभालना।


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.