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

# FATF Travel Rule API: VASP 규정 준수

> Didit의 FATF Travel Rule API로 VASP와 PSP 간 송금인·수취인 데이터를 교환하고 프로토콜 추적과 자동 해결을 지원합니다. 교환 건당 $0.02.

Didit는 가상자산 이체를 위해 FATF/EU 자금이동규칙(Transfer of Funds Regulation, TFR)에 따른 송금인-수취인 정보 교환을 수행합니다. 상대방 VASP에 도달할 수 있는 경로를 찾고, 필요한 데이터를 그 경로로 협의하며, 13개 상태로 이루어진 수명 주기에 따라 교환 과정을 추적합니다. 교환이 완료되면 알려 드리므로 온체인에서 자금을 해제할 수 있습니다.

## 개요

FATF Travel Rule(권고안 16)은 EU에서 자금이체 규정(Transfer of Funds Regulation, TFR)으로 시행되며, 발신 VASP(**originator VASP**)가 가상자산 이전이 정산되기 전에 송금인과 수취인 정보를 수집하여 수신 VASP(**beneficiary VASP**)에 전달하도록 요구합니다.

대부분의 관할권과 달리 EU TFR은 **최소 금액이 없습니다**. 보고 기준선을 넘는 거래만 대상이 되는 것이 아니라, 모든 가상자산 이전이 대상입니다.

Didit의 Travel Rule 제품은 단순한 데이터 필드가 아니라 관리형 정보 교환 서비스입니다.

* `transaction_category: "travelRule"`을 지정하여 송금을 제출합니다.
* Didit은 우선순위 순서대로 경로를 시도하여 거래 상대방 VASP에 도달하고, 도달한 첫 번째 경로에서 정보 교환을 실행합니다. 상대측이 수취인 이름을 반환하면, 보내신 이름과 대조합니다.
* 교환 상태가 `COMPLETED`에 도달한 후에만 온체인으로 거래를 전송해야 합니다.
* 이후 온체인 거래 해시를 Didit에 보고하여 교환을 `FINISHED`로 마무리합니다.

이번 릴리스에는 일곱 개의 경로가 포함되며, 엄격한 우선순위 순서대로 시도됩니다. 거래 상대방에 가장 먼저 도달할 수 있는 경로가 선택됩니다.

| 우선순위 | 경로 | 작동 방식 |
| - | - | - |
| 1 | `INTERNAL` | 다른 Didit 고객이 서명된 메시지 또는 테스트 입금으로 수취인 지갑을 통제할 수 있음을 증명한 경우에 사용됩니다. Didit은 해당 고객의 애플리케이션에 일치하는 수신 송금을 생성하고, 교환을 전적으로 플랫폼 내에서 협상합니다. 외부 네트워크 호출은 없습니다. 즉시 처리됩니다. |
| 2 | `TRP` | 개방형 Travel Rule 프로토콜(TRP / OpenVASP)에 대한 네이티브 연동입니다. 송금에 [Travel Address](#travel-address)가 포함되거나, 수취 지갑의 주소록 항목에 Travel Address가 저장되어 있거나, 거래 상대방 VASP가 [디렉터리](#디렉터리)에서 TRP endpoint와 함께 확인되는 경우에 사용됩니다. [TRP 자산 식별자](#trp-자산-식별자)에 나열된 자산을 처리합니다. Didit은 이메일 왕복 없이 시스템 간 방식으로 송금인과 수취인 데이터를 교환합니다. |
| 3 | `GTR` | 글로벌 여행 규칙(GTR) 네트워크입니다. 거래 상대방은 명시적인 `counterparty_gtr_vasp_code` 또는 이를 포함한 디렉터리 항목을 통해 확인됩니다. Didit은 GTR 응답에서 상대측의 답변을 읽거나, 송금이 대기하는 동안 5분마다 요청 상태를 폴링하여 확인합니다. |
| 4 | `TRUST` | TRUST 네트워크입니다. 거래 상대방은 `counterparty_trust_membership_id` 또는 이를 포함한 디렉터리 항목을 통해 확인됩니다. 발신 전용입니다. TRUST는 Didit이 읽을 수 있는 응답을 보내지 않으므로, 송금은 만료될 때까지 `AWAITING_COUNTERPARTY` 상태로 대기합니다. |
| 5 | `VERIFYVASP` | VerifyVASP 네트워크입니다. 거래 상대방은 `counterparty_verifyvasp_id` 또는 이를 포함한 디렉터리 항목을 통해 확인됩니다. Didit은 검증의 상태를 5분마다 폴링하여 상대측의 답변을 읽습니다. |
| 6 | `SYGNA` | Sygna Bridge 네트워크입니다(2026년 VerifyVASP가 인수했으며, 기존 회원을 위해 계속 운영됩니다). Sygna 프로토콜의 네이티브 구현으로, ECIES로 암호화된 IVMS101과 서명된 허가 요청을 사용합니다. 거래 상대방은 `counterparty_sygna_vasp_code` 또는 이를 포함한 디렉터리 항목을 통해 확인됩니다. 상대측의 수락 또는 거절 답변이 송금을 완료하거나 거부합니다. Didit은 송금 상태를 5분마다 폴링하며, Didit의 공개 TRP 호스트가 가동되면 Sygna 콜백으로도 답변을 받습니다. |
| 7 | `EMAIL` | 어떤 프로토콜이나 네트워크 경로로도 거래 상대방에 도달할 수 없고, `counterparty_vasp_email`에 상대방의 컴플라이언스 담당자를 지정했거나 상대방 디렉터리 항목에 [컴플라이언스 담당자 연락처](#연락처)가 있는 경우에 사용되는 대체 경로입니다. Didit은 해당 연락처에 일회용 수령 링크를 이메일로 보내, Didit 계정 없이도 교환을 확인하거나 거절할 수 있게 합니다. 아직 어떤 네트워크에서도 도달할 수 없는 상대방, 즉 선라이즈 문제를 해결하는 핵심 경로입니다. |

네트워크 경로(`GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`)에는 발신 측의 네트워크 멤버십이 필요하며, 현재 이 멤버십은 **고객님 본인의 것**이어야 합니다.
**Marketplace**(Travel Rule 네트워크 카테고리)의 콘솔에서 멤버십 자격 증명을 연결하면, 송금은 고객님 본인의 네트워크 신원으로 전달됩니다.

**Didit은 현재 네 개 네트워크 중 어느 곳에도 플랫폼 멤버십을 보유하고 있지 않으며, 이들 네트워크의 제공자로 등록하는 중입니다.**
등록이 완료되기 전까지 네 네트워크 모두 `platform_available`은 `false`이고, 설정의 `didit` 모드(Didit 플랫폼 멤버십을 통한 라우팅)는 어떤 네트워크에서도 사용할 수 없으며, `disabled_networks`로 끌 수 있는 항목도 없습니다.
고객님이 직접 연결한 멤버십은 항상 플랫폼 멤버십보다 우선합니다.
GTR을 포함하여 기본으로 포함된 네트워크 멤버십은 없습니다.
Travel Rule을 활성화해도 GTR, TRUST, VerifyVASP, Sygna는 활성화되지 않습니다.
고객님 애플리케이션의 유효한 `networks` 모드는 [Get Travel Rule Settings](https://docs.didit.me/ko/management-api/travel-rule/get-settings.md)에서 확인하고, 각 네트워크가 현재 도달할 수 있는 범위는 [Travel Rule 네트워크](https://docs.didit.me/ko/transaction-monitoring/travel-rule-networks.md)에서 확인하십시오.
네 네트워크 모두 `unavailable`로 표시되면 어떤 네트워크도 교환을 라우팅할 수 없습니다. 이 경우 **Marketplace**에서 고객님의 멤버십을 연결하거나, 도달 가능한 `INTERNAL`, `TRP`, `EMAIL` 거래 상대방을 사용하십시오.

멤버십을 저장할 때 참가자 코드는 반드시 고객님 본인의 것이어야 합니다. 해당 네트워크에서 Didit 고유 코드이거나, 다른 애플리케이션의 멤버십이 이미 보유한 코드이면 저장 요청은 `400`으로 응답합니다.
GTR 멤버십에는 ed25519 개인 키(base64)가 필요합니다. 이 키의 공개 키는 GTR 포털에 등록되어 있어야 합니다. 이 키는 고객님이 보내는 개인 데이터를 암호화하고, 수신하는 데이터를 복호화합니다.
VerifyVASP 멤버십은 고객님 본인의 VerifyVASP Enclave를 가리킵니다. URL은 고객님의 Enclave 주소이고, 토큰은 Enclave가 Didit에 제시하는 token이어야 합니다.

고객님 본인의 멤버십이 있더라도, 네트워크 경로는 `beneficiary_data`에 해당 네트워크의 거래 상대방 ID(`counterparty_gtr_vasp_code`, `counterparty_trust_membership_id`, `counterparty_verifyvasp_id`, `counterparty_sygna_vasp_code` 중 하나)가 포함되거나, 거래 상대방의 디렉터리 항목에 해당 네트워크의 거래 상대방 ID가 나열된 경우에만 송금을 라우팅합니다([경로](#경로) 참조).

`platform_available`은 플랫폼 멤버십이 설정되어 있는지를 나타내며, 실시간 네트워크 상태 점검 결과가 아닙니다. 마찬가지로 `connected`는 멤버십 자격 증명이 저장되어 로컬 검증을 통과했다는 뜻이며, 첫 번째 실시간 교환에서 네트워크에 대해 실제로 검증됩니다. `didit` 또는 `own` 모드라고 해서 거래 상대방 도달 가능성이나 전달 성공이 보장되는 것은 아닙니다.

자체 멤버십이 있는 경우, Didit은 네트워크 응답에서 `GTR`, `VERIFYVASP`, `SYGNA`를 통해 거래 상대방의 답변을 확인하거나, 5분마다 네트워크를 조회하여 그 답변을 읽습니다. 이 답변에 따라 전송을 완료하거나 거부합니다.
`TRUST`은 송신만 지원합니다. 이 네트워크로 보낸 전송은 `confirmation_timeout_hours`가 경과할 때까지 `AWAITING_COUNTERPARTY` 상태에 머물며, 그 후 `EXPIRED`로 전환되고 `timeout_outcome`이 적용됩니다.
네트워크를 통해 다른 VASP가 보낸 전송을 수신하는 기능은 아직 활성화되어 있지 않으며, 라이브 네트워크를 대상으로 네트워크를 통한 정보 교환도 아직 완료된 적이 없습니다. 자세한 내용은 [Travel Rule 네트워크](https://docs.didit.me/ko/transaction-monitoring/travel-rule-networks.md)를 참조하세요.

그 밖의 네트워크인 CODE(한국 얼라이언스), TRISA, Notabene, Veriscope는 콘솔의 **Marketplace**에 접근 요청 타일로 나열되어 있습니다.
Didit은 아직 이들과 연동되어 있지 않으므로, 현재는 이들 네트워크를 통해 어떤 전송도 라우팅할 수 없습니다.

7개의 네트워크 모두 거래 상대방을 확인하지 못하는 경우, Didit은 포기하기 전에 한 단계를 더 수행합니다. 바로 블록체인 분석 귀속입니다.
수취 지갑을 블록체인 분석 데이터와 대조하며, 해당 지갑이 알려진 거래소 또는 수탁 기관에 속하면 Didit은 귀속된 VASP를 전송 정보에 기록하고, 이를 [디렉터리](#디렉터리)에 추가한 뒤 새 이름으로 네트워크 조회를 한 번 다시 실행합니다.
전체 동작 방식과 요금은 [블록체인 분석 귀속](#블록체인-분석-귀속)을 참조하세요.

목적지가 [자체 보관 지갑으로 선언된](#선언된-자체-보관-지갑) 전송은 7개의 네트워크를 모두 건너뜁니다. 교환할 거래 상대방 VASP가 없기 때문입니다. 이 경우 바로 최종 사용자의 지갑 소유권 증명 단계로 넘어갑니다.

> [!NOTE]
>
> Didit은 상대방 측 교환 기능도 운영합니다. 다른 VASP가 귀하에게 연결하기 위해 호출하는 공개 TRP 식별 및 조회 endpoint, 콜백 endpoint, 이메일 수신 흐름이 여기에 포함됩니다.
> 이들은 관리형 인프라이며, 귀하는 이들을 직접 호출하지 않습니다. 다만 거래 상대방 VASP가 귀하와 연결할 수 있도록, [Travel Rule 상호운용성](https://docs.didit.me/ko/transaction-monitoring/travel-rule-interoperability.md) 페이지에서 이들 각각을 문서화하고 있습니다.
> 귀하는 이 페이지에 설명된 `/v3/` endpoint를 통해서만 Travel Rule과 상호작용합니다.

## 전송 요청 시 실행되는 작업

검사는 `POST /v3/transactions/` 호출 한 번 안에서 실행됩니다. 응답을 받을 때쯤이면 스크리닝이 끝났고, 전송이 저장되었으며, 규칙 평가도 완료되어 있습니다.
교환 자체는 응답 이후에 시작됩니다. Didit이 경로를 선택하고 요청 외부에서 거래 상대방에게 연락하므로, 네트워크나 거래 상대방 때문에 생성 요청이 느려지거나 실패하는 일은 없습니다.
그 이후의 모든 상태 변경은 경로 정보를 포함한 [`travel_rule.status.updated`](#webhook) webhook으로 전달됩니다. Travel Rule 전송 endpoint가 반환하는 전송 객체에는 메시지가 전달되었는지 보여주는 [`delivery`](#전달-및-재시도) 필드가 있습니다.
폴링해야 하는 파이프라인은 없습니다. 비동기로 처리되는 부분은 교환, 거래 상대방의 응답, 그리고 최종 사용자의 작업뿐입니다.

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant You as Your platform
    participant Didit as Didit API
    participant CP as Counterparty VASP
    participant User as Your customer

    You->>Didit: POST /v3/transactions/ (travelRule)
    Note over Didit: Validate + bind subject + blocklist check
    Note over Didit: Device & IP enrichment
    Note over Didit: Crypto screening (when crypto + monitoring on)
    Note over Didit: Threshold check -> transfer stored (AWAITING_COUNTERPARTY)
    Note over Didit: Rules engine (EU TFR bundle + your rules)
    Didit--)You: transaction.created webhook
    Didit-->>You: Transaction + travel_rule.status + action_required
    Note over Didit: After the response: rails in priority order -> analytics attribution
    Didit->>CP: Exchange over the first reachable rail
    Didit--)You: travel_rule.status.updated (each status change)
    User->>Didit: Proves wallet control (when action_required)
    CP-->>Didit: Confirms beneficiary data
    Didit--)You: travel_rule.status.updated (COMPLETED)
    You->>You: Broadcast on-chain
    You->>Didit: PATCH .../travel-rule/ { payment_txn_id }
    Didit--)You: travel_rule.status.updated (FINISHED)
```

단계별 설명:

1. **검증 및 주체 연결.** 페이로드를 검증합니다.
   애플리케이션에서 이미 사용된 `transaction_id`는 `400`으로 거부되며, 같은 id를 가진 두 요청이 경합하면 그중 하나는 `409`를 받습니다.
   `subject`는 `vendor_data`를 통해 사용자 또는 기업 프로필에 연결되므로, 전송이 모니터링을 위해 해당 프로필에 표시됩니다.
   차단 목록에 있는 주체나 거래 상대방 `vendor_data`는 `400`으로 거부되며, 거래는 생성되지 않습니다.
   차단 목록에 있는 지갑, 은행 계좌, IP, 기기는 6단계에서 확인되며, 라우팅이 실행된 후 그 단계에서 거래를 거부합니다.
2. **기기 및 네트워크 보강.** 당사자 IP에 위치 정보가 추가되고, 기기 컨텍스트([SDK 제출](https://docs.didit.me/ko/transaction-monitoring/sdk-transaction-submission.md) 시 자동으로)는 기기 그래프로 정규화됩니다. 이는 KYC 세션이 채우는 것과 같은 그래프이므로, "기기로 연결됨" 기능이 온보딩과 거래 전반에서 작동합니다.
3. **암호화폐 스크리닝.** 모니터링이 활성화된 암호화폐 전송의 경우, 교환이 이루어지기 전에 관련 지갑(또는 온체인 해시)을 블록체인 분석 서비스로 스크리닝합니다. 제재 노출, 믹서 근접성, 도난 자금 여부를 확인합니다. [암호화폐 및 지갑 스크리닝](https://docs.didit.me/ko/transaction-monitoring/aml-screening.md)을 참조하십시오.
4. **Travel Rule 전송.** 교환 엔진은 임계값을 확인하고 [자체 보관 지갑 선언](#선언된-자체-보관-지갑)을 반영한 뒤 전송을 저장합니다. 임계값 미만이면 `NOT_APPLICABLE`, 선언된 자체 보관 지갑이면 `UNCONFIRMED_OWNERSHIP`, 거래 상대방의 지갑이 사용자의 검증된 지갑이면 `COMPLETED`, 그 외에는 `AWAITING_COUNTERPARTY`가 설정되며, 이때 경로는 아직 비어 있습니다.
   각 상태 변경은 `travel_rule.status.updated` 이벤트를 대기열에 넣으며, Didit은 이 이벤트를 거래의 `transaction.created`(7단계) 다음에 전송합니다.
5. **지갑 소유권 확인 발급.** 전송에 최종 사용자의 소유 증명이 필요하고 `auto_wallet_verification`이 켜져 있으면, [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md) 세션이 발급되어 `action_required`에 반환됩니다.
6. **규칙 평가.** 활성화된 모든 규칙이 실행됩니다. [EU TFR 번들](#eu-tfr-규칙-번들)은 4단계에서 설정된 전송 상태에 반응하고, 사용자 정의 규칙은 3단계의 스크리닝 결과를 확인하며, 규칙은 거래를 보류(`IN_REVIEW`)하거나, 거부하거나, [추가 인증](https://docs.didit.me/ko/transaction-monitoring/sdk-transaction-submission.md#필수-사용자-작업)(`AWAITING_USER`)을 요구할 수 있습니다.
7. **결제 및 알림.** 아웃바운드 거래는 한 개의 항목으로 결제됩니다. 교환을 시작한 경우에는 [Travel Rule 항목](#요금)으로, 그렇지 않으면 Transaction Screening으로 결제됩니다. 인바운드 전송에는 어떤 수수료도 부과되지 않으며, 샌드박스 애플리케이션은 요금이 결제되지 않습니다.
   응답이 반환되고, `transaction.created`가 webhook 대상으로 전송된 뒤, 4단계의 `travel_rule.status.updated` 이벤트가 이어서 전송됩니다.
8. **응답 이후의 라우팅.** Didit은 우선순위 순서대로 [7개 경로](#개요)를 확인합니다. 어느 경로로도 해결되지 않으면 [블록체인 분석 기반 식별](#블록체인-분석-귀속)로 거래 상대방을 찾고, 경로를 한 번 더 실행합니다. 처음으로 연결 가능한 경로가 교환을 담당합니다. 거래 상대방이 응답하는 동안 전송은 해당 경로에서 `AWAITING_COUNTERPARTY` 상태를 유지하고, `INTERNAL` 거래 상대방이 일치하면 즉시 정산되며(`COMPLETED` 또는 `COUNTERPARTY_MISMATCHED_DATA`), 어느 경로로도 거래 상대방에 도달하지 못하면 `COUNTERPARTY_VASP_NOT_FOUND`가 됩니다. 각 변경은 `travel_rule.status.updated`를 발생시키며, 상태나 경로가 바뀔 때마다 규칙이 다시 실행됩니다. 라우팅 후 `COUNTERPARTY_VASP_NOT_FOUND` 상태로 남은 전송은 이 시점에 지갑 소유권 위젯 세션을 받습니다(`auto_wallet_verification`이 켜져 있는 경우). 이 세션은 거래의 `action_required`에서 [거래 조회](https://docs.didit.me/ko/management-api/transactions/get.md)로 읽을 수 있습니다.

### 각 단계가 충족하는 의무

| 파이프라인 단계 | 충족하는 의무 |
| - | - |
| `originator_data`에 제공하는 송금인 데이터 | 정확한 송금인 정보(FATF R.16; EU TFR 제14조). 사람 데이터를 전달하는 경로(`INTERNAL`, `TRP`, 네트워크 경로)에서는 주체가 승인된 Didit KYC 인증을 보유한 사용자인 경우 Didit이 검증된 신원을 전송하며([검증된 데이터 및 출처](#검증된-데이터와-출처) 참조), 그 외에는 전송한 `originator_data`를 그대로 보냅니다. |
| 정산 전 경로 교환 | 정산 **전에** 거래 상대방 VASP로 송금인 및 수취인 정보 전송(FATF R.16; EU TFR 제14\~16조) |
| 수취인 이름 대조 | 수취인 데이터가 목적지와 일치하는지 확인(EU TFR 제16조, 수취인 CASP의 의무) |
| 자체 보관 지갑 소유 증명 | EU TFR 자체 보관 지갑 의무 - VASP와 교환하는 대신 비호스팅 주소의 소유를 확인 |
| 암호화폐 스크리닝 + EU TFR 규칙 번들 | 정보가 누락되었거나 불완전한 전송에 대한 위험 기반 절차, 그리고 제재 노출 통제 |
| 브로드캐스트 전 `COMPLETED` 게이트 | 교환이 완료될 때까지 자금을 지급하지 않음 - 규칙의 핵심 |
| 온체인 해시와 타임라인이 포함된 `FINISHED` 종료 처리 | 기록 보존: 모든 전송은 감사를 위해 전체 협상 타임라인을 보관 |

> [!NOTE]
>
> **테스트 참고.** 샌드박스 애플리케이션은 규칙 평가와 결제를 건너뛰며, `transaction.created` 또는 `transaction.status.updated` webhook을 보내지 않습니다. 샌드박스 거래는 만료되는 Travel Rule 전송이 `timeout_outcome`을 적용하는 경우를 제외하고 `APPROVED` 상태를 유지하며, 요금은 결제되지 않습니다.
> Travel Rule이 활성화되어 있으면 Travel Rule 교환 엔진 자체가 샌드박스 애플리케이션에서 실행되며, 거래 상대방은 한 곳만 있습니다. 모든 **아웃바운드** 교환은 `TEST_VASP` 경로를 통해 [Didit 테스트 VASP](https://docs.didit.me/ko/transaction-monitoring/travel-rule-sandbox.md)로 전송되며, 이 테스트 VASP는 Didit 내부에서 선택한 시나리오에 따라 응답합니다. 따라서 전송은 완료되거나, 거부되거나, 불일치가 발생하거나, 도달할 수 없게 되거나, 만료되거나, 지갑 소유권 확인을 기다리는 상태가 됩니다.
> 실제 거래 상대방 VASP에는 어떤 컴플라이언스 메시지도 전달되지 않으며, `travel_rule.status.updated` webhook은 `environment: sandbox`와 함께 전송됩니다.
> **인바운드**의 경우 테스트 VASP가 사용자에게 전송을 보낼 수 있으며([Trigger Test VASP Inbound Transfer](https://docs.didit.me/ko/management-api/travel-rule/trigger-test-vasp-inbound.md)), 등록(`POST /v3/travel-rule/inbound/`)은 실제 애플리케이션과 마찬가지로 지갑 주소록을 기준으로 처리됩니다. 결과는 `ON_HOLD`, `UNCONFIRMED_OWNERSHIP`, `COUNTERPARTY_MISMATCHED_DATA`, 또는 `COMPLETED` 후 `FINISHED`입니다.
> [샌드박스에서 Travel Rule 테스트](https://docs.didit.me/ko/transaction-monitoring/travel-rule-sandbox.md)를 참조하십시오.

## 수명 주기

모든 전송에는 13개 값 중 하나의 `status`가 있습니다.
일반적인 경우 교환은 `AWAITING_COUNTERPARTY` 상태에서 시작합니다. 금액이 `threshold_amount`보다 작거나 Lightning 결제 요청에 수취인이 지정되지 않은 경우([Lightning](#라이트닝) 참고)에는 `NOT_APPLICABLE` 상태에서 시작합니다. 대상 지갑이 [자체 보관 지갑으로 선언된](#선언된-자체-보관-지갑) 경우에는 `UNCONFIRMED_OWNERSHIP` 상태에서 시작합니다. 교환은 최종 상태 중 하나인 `FINISHED`, `CANCELLED`, `EXPIRED`에서 끝납니다.

전송은 기본 거래의 검토 상태와 **별도로** 자체 상태 머신을 실행합니다.
교환 상태가 단독으로 거래 상태를 바꾸는 일은 없으며, 예외가 하나 있습니다. 만료되면 설정하신 `timeout_outcome`이 적용됩니다(해당하는 경우 [선라이즈 또는 무응답 정책](#초기-시행-및-응답-없음-정책)이 대신 적용됩니다).
대신 모든 전환마다 규칙이 다시 실행되므로, [EU TFR 규칙](#eu-tfr-규칙-번들)이나 `travel_rule_status`를 키로 하는 규칙이 거래를 `IN_REVIEW` 같은 상태로 옮길 수 있습니다.
최종 사용자의 지갑 통제 증명이 필요한 전송은 거래에 `action_required` 블록을 추가하지만, 그 `status`는 규칙이 설정한 상태 그대로 유지됩니다.
교환 상태는 `travel_rule.status`에서 읽고, 허용 / 보류 / 거부 판정은 거래 자체의 `status`에서 읽습니다.

모든 전환(그리고 수동으로 보고된 모든 상태)은 [규칙](https://docs.didit.me/ko/transaction-monitoring/rules.md)이 키로 사용할 수 있도록 거래의 사용자 지정 값에도 반영됩니다. `custom_values.travel_rule_status`는 표준 상태, `custom_values.travel_rule_rail`은 교환을 처리한 레일(레일이 선택되기 전까지 비어 있음), `custom_values.travel_rule_regime`은 [설정](#설정)의 `jurisdiction`에서 파생됩니다. `SG`는 `singapore`, `GB`는 `uk`, `DE`나 `FR` 같은 EU 또는 EEA 회원국 코드는 `eu`로 변환되며, 매핑되지 않은 코드는 소문자로 기록됩니다(예: `US` → `us`). 직접 제출한 `props.travel_rule_regime`은 항상 파생 값보다 우선합니다.

| 상태 | 의미 |
| - | - |
| `AWAITING_COUNTERPARTY` | 전송은 상대방의 처리를 기다리는 상태입니다. 생성 직후에는 Didit이 레일을 선택하고 교환을 전송하는 동안 이 상태에 있고, 그 후에는 상대방이 응답할 때까지 이 상태에 머뭅니다. 전송의 [`delivery`](#전달-및-재시도)는 메시지가 전달되었는지 보여줍니다. `TRUST`는 전송만 하므로, 이 레일을 사용하는 전송은 만료될 때까지 이 상태에 머뭅니다. |
| `UNCONFIRMED_OWNERSHIP` | 지갑 통제 증명을 기다리는 상태입니다. 제출 시(송신 또는 수신) 상대방 지갑이 [자체 보관 지갑으로 선언된](#선언된-자체-보관-지갑) 경우, 또는 수신 전송이 아직 소유권이 확인되지 않은 [주소록 항목](#지갑-주소록)으로 해석된 경우에 이 상태가 됩니다. [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md) 또는 [`POST .../travel-rule/ownership/`](#지갑-소유권-확인)를 통해 다음 단계로 넘어갑니다. |
| `ON_HOLD` | Didit은 수신 전송에만 이 상태를 설정합니다. [입금 후 등록](#입금-후-소급-등록)의 대상 지갑이나 수신 `TRP` 문의의 대상 지갑이 [주소록](#지갑-주소록)에 없는 경우, 또는 수신 `TRP` 문의가 귀사의 결정을 기다리는 경우(자동 수락이 꺼져 있거나 자동 응답을 전달할 수 없는 경우)에 해당합니다. [`POST .../travel-rule/ownership/`](#지갑-소유권-확인)로 해결하십시오. 이 상태는 그 자체로 거래 상태를 바꾸지 않습니다. 옵트인 방식의 `travel-rule-pending-action` [라이브러리 규칙](https://docs.didit.me/ko/transaction-monitoring/rules-library.md)이 이런 거래를 `AWAITING_USER`로 옮깁니다. |
| `COMPLETED` | 교환이 성공적으로 완료되었습니다. 상대방(또는 수신 전송의 경우 귀사)이 승인했거나, 해당 경로에서 Didit이 수행하는 검사(수취인 이름 일치, 지갑 소유권 증명)를 통과했습니다. 온체인으로 전송을 브로드캐스트해도 안전합니다. |
| `FINISHED` | 온체인 거래 해시와 함께 교환이 종료되었습니다. 송신 전송의 경우 [`PATCH .../travel-rule/`](#전송하기)로 해시를 보고합니다. 수신 전송의 경우 Didit은 입금이 이미 정산되었을 때([입금 후 등록](#입금-후-소급-등록)), 발신자가 `TRP` 전송을 확인했을 때, 또는 `INTERNAL` 송신 측이 완료되었을 때 이 상태를 설정합니다. |
| `CANCELLED` | 최종 상태가 아닌 어느 상태에서든 `action: "cancel"`로 수동 취소되었거나, 수신 `TRP` 전송의 발신자가 취소한 상태입니다. |
| `EXPIRED` | `AWAITING_COUNTERPARTY` 상태에서 `confirmation_timeout_hours`가 경과했습니다. 스위퍼(15분마다 실행)가 전송을 만료시키고, 설정된 `timeout_outcome`을 기본 거래에 적용합니다. `REJECT`는 거래를 거부하고, `HOLD`는 거래를 검토 상태로 전환하며, `PROCEED`는 거래를 그대로 둡니다. 해당하는 경우 [선라이즈 또는 무응답 정책](#초기-시행-및-응답-없음-정책)이 `timeout_outcome`을 대신합니다. |
| `COUNTERPARTY_MISMATCHED_DATA` | 수취인 이름이 상대 측과 비교했을 때 설정된 `name_matching_strictness`를 충족하지 못했습니다. 여기서 상대 측은 상대방이 반환한 이름 또는 일치하는 주소록 항목의 `holder_name`을 의미합니다. |
| `COUNTERPARTY_VASP_NOT_FOUND` | 7개 레일과 [블록체인 분석 귀속](#블록체인-분석-귀속) 대체 경로 중 어느 것도 이 전송에 도달 가능한 상대방 VASP를 찾지 못했습니다. 귀속된 엔터티는 여전히 전송과 디렉터리에 기록됩니다. 송신 전송의 경우 (`auto_wallet_verification`이 켜져 있으면) Didit이 [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md) 세션을 자동으로 생성하므로, 전송은 자체 보관 지갑 전송으로 완료될 수 있습니다. `reason`과 `next_actions`는 이유와 다음 단계를 알려줍니다. 자세한 내용은 [전송이 멈추는 이유](#전송이-멈춘-이유)를 참고하십시오. |
| `COUNTERPARTY_VASP_NOT_REACHABLE` | 상대방 VASP는 확인되었지만 선택된 레일이 전달하지 못했습니다. 다음 중 하나입니다. 그 `TRP` 신원 또는 문의 엔드포인트에 연결할 수 없는 경우, 보고된 신원이 `counterparty_vasp_lei` 또는 `counterparty_vasp_name`과 일치하지 않는 경우, 네트워크가 메시지를 거부했거나 여섯 번 시도한 후에도 수락하지 않은 경우([전송 및 재시도](#전달-및-재시도) 참고), 또는 메일 제공자가 컴플라이언스 이메일을 거부한 경우입니다. `reason`은 이 경우들을 구분하고, `next_actions`는 전송을 다음 단계로 옮기는 방법을 알려줍니다. 자세한 내용은 [전송이 멈추는 이유](#전송이-멈춘-이유)를 참고하십시오. 다시 연결 가능해지면 `action: "resend"`로 재시도하십시오. |
| `COUNTERPARTY_VASP_GENERAL_DECLINE` | 상대방이 교환을 거부했습니다. 소유권 거부, 이메일 수신 거부, 또는 `TRP` 또는 Sygna 거절이 해당합니다. |
| `NOT_ENOUGH_COUNTERPARTY_DATA` | 수신 `TRP` 문의가 수취인을 이름이나 지갑 주소 어느 것으로도 식별하지 않아서, Didit이 이를 주소록과 매칭할 수 없었습니다. 소유권 엔드포인트와 `resend` 모두 이런 수신 전송을 받지 않습니다. 취소만 할 수 있으며, 취소해도 상대방의 문의에는 응답하지 않습니다. Didit은 송신 전송에는 이 상태를 설정하지 않습니다. 송신 전송에 직접 이 상태를 기록하는 경우, `action: "resend"`는 라우팅을 다시 실행합니다. |
| `NOT_APPLICABLE` | 전송 금액이 설정된 `threshold_amount`보다 낮은 경우, 발신 [Lightning](#라이트닝) 결제 요청에 수취인이 지정되지 않은 경우(`chain_details.not_applicable_reason`에 이유가 표시됨), 또는 Travel Rule이 비활성화된 상태에서 직접 이 상태를 기록한 경우입니다(레거시 `UNKNOWN` 및 `EXEMPT` 값 포함, 아래 참조). |

> [!WARNING]
>
> **`COMPLETED` 전에는 절대 브로드캐스트하지 마십시오.**
> 전송이 `AWAITING_COUNTERPARTY`, `UNCONFIRMED_OWNERSHIP` 또는 불일치/거부 상태에 있는 동안 온체인에서 자금을 내보내면 이 정보 교환의 목적이 무너집니다.
> 브로드캐스트 단계는 `status == "COMPLETED"`일 때만 진행하도록 제어한 뒤, 결과로 나온 `payment_txn_id`와 함께 `PATCH .../travel-rule/`를 호출해 `FINISHED`로 전환하십시오.

상태 전환 한눈에 보기(`CANCELLED`는 종료되지 않은 모든 상태에서 `action: "cancel"`로도 도달할 수 있습니다):

```mermaid theme={null}
stateDiagram-v2
    [*] --> NOT_APPLICABLE: below threshold
    [*] --> UNCONFIRMED_OWNERSHIP: self-hosted wallet declared
    [*] --> AWAITING_COUNTERPARTY: transfer stored, routing queued
    [*] --> COMPLETED: your own verified wallet
    [*] --> ON_HOLD: inbound with no matching wallet entry
    [*] --> NOT_ENOUGH_COUNTERPARTY_DATA: inbound TRP inquiry names no beneficiary
    AWAITING_COUNTERPARTY --> COUNTERPARTY_MISMATCHED_DATA: INTERNAL rail, verified entry, name mismatch
    AWAITING_COUNTERPARTY --> COUNTERPARTY_VASP_NOT_FOUND: no rail resolved
    AWAITING_COUNTERPARTY --> COUNTERPARTY_VASP_NOT_REACHABLE: delivery failed
    AWAITING_COUNTERPARTY --> COMPLETED: counterparty confirmed, names match where checked
    AWAITING_COUNTERPARTY --> COUNTERPARTY_MISMATCHED_DATA: name match failed
    AWAITING_COUNTERPARTY --> COUNTERPARTY_VASP_GENERAL_DECLINE: counterparty declined
    AWAITING_COUNTERPARTY --> EXPIRED: confirmation timeout
    AWAITING_COUNTERPARTY --> COUNTERPARTY_VASP_NOT_REACHABLE: TRP inquiry not delivered
    UNCONFIRMED_OWNERSHIP --> COMPLETED: ownership proven, names match
    UNCONFIRMED_OWNERSHIP --> COUNTERPARTY_MISMATCHED_DATA: name match failed
    UNCONFIRMED_OWNERSHIP --> COUNTERPARTY_VASP_GENERAL_DECLINE: ownership denied
    ON_HOLD --> COMPLETED: you confirm the wallet, names match
    ON_HOLD --> COUNTERPARTY_MISMATCHED_DATA: you confirm the wallet, name mismatch
    ON_HOLD --> COUNTERPARTY_VASP_GENERAL_DECLINE: you deny the wallet
    COUNTERPARTY_MISMATCHED_DATA --> COMPLETED: you approve an inbound TRP inquiry
    COUNTERPARTY_MISMATCHED_DATA --> COUNTERPARTY_VASP_GENERAL_DECLINE: you decline an inbound TRP inquiry
    COUNTERPARTY_VASP_NOT_FOUND --> COMPLETED: wallet ownership proven in the widget
    COUNTERPARTY_VASP_NOT_FOUND --> AWAITING_COUNTERPARTY: action resend
    COUNTERPARTY_VASP_NOT_REACHABLE --> AWAITING_COUNTERPARTY: action resend
    COMPLETED --> FINISHED: payment_txn_id reported or deposit already settled
    FINISHED --> [*]
    EXPIRED --> [*]
```

### 전달 및 재시도

Didit은 전송이 보내야 하는 모든 메시지를 해당 메시지를 유발한 요청과 별도로 전송하며, 상대방이 수신할 때까지 재시도합니다.
[완료, 취소 또는 재전송](https://docs.didit.me/ko/management-api/travel-rule/update-transfer.md), [소유권 확인](https://docs.didit.me/ko/management-api/travel-rule/confirm-ownership.md), [sunrise 등록](#입금-후-소급-등록)의 응답으로 반환되는 전송 객체는 최신 메시지를 `delivery`에 담고, 거래에 포함된 `travel_rule` 객체도 마찬가지입니다. 전송이 메시지를 보낼 의무가 없었다면 `null`입니다.

| 필드 | 설명 |
| - | - |
| `kind` | `SEND`(거래 상대방에게 보내는 첫 메시지), `ANSWER`(수락 또는 거부, 요청한 네트워크에 보내는 응답), `TXID`(전송 완료 후의 온체인 해시), `CANCEL`(전송을 취소한 후), 또는 `POLL`(상태 조회를 지원하는 네트워크에 보내는 상태 질의로, 전송이 거래 상대방을 기다리는 동안 5분마다 실행됨). |
| `status` | `QUEUED`, `SENDING`, `RETRYING`, `DELIVERED`, 또는 `FAILED`. |
| `rail` | 메시지를 전달하는 경로입니다. Didit이 첫 메시지에 대한 경로를 선택하기 전까지는 비어 있습니다. |
| `attempts` | 지금까지 시도한 횟수. |
| `last_error` | 마지막 시도가 실패한 이유이며, 실패가 없었다면 비어 있습니다. 실패한 단계와 실패 유형을 짧게 나타내는 이유입니다. 예를 들어 `sygna permission request failed: HTTP 503` 또는 `sygna status query failed: no answer in time`처럼 표시되며, GTR이 오류로 응답한 경우에는 해당 상태 코드와 메시지가 포함됩니다. 응답 본문은 절대 포함되지 않습니다. |
| `next_attempt_at` | 메시지가 `QUEUED` 또는 `RETRYING` 상태일 때 다음 시도가 실행되는 시점입니다. |
| `delivered_at` | 상대방이 메시지를 수신한 시점입니다. |

네트워크가 복구할 수 있는 실패(시간 내 응답이 없음, 연결 실패, HTTP `408`, `429` 또는 `5xx`)는 30초, 2분, 10분, 30분, 1시간 후에 재시도됩니다.
첫 메시지가 최종적으로 실패하면(네트워크가 거부했거나 여섯 번째 시도도 실패한 경우) 전송은 `COUNTERPARTY_VASP_NOT_REACHABLE`로 이동하며, `timeline`에 `last_error`가 기록됩니다. 이때 `action: "resend"`로 재시도할 수 있습니다.
최종적으로 실패한 그 밖의 메시지는 `timeline`에 기록됩니다.
`delivery`는 전송의 `status`를 대체하지 않습니다. 전송 상태는 `status`에서 읽고, 전송이 여전히 대기 중인 이유를 확인할 때 `delivery`를 사용하십시오.

### 레거시 상태 값

이 릴리스 이전에 `travel_rule_details.status`를 사용하고 있었다면 해당 값은 계속 동작하며 자동으로 매핑됩니다. 모든 상태의 camelCase 표기(예: `counterpartyVaspNotFound`)도 제출 시와 [규칙](https://docs.didit.me/ko/transaction-monitoring/rules.md)의 조건 값에서 허용되며, 정식 형식으로 정규화됩니다.

| 레거시 값 | 매핑 대상 |
| - | - |
| `UNKNOWN` | `NOT_APPLICABLE` |
| `COMPLIANT` | `COMPLETED` |
| `PENDING_ACTION` | `ON_HOLD` |
| `PENDING_COUNTERPARTY` | `AWAITING_COUNTERPARTY` |
| `FAILED` | `CANCELLED` |
| `EXEMPT` | `NOT_APPLICABLE` |

## 설정

애플리케이션마다 한 번 `GET`/`PUT /v3/travel-rule/settings/`를 사용해 VASP 프로필과 협상 정책을 구성합니다.

```bash theme={null}
curl -X GET https://verification.didit.me/v3/travel-rule/settings/ \
  -H "x-api-key: YOUR_API_KEY"
```

```json theme={null}
{
  "is_enabled": false,
  "jurisdiction": "EU",
  "name_matching_strictness": "DEFAULT",
  "confirmation_timeout_hours": 48,
  "timeout_outcome": "HOLD",
  "threshold_amount": "0.00",
  "inbound_auto_accept": true,
  "allow_self_declaration": true,
  "allow_screenshot_proof": true,
  "auto_wallet_verification": true,
  "vasp_attribution_enabled": true,
  "counterparty_screening_enabled": false,
  "sunrise_policy": "",
  "sunrise_jurisdictions": ["CO", "KH", "VN"],
  "non_responsive_policy": "",
  "legal_name": "",
  "lei": "",
  "compliance_email": "",
  "is_discoverable": true,
  "travel_address": "",
  "disabled_networks": [],
  "networks": {
    "gtr": { "rail": "GTR", "mode": "unavailable", "platform_available": false, "connected": false },
    "trust": { "rail": "TRUST", "mode": "unavailable", "platform_available": false, "connected": false },
    "verifyvasp": { "rail": "VERIFYVASP", "mode": "unavailable", "platform_available": false, "connected": false },
    "sygna": { "rail": "SYGNA", "mode": "unavailable", "platform_available": false, "connected": false }
  }
}
```

```bash theme={null}
curl -X PUT https://verification.didit.me/v3/travel-rule/settings/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "is_enabled": true,
    "legal_name": "Origin CASP SL",
    "jurisdiction": "EU",
    "name_matching_strictness": "DEFAULT",
    "confirmation_timeout_hours": 48,
    "timeout_outcome": "HOLD",
    "is_discoverable": true
  }'
```

`PUT`은 부분 업데이트입니다. 변경하려는 필드만 보내면 됩니다.

| 필드 | 유형 | 설명 |
| - | - | - |
| `is_enabled` | 불리언 | 이 애플리케이션에서 나가는 `travelRule` 거래에 대해 관리형 협상 흐름을 켭니다. 공백이 아닌 `legal_name`이 필요합니다(이미 저장되어 있거나 같은 요청에 포함되어야 함). 그렇지 않으면 요청은 `400`을 반환합니다. |
| `legal_name` | 문자열 | 귀사 VASP의 법적 명칭입니다. 이메일 수령 페이지에서 거래 상대방에게 표시되며 [VASP 디렉터리](#디렉터리)에도 반환됩니다. |
| `lei` | 문자열 | 귀사의 법인 식별자(Legal Entity Identifier)입니다. VASP 디렉터리에 반환됩니다. |
| `jurisdiction` | 문자열 | 귀사 VASP의 영업 관할 지역 코드이며, 최대 8자입니다(기본값 `"EU"`). |
| `name_matching_strictness` | 문자열 | `NONE`, `STRICT`, `CONTAINS`, `DEFAULT`, 또는 `FUZZY` 중 하나입니다. 아래를 참조하십시오. |
| `confirmation_timeout_hours` | 정수 | `AWAITING_COUNTERPARTY` 송금이 자동 만료되기 전까지 대기할 수 있는 시간(시간 단위)입니다. 최소 `1`, 기본값 `48`. |
| `timeout_outcome` | 문자열 | 송금이 만료될 때 기본 거래에 어떤 일이 일어나는지 정합니다. `HOLD`는 거래를 검토로 이동시키고, `REJECT`는 거래를 거부하며, `PROCEED`는 거래 상태를 바꾸지 않습니다. 기본값 `HOLD`. 해당되는 경우 `sunrise_policy`와 `non_responsive_policy`가 우선합니다. |
| `sunrise_policy` | 문자열 | `HOLD`, `REJECT`, `PROCEED`, 또는 `""`(기본값)으로, `timeout_outcome`을 따릅니다. `sunrise_jurisdictions`에 나열된 관할 지역의 디렉터리 항목이 거래 상대방일 때 만료된 송금이 어떻게 처리되는지 정합니다. [선라이즈 및 무응답 정책](#초기-시행-및-응답-없음-정책)을 참조하십시오. |
| `non_responsive_policy` | 문자열 | `HOLD`, `REJECT`, `PROCEED`, 또는 `""`(기본값)으로, `timeout_outcome`을 따릅니다. 거래 상대방이 [응답성](#응답성)이 `UNRESPONSIVE`인 디렉터리 항목일 때 만료된 송금이 어떻게 처리되는지 정합니다. 두 정책이 모두 해당하면 `sunrise_policy`가 먼저 적용됩니다. |
| `sunrise_jurisdictions` | 문자열 배열 | 읽기 전용입니다. Didit이 Travel Rule 선라이즈 기간에 있다고 보는 국가이며, 현재 `["CO", "KH", "VN"]`입니다. 그 밖의 관할 지역은 `timeout_outcome`을 따릅니다. |
| `compliance_email` | 문자열 | 귀사 VASP 프로필의 컴플라이언스 담당자 이메일 주소입니다. [Didit 네트워크](#didit-네트워크)는 이 주소가 이메일 링크로 확인된 후에만 프로필을 등재합니다. 주소를 변경하려면 새로 확인해야 합니다. |
| `is_discoverable` | 불리언 | 귀사 VASP 프로필을 [Didit 네트워크](#didit-네트워크)에 등재할 수 있는지 정합니다. 등재되면 다른 Didit 고객이 `GET /v3/travel-rule/vasps/`에서 찾을 수 있고 INTERNAL 레일을 통해 연결할 수 있습니다. 등재에는 추가 조건이 있으며, 설정의 `didit_network`에 어떤 조건이 빠져 있는지 표시됩니다. 기본값 `true`. |
| `inbound_auto_accept` | 불리언 | `true`이면, 수신 `TRP` 문의의 수취 지갑이 주소록에 있는 확인된 항목이고 이름이 일치할 때 자동으로 승인됩니다. `false`이면 귀사의 결정을 기다리며 `ON_HOLD` 상태에 머무릅니다. 다른 수신 경로는 이 설정을 읽지 않습니다. 기본값 `true`. |
| `allow_self_declaration` | 불리언 | `true`이면 [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)이 증명 방법으로 자가 선언을 제공하며, `self_declared: true`로 [지갑 주소록](#지갑-주소록) 항목을 만들 수 있습니다. 기본값 `true`. |
| `allow_screenshot_proof` | 불리언 | `true`이면 [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)이 소유권 증거 방법으로 스크린샷 업로드를 제공합니다. 스크린샷만으로는 지갑이 검증되지 않습니다. 컴플라이언스 팀이 이를 검토하고 송금을 확인합니다. 암호 서명보다 약한 증명이므로, 정책상 서명 수준의 증명이 필요하다면 비활성화하십시오. 기본값 `true`. |
| `auto_wallet_verification` | 불리언 | `true`이면, 최종 사용자의 지갑 통제 증명이 필요한 아웃바운드 송금에 대해 [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md) 세션을 거래에 연결하여 자동으로 생성하고, 거래의 `action_required` 블록에 표시합니다. Didit SDK는 [클라이언트 측 제출](https://docs.didit.me/ko/transaction-monitoring/sdk-transaction-submission.md) 시 이 세션을 자동으로 실행합니다. 기본값 `true`. |
| `counterparty_screening_enabled` | 불리언 | 아웃바운드 교환이 연결되는 거래 상대방 VASP에 대해 제재 및 부정적 언론 보도 [스크리닝](#거래-상대방-스크리닝)을 옵트인 방식으로 수행합니다. 스크리닝 건마다 기업 AML 스크리닝 가격으로 과금됩니다. `true`이면 교환이 시작될 때 Didit이 거래 상대방을 스크리닝하며, 30일 미만의 기존 스크리닝 결과를 재사용합니다. 기본값 `false`: 아무것도 스크리닝되거나 과금되지 않습니다. |
| `vasp_attribution_enabled` | 불리언 | `true`이면, 어떤 레일로도 라우팅할 수 없는 아웃바운드 송금은 [블록체인 분석](#블록체인-분석-귀속)을 통해 귀속됩니다. 대상 지갑을 소유한 VASP가 확인되고, 해당 법인이 디렉터리에 추가되며, 귀속된 이름으로 레일이 한 번 더 실행됩니다. 기본값 `true`. |
| `threshold_amount` | 문자열(십진수) | 관리형 교환을 트리거하는 최소 송금 금액이며, 애플리케이션의 기본 통화를 기준으로 합니다. Didit은 이 값을 송금 금액을 기본 통화로 환산한 값(또는 전송한 `amount_in_default_currency`)과 비교합니다. 송금 자체 통화의 금액과는 비교하지 않습니다. 이 금액보다 작은 송금은 바로 `NOT_APPLICABLE`로 처리되며 협상되지 않습니다. Didit이 금액을 환산할 수 없으면 임계값이 적용되지 않고 송금은 교환됩니다. 샌드박스 애플리케이션은 금액을 환산하지 않으므로, 그곳에서는 `amount_in_default_currency`를 보낼 때만 임계값이 적용됩니다. 기본값은 `0.00`이며, 이는 EU TFR에 최소 금액 기준이 없다는 원칙에 맞춰 크기와 관계없이 모든 송금을 확인한다는 뜻입니다. |
| `ownership_validation_mode` | 문자열 | `FULL`, `SIMPLIFIED`, 또는 `AUTOMATED`이며, 지갑 소유권에 필요한 증명의 정도를 정합니다. [소유권 검증 모드](#소유권-검증-모드)를 참조하십시오. 기본값 `AUTOMATED`. |
| `simplified_validation_threshold` | 문자열(십진수) | `SIMPLIFIED` 모드에서 이 금액보다 작은 송금은 자가 선언을 허용합니다. 기본값 `0.00`. |
| `data_sharing_fields` | 객체 | 귀사의 사용자에 대해 보내는 필드를 엔티티 유형별로 정합니다. [데이터 공유](#데이터-공유)를 참조하십시오. |
| `matching_rules` | 객체 | 거래 상대방 데이터의 각 필드를 엔티티 유형별로 얼마나 엄격하게 비교할지 정합니다. [필드별 매칭](#필드별-일치)을 참조하십시오. |
| `disabled_networks` | string\[] | 플랫폼 멤버십을 통해 Didit을 거쳐 전송을 라우팅하면 안 되는 네트워크 레일(`GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`)입니다. 직접 멤버십을 연결한 네트워크(콘솔 Marketplace)는 이 목록의 영향을 받지 않습니다. Didit은 아직 플랫폼 멤버십을 보유하고 있지 않으므로, 현재 이 목록은 라우팅에 아무런 영향을 주지 않습니다(목록에 있는 레일은 `mode: off`로 표시됩니다). 기본값은 `[]`입니다. |
| `networks` | object | 읽기 전용입니다. `gtr` / `trust` / `verifyvasp` / `sygna` 키로 구분되는 네트워크별 상태이며, `{rail, mode, platform_available, connected}` 형식입니다. `mode`는 `own`(직접 연결한 멤버십), `didit`(Didit의 플랫폼 멤버십, 현재 어떤 네트워크에서도 사용 불가), `off`(직접 비활성화함), `unavailable` 중 하나입니다. |
| `travel_address` | string | 읽기 전용입니다. VASP 자체의 Travel Address이며, 다른 VASP가 귀하에게 연결할 때 사용하는 인코딩된 인바운드 `TRP` 조회 엔드포인트입니다. Travel Rule을 활성화하면 채워집니다. 예: `ta2ih1MicegQ3JqiKz9xT89KsSuwaDJYw8DgzmeS5uNDp3dGW3bweBWLKws442chRsyNxmmJnyWc7frxjjSeABVsYR11UuBePkw6E` |

### 이름 일치 엄격도

| 값 | 동작 |
| - | - |
| `NONE` | 모든 수취인 이름을 허용하며, 비교를 수행하지 않습니다. |
| `STRICT` | 두 이름의 정규화된 토큰 집합이 정확히 일치해야 합니다. |
| `CONTAINS` | 한쪽 이름의 모든 단어가 다른 쪽에 포함되어야 하며, 단어 수는 몇 개여도 상관없습니다. |
| `DEFAULT` | 짧은 이름의 모든 토큰이 긴 이름에 있어야 하며(부분 집합 일치), 두 이름이 동일하지 않은 한 짧은 이름은 최소 두 개의 토큰이 있어야 합니다. |
| `FUZZY` | 정규화된 전체 문자열의 유사도 비율이 `0.85` 이상이어야 합니다. |

### 소유권 검증 모드

`ownership_validation_mode`는 전송이 지갑의 소유권을 근거로 삼기 전에 필요한 증명의 수준을 결정합니다:

| 모드 | 동작 |
| - | - |
| `FULL` | 증명이 항상 필요합니다. 서명된 메시지, 테스트 입금, 검토된 스크린샷 중 하나여야 합니다. 최종 사용자의 진술은 인정되지 않습니다. 위젯은 `SELF_DECLARATION`을 제공하지 않으며, 이를 시작하면 `403`이 반환되고, `self_declared: true`로 생성한 주소록 항목은 `400`이 반환됩니다. |
| `SIMPLIFIED` | 전송 금액이 `simplified_validation_threshold` 미만이면 자기 신고가 허용됩니다. 이 임계값을 초과하면 증명이 필요합니다. 인바운드 전송(TRP 조회, 네트워크 메시지 또는 귀하가 보고한 입금)은 금액을 현재 환율로 애플리케이션의 기본 통화로 환산하여 판단합니다. 환율이 없는 자산과 샌드박스 애플리케이션의 모든 전송은 임계값을 초과하는 것으로 간주합니다. |
| `AUTOMATED` | 주소록과 최종 사용자의 이전 증명이 자체적으로 판단하며, `allow_self_declaration`이 `true`인 동안에는 자기 신고도 포함됩니다. 이것이 기본값이며, 모드가 도입되기 전 모든 애플리케이션이 사용하던 방식입니다. |

지갑은 검증되면 최종 사용자별로 기억됩니다.
`FULL`과 `SIMPLIFIED`에서는 이후 전송이 해당 전송에 대해 모드가 인정하는 증명으로 뒷받침될 때만 그 지갑을 재사용합니다.
`inbound_auto_accept`에서 일치시킬 주소록 지갑이 없으면, 귀하의 거래에서 정확히 한 최종 사용자에게만 기록된 암호화폐 지갑이 보유자를 식별합니다.
Didit은 해당 지갑을 귀하의 자기 신고로 최종 사용자의 주소록에 추가하며, 모드가 `FULL`이 아니라면 검증됨으로 표시합니다. 같은 체인의 다른 이름(`eth`와 `ethereum`)으로 주소록에 이미 등록된 지갑은, 다른 VASP가 기록한 지갑이라도 절대 추가하지 않습니다.
다른 VASP가 전송 전에 수행하는 주소 확인(`GTR`, `Sygna`, `VerifyVASP` 경유)도 이 모드를 따릅니다. `FULL`에서는 최종 사용자가 자기 신고만 한 지갑을 귀하의 지갑으로 보고하지 않습니다.

### 데이터 공유

`data_sharing_fields`는 개인과 기업에 대해 따로, Didit이 거래 상대방에게 귀하의 사용자에 관해 보낼 필드를 선택합니다:

```json theme={null}
{
  "data_sharing_fields": {
    "individual": ["name", "wallet_address", "address", "date_of_birth"],
    "company": ["name", "wallet_address", "address"]
  }
}
```

* 개인은 `name`, `wallet_address`, `address`, `national_id`, `customer_identification`, `date_of_birth`, `place_of_birth`를 공유할 수 있습니다. 기업은 앞의 다섯 개를 사용합니다.
* `name`과 `wallet_address`는 항상 전송됩니다.
* 선택하지 않은 엔터티 유형은 모든 필드를 공유합니다. 빈 목록도 하나의 선택이며, 이 경우 이름과 지갑만 공유합니다.
* `PUT`은 법인 유형별로 병합되므로, `individual`을 보내도 저장된 `company` 선택은 변경되지 않습니다.
* 귀하의 `jurisdiction` 규제(EU TFR 또는 FATF Recommendation 16)에 따른 송금인 의무를 충족할 수 없는 선택은, 빈 목록을 포함하여 `400`으로 거부됩니다. 오류는 법인 유형별로 키가 지정되어 `data_sharing_fields` 아래에 표시됩니다.

### 필드별 일치

`matching_rules`는 거래 상대방 데이터의 각 필드를 귀하가 보유한 정보와 얼마나 엄격하게 비교할지 설정합니다:

```json theme={null}
{
  "matching_rules": {
    "individual": { "name": "DEFAULT", "date_of_birth": "STRICT", "address": "NONE", "account": "STRICT" },
    "company": { "name": "FUZZY", "address": "NONE", "account": "STRICT" }
  }
}
```

각 값은 `NONE`, `STRICT`, `CONTAINS`, `DEFAULT`, `FUZZY` 중 하나입니다.
기본값은 다음과 같습니다. `name`은 `name_matching_strictness`를 따르고, `date_of_birth`와 `address`는 `NONE`, `account`는 `STRICT`입니다.
생략한 필드는 기본값을 유지하며, 법인 유형이 비교하지 않는 필드(예: 법인에는 `date_of_birth`가 없음)는 `400`으로 거부됩니다.

### 관할권별 임계값

`threshold_amount`가 존재하는 이유는 Travel Rule의 적용 범위가 규제 기관마다 다르기 때문입니다.
Didit의 기본값은 `0.00`(EU 입장)이므로, 직접 올리지 않으면 모든 전송에 대해 정보를 교환합니다:

| 규제 | 임계값 | 비고 |
| - | - | - |
| **EU** - Transfer of Funds Regulation (EU) 2023/1113 | **최소값 없음** | CASP 간 모든 암호화폐 전송이 적용 대상입니다. 1,000유로를 초과하는 전송에서는 자체 보관 지갑의 소유권을 검증해야 합니다. |
| **FATF Recommendation 16** (글로벌 기준) | USD/EUR 1,000 | 이 금액 미만에서는 FATF가 축소된 데이터 세트(이름과 주소, 검증 없음)를 허용합니다. |
| **미국** - BSA Travel Rule | USD 3,000 | FinCEN의 오랜 자금 이체 임계값이며, 교환 가능한 가상 자산에 적용됩니다. |
| **영국** | EUR 1,000 상당액 | 이 금액을 초과하면 전체 정보, 미만이면 축소된 데이터 세트를 사용합니다(MLRs Part 7A). |
| **싱가포르** - MAS PSN02 | SGD 1,500 | 이 금액 미만에서는 축소된 요건이 적용됩니다. |
| **스위스** - FINMA | **최소값 없음** | 모든 전송에 정보가 필요하며, 자체 보관 지갑에 대해서는 소유권 증명이 요구될 것으로 예상됩니다. |

`threshold_amount`는 규제 기관이 정한 금액으로 설정합니다. 이 금액 미만의 전송은 신원 데이터를 교환하지 않고 `NOT_APPLICABLE`로 처리됩니다.
임계값은 변경될 수 있으므로, 이 표를 법률 자문으로 여기지 말고 현재 금액은 컴플라이언스 법률 고문에게 확인해야 합니다.

### IVMS 101 데이터 모델

Travel Rule에 따라 교환되는 데이터의 업계 표준은 **IVMS 101**(interVASP Messaging Standard)입니다.
`travel_rule_details.originator_data`와 `travel_rule_details.beneficiary_data`는 IVMS 101 형식의 개인 페이로드입니다. Didit은 각 규약의 고유 봉투(envelope)에 이 데이터를 담습니다. TRP 규약은 `IVMS101` 블록(`ivms101.2023`, 전송의 `ivms_version` 필드에 보고됨)을 교환하고, Sygna 규약은 ECIES로 암호화된 IVMS 변형을 사용하며, `GTR`, `TRUST`, `VERIFYVASP` 규약은 각자의 암호화된 봉투를 사용합니다. 따라서 하나의 페이로드 형식으로 모든 거래 상대방이 자신의 프로토콜이 요구하는 형식으로 데이터를 받을 수 있습니다.

Didit은 IVMS 101 메시지를 만들 때 각 개인 페이로드에서 다음 키를 읽습니다: `first_name`과 `last_name`(또는 단일 `name` 또는 `full_name`), `date_of_birth`, `place_of_birth`, `residence_country`(또는 `country`), `vendor_data`(또는 `customer_identification`), `wallet_address`.

두 가지 실무 규칙이 있습니다:

* **라틴 문자.** IVMS 101은 라틴 문자 값을 요구하므로, 본래 다른 문자로 표기된 이름은 음역해야 합니다. Didit의 [이름 일치 검사](#이름-일치-엄격도)는 정규화된 토큰 집합을 비교하므로 순서와 대소문자 차이 때문에 교환이 실패하지는 않습니다. 다만 양측의 문자가 다르면 실패합니다.
* **검증된 데이터를 보내십시오.** `vendor_data`가 가리키는 대상이 승인된 Didit KYC 인증을 가진 사용자이면, Didit은 입력하신 값이 아니라 검증된 신원을 전송합니다. [검증된 데이터와 출처](#검증된-데이터와-출처)를 참조하십시오.
  그 밖의 대상에 대해서는 Didit이 보내신 `originator_data`를 그대로 전송하므로, 검증되지 않은 양식 필드가 아니라 온보딩 시 확인한 신원으로 구성하십시오. FATF의 "required and **accurate** originator information"(필수이며 정확한 송금인 정보) 문구가 바로 이를 요구합니다. `INTERNAL` 규약에서는 수취 측 애플리케이션이 보내신 `subject`(이름, `vendor_data`, 생년월일, 국가, 주소)를 수신 거래의 거래 상대방으로도 받습니다.

#### 검증된 데이터와 출처

전송의 대상이 여러분의 사용자, 즉 승인된 KYC 인증을 받은 개인인 경우, Didit은 해당 측의 데이터를 검증된 데이터로 구성합니다:

* `POST /v3/transactions/`로 생성한 발신 전송의 송금인;
* `POST /v3/transactions/`로 보고한 수신 전송의 수취인, 또는 일치한 지갑의 `holder_vendor_data`가 해당 사용자를 지정하는 경우 `POST /v3/travel-rule/inbound/`로 보고한 수신 전송의 수취인.

`POST /v3/transactions/`에서는 두 경우 모두 Travel Rule이 켜져 있는 동안 `transaction_category`가 `travel_rule`인 거래에 적용됩니다.

검증된 이름, 생년월일과 출생지, 문서 번호와 유형, 주소, 그리고 (그 주소에서 도출한) 거주 국가가 여러분이 보낸 값보다 우선합니다. 검증에 없는 필드는 여러분의 값을 유지하며, 검증된 주소가 전송하기에 충분히 완전하지 않은 경우에는 여러분이 보낸 완전한 주소도 그대로 유지됩니다.

거래에 포함된 `travel_rule` 객체의 전송 `metadata`에는 각 필드의 출처와 여러분이 보낸 값이 검증된 값과 달랐던 위치가 기록됩니다:

```json theme={null}
{
  "metadata": {
    "originator_provenance": { "name": "verified", "date_of_birth": "verified", "customer_identification": "caller" },
    "originator_verified_differences": {
      "name": { "caller": "Jane Doe", "verified": "Jane Elizabeth Doe" }
    }
  }
}
```

`*_provenance`는 각 필드를 `verified` 또는 `caller`에 매핑하고, `*_verified_differences`는 여러분이 검증된 값과 다른 값을 보낸 모든 필드를 나열합니다. 이때 대소문자, 공백, 2자리 또는 3자리 국가 코드의 차이는 무시합니다. 수신 전송의 데이터가 [마스킹](#마스킹된-거래-상대방-데이터)된 동안에는 차이 목록이 제외됩니다. Didit이 두 키를 모두 직접 계산하므로, `travel_rule_details.metadata`에 보내신 이 키들의 사본이나 `name_match`의 사본은 버려집니다.

#### IVMS 101 직접 전송

평면형 `originator_data` 및 `beneficiary_data` 객체 외에도, `travel_rule_details`(`POST /v3/transactions/`에서)와 `POST /v3/travel-rule/inbound/`는 정식 IVMS 101을 받습니다:

* `originatorPersons` 또는 `beneficiaryPersons`에 `naturalPerson` 또는 `legalPerson`을 담고 `accountNumber`를 포함하는 `originator` 및 `beneficiary` 블록;
* `ivms101`(또는 `IVMS101`) 객체 안의 같은 블록;
* `originator_data` 또는 `beneficiary_data`로서의 IVMS 101 개인 하나, 또는 블록 전체 하나.

각 측은 하나의 person 객체를 받습니다. `naturalPerson` 또는 `legalPerson` 중 하나이며, 후자이면 해당 당사자는 법인이 됩니다. 비어 있지 않은 평면형 `originator_data` 또는 `beneficiary_data`는 같은 측의 블록보다 우선합니다.

```json theme={null}
{
  "travel_rule_details": {
    "status": "AWAITING_COUNTERPARTY",
    "beneficiary": {
      "beneficiaryPersons": [
        {
          "naturalPerson": {
            "name": { "nameIdentifier": [{ "primaryIdentifier": "Ruiz", "secondaryIdentifier": "Carlos", "nameIdentifierType": "LEGL" }] }
          }
        }
      ],
      "accountNumber": ["0xBeneficiaryWallet01"]
    }
  }
}
```

잘못된 블록은 경로별 키와 함께 `400`을 반환합니다. 예를 들면 `{"travel_rule_details": {"originator.originatorPersons": ["must be a non-empty list of originator persons."]}}`와 같습니다. `POST /v3/travel-rule/inbound/`에서는 같은 키가 최상위 수준에 놓이며, 래퍼를 사용한 경우에는 `ivms101.` 아래에 놓입니다.

#### 규제 체계별 필수 데이터

Travel Rule이 켜져 있는 동안, 임계값 이상인 발신 전송은 해당 규제 체계가 요구하는 데이터를 포함해야 하며, 그렇지 않으면 `400`을 반환합니다. 여러분이 보낸 측만 검사하며, 검증된 데이터는 송금인에 대해 인정됩니다. 오류는 `travel_rule_details.originator_data` 또는 `travel_rule_details.beneficiary_data` 아래에 표시되며, 예를 들면 `"EU TFR requires the originator name."` 또는 `"FATF Recommendation 16 requires the beneficiary wallet address."`와 같습니다:

| 규제 체계 | 송금인 | 수취인 |
| - | - | - |
| EU TFR(`jurisdiction`이 EU 회원국, 아이슬란드, 리히텐슈타인, 노르웨이, `EU` 또는 `EEA`인 경우이며, 기본값은 `EU`) | 이름과 함께 주소, 공식 문서 번호, 고객 식별 번호를 모두, 또는 생년월일과 출생지를 함께. 법인에는 출생 정보를 대신할 수 있는 항목이 없습니다. | 이름과 지갑 주소 |
| FATF Recommendation 16(그 밖의 모든 관할, 비어 있는 경우 포함) | 이름과 함께 주소, 공식 문서 번호 또는 고객 식별 번호 중 하나, 또는 생년월일과 출생지를 함께. 법인에는 출생 정보를 대신할 수 있는 항목이 없습니다. | 이름과 지갑 주소 |

송금인의 경우, 검사는 애플리케이션이 [`data_sharing_fields`](#데이터-공유)에서 공유하는 필드를 읽습니다. 규약은 이 필드들만 전송하기 때문입니다. 수취인은 보내신 그대로 검사합니다. 전송이 여러분이 공유하지 않는 필드로만 규제 요건을 충족하는 경우, `400`은 해당 규제 체계의 요건을 명시한 다음 설정에서 공유하는 필드를 나열합니다.

공식 문서 번호는 `national_id`(또는 `document_number`), 고객 식별 번호는 `customer_identification`(또는 `vendor_data`), 생년월일은 `date_of_birth`입니다. 검사 전에 비어 있는 `beneficiary_data.wallet_address`는 `counterparty.payment_method.account_id`로 채워지며, Didit이 `travel_rule_details.utxo`에서 추론한 UTXO 출금의 수취 출력은 해당 값을 포함하는 것으로 간주합니다. Travel Rule이 적용되지 않는 Lightning 전송은 검사하지 않습니다.

### Travel Address

**Travel Address**는 VASP의 수신 `TRP` 조회 endpoint를 가리키는 간결한 인코딩 문자열입니다. 원시 URL이나 지갑 주소는 절대 노출하지 않습니다.
Travel Rule을 활성화하면 Didit이 고유한 `travel_address`를 생성합니다(설정 응답에서 읽기 전용으로 반환됨). 이를 `TRP` 방식으로 연결되는 다른 VASP와 공유할 수 있습니다.
주소록 지갑마다 지갑별 `receiving_travel_address`도 생성됩니다. 이 주소로 보낸 조회 요청은 해당 지갑을 지정하므로, Didit이 계좌 번호나 예금주 이름으로 지갑을 찾을 필요가 없습니다.
Didit의 공개 TRP 호스트는 아직 다른 VASP에 응답하지 않으므로, 그때까지 `TRP`로 수신하는 기능은 사용할 수 없습니다. [Travel Rule 네트워크](https://docs.didit.me/ko/transaction-monitoring/travel-rule-networks.md)를 참조하십시오.
거래 상대방의 Travel Address를 알고 있다면, 수취인의 [지갑 주소록 항목](#지갑-주소록)에 저장하십시오. 그러면 Didit이 해당 수취인에게 가는 송금을 이메일로 대체하지 않고 바로 `TRP`로 라우팅합니다.
Didit은 지갑 주소록 항목에 저장하거나 `originating_vasp`로 보내는 Travel Address를 검증합니다. 각 주소는 `ta`로 시작하고 `t=i`가 포함된 조회 URL로 디코딩되어야 합니다. 따라서 `ta...` 같은 자리 표시자는 `400`을 반환합니다.
거래의 `beneficiary_data.travel_address`는 검증하지 않습니다. 디코딩되지 않으면 `TRP` 경로를 건너뜁니다.

인코딩 방식은 `ta` 접두사를 붙인 Base58Check입니다. 엔드포인트 호스트와 경로(항상 `t=i` 쿼리 쌍 포함)에 4바이트 이중 SHA-256 체크섬을 더한 뒤 base58로 인코딩합니다. 실제 주소는 다음과 같이 생겼으며 길이가 약 100자입니다:

```text theme={null}
ta2ih1MicegQ3JqiKz9xT89KsSuwaDJYw8DgzmeSEp1h9tduJcNDZ5SgKLEHeyGMcS1s2HgbM6undFrMQknhM5aTXw3G9KBzi16hx
```

Didit이 Travel Address를 검증하는 경우, 올바른 형식이 아니거나 체크섬이 맞지 않는 값은 `400`과 `"Invalid travel address."`를 반환합니다.
실제 주소는 본인의 설정 응답이나 거래 상대방에게서 받아 사용하십시오.

## 네트워크 자격 증명 없이 테스트하기

멤버십 없이 가능한 경로를 테스트하는 데 GTR, TRUST, VerifyVASP 또는 Sygna 멤버십이 필요하지 않습니다. 다만 테스트 대상 애플리케이션의 Didit API 키는 여전히 필요합니다.

* **샌드박스 발신:** 샌드박스 애플리케이션에서 `travelRule` 거래를 제출합니다. 임계값과 자체 보관 지갑 검사를 거친 후, [Didit Test VASP](https://docs.didit.me/ko/transaction-monitoring/travel-rule-sandbox.md)가 수취인 지갑 또는 이름으로 선택한 시나리오(승인, 거부, 이름 불일치, 연결 불가, 시간 초과 또는 자체 보관)에 따라 Didit 내부에서 응답합니다. 네트워크, 이메일 또는 다른 Didit 고객에게는 아무것도 전송되지 않습니다.
* **샌드박스 수신:** 테스트 VASP가 [테스트 VASP 수신 송금 트리거](https://docs.didit.me/ko/management-api/travel-rule/trigger-test-vasp-inbound.md)로 송금을 보내도록 하거나, [수신 Travel Rule 송금 등록](https://docs.didit.me/ko/management-api/travel-rule/register-inbound.md)으로 직접 등록합니다. 알 수 없는 지갑, 검증되지 않은 지갑, 수취인 이름 불일치, 검증되었고 일치하는 지갑을 테스트하십시오. 네트워크에 연결하지 않고도 실제 수신 결정 흐름을 테스트할 수 있습니다.
* **통제된 프로덕션 애플리케이션 2개:** 두 개의 자체 프로덕션 애플리케이션 사이에서 `INTERNAL` 방식을 사용하거나(수신 측은 검색 가능하며 서명된 메시지 또는 테스트 입금으로 대상 지갑에 대한 통제권을 증명합니다), 통제 가능한 TRP 거래 상대방을 사용하여 상업용 네트워크 자격 증명 없이 두 당사자 간 교환을 테스트합니다. 프로덕션 애플리케이션에는 일반 요금과 알림이 발생할 수 있으므로, 직접 통제하는 테스트용 신원과 거래 상대방만 사용하십시오.

모든 시나리오와 정확한 결과는 [샌드박스에서 Travel Rule 테스트](https://docs.didit.me/ko/transaction-monitoring/travel-rule-sandbox.md)를 참조하십시오. 성공한 샌드박스 또는 로컬 프로토콜 테스트는 상업용 네트워크의 자격 증명, 멤버십 또는 프로덕션 상호 운용성을 검증하지 않습니다. 이를 검증하려면 승인된 네트워크 샌드박스 또는 유효한 멤버십 자격 증명을 갖춘 통제된 프로덕션 교환이 필요합니다.

## 전송하기

[`POST /v3/transactions/`](https://docs.didit.me/ko/transaction-monitoring/transactions)에 전송을 포함하려면 `transaction_category`를 `travelRule`(또는 `travel_rule`)로 설정하고 `travel_rule_details` 객체(별칭: `travelRule`)를 첨부합니다.

`travel_rule_details` 블록 자체는 선택 사항이지만, 블록을 첨부하면 `travel_rule_details.status`는 필수입니다. 이 값이 없는 블록은 `400`을 반환합니다.
애플리케이션에서 Travel Rule이 **활성화**되어 있으면, Didit은 전송한 `status` 값을 무시하고 교환 과정을 직접 진행합니다.

1. 금액이 설정된 `threshold_amount`보다 작으면 전송은 즉시 `NOT_APPLICABLE`로 확정되며, 아무런 교섭도 이루어지지 않습니다.
2. 거래 상대방의 결제 수단이 목적지를 자체 보관 지갑(`payment_method.method_type: "unhosted_wallet"`)으로 선언하면 어떤 경로도 시도하지 않습니다. 전송은 바로 `UNCONFIRMED_OWNERSHIP`으로 넘어가고 지갑 소유권 위젯이 자동으로 발급됩니다([선언된 자체 보관 지갑](#선언된-자체-보관-지갑) 참조).
3. 그렇지 않으면 경로가 우선순위에 따라 실행되며, 거래 상대방에 도달할 수 있는 첫 번째 경로가 교환을 담당합니다.
   * `INTERNAL`: `beneficiary_data.wallet_address`에 대해 서명 메시지 또는 테스트 입금 증명을 보유한 다른 Didit 고객이 정확히 한 명 있어야 하며, 이 증명은 지갑 보유자가 마지막으로 바뀐 이후에 만들어진 것이어야 합니다. 귀하의 애플리케이션 자체는 계산에 포함되지 않습니다. 최종 사용자가 [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)에서 Didit 네트워크 멤버를 선택했다면 그 멤버의 증명만 인정됩니다. 해당 고객의 애플리케이션은 프로덕션 환경(샌드박스 아님)이어야 하고, Travel Rule이 켜져 있으며 `is_discoverable`이 설정되어 있어야 하고, `counterparty_vasp_lei` 또는 `counterparty_vasp_name`으로 지정한 VASP여야 합니다. 자체 선언만 되어 있거나, 스크린샷으로 증명되었거나, 검증되지 않았거나, 주소록에만 등록된 지갑은 `INTERNAL`을 통해 아무것도 받지 못합니다.
   * `TRP`: 전송 자산에 해당 네트워크의 디지털 토큰 식별자 매핑이 있어야 하고([TRP 자산 식별자](#trp-자산-식별자) 참조), 페이로드에 `beneficiary_data.travel_address`가 포함되어 있거나, 목적지의 주소록 항목에 이 값이 저장되어 있거나, 거래 상대방이 [디렉터리](#디렉터리)에서 (`counterparty_vasp_lei` 또는 `counterparty_vasp_name`으로) 확인되고 TRP endpoint를 가지고 있어야 합니다.
   * `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`: **Marketplace**에서 해당 네트워크의 멤버십을 직접 연결했어야 하며, 거래 상대방이 네트워크 ID로 확인되어야 합니다(명시적 필드 또는 디렉터리 항목. [거래 상대방 확인 필드](#거래-상대방-확인-필드) 참조).
     Didit은 아직 이 네트워크들에 플랫폼 멤버십을 보유하고 있지 않습니다(프로바이더로 온보딩 중입니다). 따라서 현재 `disabled_networks`는 이 단계를 바꾸지 않습니다.
   * `EMAIL`: `beneficiary_data.counterparty_vasp_email`이 제공되었거나, `counterparty_vasp_name`이 컴플라이언스 이메일이 있는 디렉터리 항목으로 확인되어야 합니다.
4. 어떤 경로로도 거래 상대방을 확인할 수 없으면 Didit은 [거래 상대방 탐색](#거래-상대방-탐색) 절차를 실행합니다. `vasp_attribution_enabled`가 켜져 있으면 이 절차에 [블록체인 분석 귀속](#블록체인-분석-귀속)이 포함됩니다. 목적지 지갑은 소유 VASP로 확인되고, 귀속된 이름은 (비어 있을 때에 한해) `beneficiary_data.counterparty_vasp_name`과 [디렉터리](#디렉터리)에 기록되며, 경로는 한 번 더 실행됩니다. 따라서 TRP endpoint, 네트워크 ID 또는 컴플라이언스 이메일이 있는 디렉터리 항목을 가진 VASP에 귀속된 지갑은 이 두 번째 실행에서 경로가 지정됩니다.
5. 거래 상대방이 여전히 확인되지 않으면 전송은 `COUNTERPARTY_VASP_NOT_FOUND`로 넘어갑니다. 발신 전송의 경우 (`auto_wallet_verification`이 켜져 있다면) Didit은 [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)을 자동으로 발급하므로, 전송은 자체 보관 지갑 전송으로 여전히 완료될 수 있습니다. 더 나은 거래 상대방 데이터를 확보한 후에는 `action: "resend"`로 경로 지정을 다시 실행할 수 있습니다.

1단계와 2단계는 요청 안에서 처리됩니다. 3단계부터 5단계까지는 응답 직후에 실행됩니다. 전송은 `protocol: null`과 함께 `AWAITING_COUNTERPARTY` 상태로 돌아오며, 각 결과는 [`travel_rule.status.updated`](#webhook) webhook으로 전달됩니다.

### TRP 자산 식별자

`TRP`는 전송되는 자산을 ISO 24165 디지털 토큰 식별자(DTI)로 지정합니다.
DTI는 하나의 블록체인에 있는 토큰을 식별하므로, 여러 체인에 존재하는 토큰은 네트워크마다 DTI가 하나씩 있습니다.

Didit은 다음 자산에 대해 `TRP`로 전송합니다.

| 자산 | 네트워크 | DTI |
| - | - | - |
| `BTC` | Bitcoin | `4H95J0R2X` |
| `ETH` | Ethereum | `X9J9K872S` |
| `USDC` | Ethereum | `CK9PW1MFH` |
| `USDC` | Celo | `2ZTG3NNVB` |
| `USDC` | Base | `CP6817N4B` |
| `USDC` | Optimism | `898NNTPZJ` |
| `USDC` | Arbitrum One | `4ZSZS1F4F` |
| `USDC` | Polygon PoS | `LS71LQK3L` |
| `USDC` | Avalanche C-Chain | `45J6XJX7H` |
| `USDC` | Solana | `5R3XFPZSM` |
| `USDC` | Plume | `N064ZKXDM` |

`USDC` 식별자는 토큰 발행사가 MiCA 백서에 게시한 것과 같습니다.

여러 체인에 존재하는 토큰의 경우, 전송에서 `transaction_details.crypto_params.crypto_chain`에 네트워크를 지정해야 합니다(예: `CELO`, `BASE`, `OP`, `ARB`, `POL`).
네트워크가 없거나 매핑이 없는 네트워크라면 전송은 `TRP`로 보내지지 않으며, Didit은 나머지 경로를 시도합니다. 먼저 상호운용 네트워크를, 그다음 이메일을 시도합니다.

`USDT`는 아직 매핑되지 않았습니다.
DTI 매핑이 없는 토큰(예: 기업 고유의 스테이블코인)은 발행사가 ISO 24165 등록 기관에 DTI를 등록해야 하며, 토큰 하나당 블록체인마다 하나씩 등록해야 합니다.
DTI가 준비되면 각 DTI, 해당 블록체인, 토큰 소수점 자릿수를 Didit 지원팀에 알려 주십시오. Didit이 매핑을 추가합니다.

매핑된 체인별 DTI를 포함한 수신 `TRP` 요청은 토큰의 통화, 토큰 소수점 자릿수로 표시된 금액, 네트워크와 함께 기록됩니다.

### 거래 상대방 탐색

주소만 포함된 전송도, 누군가 그 상대방을 식별할 수 있다면 거래 상대방에게 도달합니다.
발신 전송이 경로를 찾을 수 없다고 선언되기 전에, Didit은 다음 순서로 확인하며, 경로가 있는 VASP를 찾는 첫 번째 단계에서 멈춥니다.

| 단계 | 역할 |
| - | - |
| `internal_address_book` | 다른 Didit 고객이 해당 지갑의 통제권을 증명했으므로(서명 또는 테스트 입금), `INTERNAL` 경로가 그 고객에게 전달합니다. 증명되지 않은 주소록 항목은 인정되지 않습니다. |
| `wallet_attribution` | 지갑의 블록체인 분석 귀속 결과를 경로가 있는 디렉터리 VASP와 매칭합니다. 저장된 결과는 재사용되며 한 번만 과금됩니다. `vasp_attribution_enabled`가 필요합니다. |
| `network_lookup` | "누가 이 주소를 보유하고 있는지"를 알려 주는 Travel Rule 네트워크이며, 애플리케이션이 접근 권한을 가진 경우에 사용됩니다. 현재 이를 제공하는 것은 GTR뿐이며, GTR 레일이 전송할 때 실행됩니다. 그 외의 경우 해당 단계는 사유와 함께 `skipped`로 기록됩니다. |
| `customer_hint` | `beneficiary_data`에 보낸 VASP 이름, LEI 또는 관할권을 경로가 있는 디렉터리 VASP와 대조합니다. `travel_address`나 컴플라이언스 이메일은 각각의 전용 레일을 통해 전달됩니다. |
| `end_user` | 어떤 상대방도 지정되지 않았으므로 최종 사용자에게 묻습니다. 자세한 내용은 아래를 참고하십시오. |

거래의 `discovery` 필드에는 실행된 모든 단계가 해당 `outcome`(`match`, `miss` 또는 `skipped`), 기계가 읽을 수 있는 `reason`, 찾은 `vasp`와 `rail`, 타임스탬프와 함께 나열됩니다.
귀하 또는 최종 사용자가 선택한 VASP는 모든 자동 단계보다 우선합니다. discovery는 자체 추측으로 이를 대체하지 않습니다.

어떤 단계도 거래 상대방을 지정하지 않으면 거래는 `COUNTERPARTY_VASP_NOT_FOUND` 상태로 전환됩니다. VASP가 지정되었지만 그 경로 중 어느 것도 애플리케이션에서 사용할 수 없는 경우에는 `COUNTERPARTY_VASP_NOT_REACHABLE` 상태로 전환됩니다.
그런 다음 거래에는 `reason`(`awaiting_end_user`, `no_directory_match`, `directory_match_no_route`, 또는 최종 사용자가 선택한 멤버가 지갑을 증명하지 않은 경우의 `picked_member_without_proven_wallet`)과 `next_actions` 목록이 포함됩니다:

| `action` | 의미 |
| - | - |
| `select_counterparty_vasp` | 최종 사용자가 [지갑 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)에서 수신 VASP를 선택하거나, 직접 [거래 endpoint](#전송하기)에서 `{"action": "beneficiary_vasp", "beneficiary_vasp": "<directory VASP id>"}`로 지정할 수 있습니다. 거래는 해당 VASP로 라우팅되어 다시 전송됩니다. |
| `declare_self_hosted` | 최종 사용자가 같은 위젯에서 지갑을 자체 보관 지갑으로 선언합니다. |
| `add_vasp_hint` | `beneficiary_data`에 VASP 이름, LEI 또는 `travel_address`를 보낸 다음 다시 전송하십시오. |
| `update_compliance_email` | 첫 번째 이메일이 거부된 경우 상대방의 컴플라이언스 이메일을 다시 제공하십시오. 자세한 내용은 [거래가 중단된 이유](#전송이-멈춘-이유)를 참고하십시오. |
| `resend` | 변경된 내용을 반영하여 discovery를 다시 실행합니다. |

위젯 링크는 최종 사용자가 수행하는 작업의 `url`입니다.
지갑 소유권 작업이 대기 중일 때는 거래의 `action_required` 블록(type `wallet_ownership`)에 동일한 `reason`과 `next_actions`가 담깁니다. 따라서 이미 `action_required`를 따르는 클라이언트는 추가 작업 없이 선택 화면을 표시합니다.

### 거래 상대방 확인 필드

Didit이 거래 상대방에 대해 아는 모든 정보는 `travel_rule_details.beneficiary_data`에서 가져옵니다.
다음 필드는 레일 결정을 좌우하며, 모두 선택 사항입니다:

| 필드 | 적용 대상 |
| - | - |
| `wallet_address` | `INTERNAL` 주소록 일치, `TRP` 주소록 조회, 지갑 소유권 흐름. 생략하면 Didit은 `counterparty.payment_method.account_id`를 사용합니다. |
| `chain` | 두 주소록 조회의 범위를 지정합니다. 항목은 해당 `chain`이 이 값과 같거나(대소문자 구분 안 함) 항목에 `chain`이 없는 경우에만 일치합니다. 생략하면 Didit은 `transaction_details.crypto_params.crypto_chain`, `custom_properties.crypto_chain`, 또는 거래 통화를 대문자로 바꾼 값으로 채웁니다. 단, 이 값이 Didit이 인식하는 체인(예: `ETH`, `BTC`, `SOL`, `TRX`)인 경우에만 적용합니다. 그렇지 않으면 거래에 체인이 없으며, 모든 체인의 항목과 일치합니다. 명시적인 체인이 없는 `USDT` 또는 `USDC` 거래의 경우, Didit은 `transaction_details.payment_reference_id`의 트랜잭션 해시에서 체인을 추론합니다(`0x` 해시는 `ETH`가 됩니다). |
| `memo` | 메모 네트워크에서 `wallet_address`의 대상 태그 또는 메모입니다. `wallet_address`가 일치하는 모든 곳에서 지갑의 일부로 취급됩니다([메모 및 태그 네트워크](#메모-및-태그-네트워크) 참고). 별칭: `destinationTag`. |
| `travel_address` | `TRP`를 통해 직접 라우팅합니다. 전달할 수 있는 가장 강력한 신호입니다. |
| `counterparty_vasp_lei` | `TRP`, `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`에 대해 LEI로 디렉터리를 조회합니다. 또한 TRP endpoint가 보고하는 신원과 교차 확인합니다. 불일치하거나 LEI가 없는 신원이 있으면 교환을 `COUNTERPARTY_VASP_NOT_REACHABLE`로 중단합니다. |
| `counterparty_vasp_name` | `TRP`, `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`, `EMAIL`에 대해 디렉터리를 조회합니다. 정확한 이름, 다른 방식으로 표기된 같은 이름(`BV`에 대한 `B.V.`, 악센트 차이), 그다음 항목의 별칭 순으로 확인합니다. 둘 다 있으면 TRP 신원과 퍼지 방식으로 확인합니다. |
| `counterparty_vasp_jurisdiction` | 명명된 VASP가 여러 규제 법인을 가진 브랜드이거나, 디렉터리 항목 여러 개가 같은 이름을 공유할 때 수신 법인의 ISO 3166 국가 코드입니다. [브랜드와 법인](#브랜드와-법인)을 참고하십시오. |
| `counterparty_gtr_vasp_code` | 명시적인 `GTR` 네트워크 ID입니다. 디렉터리를 건너뜁니다. |
| `counterparty_trust_membership_id` | 명시적인 `TRUST` 네트워크 ID입니다. 디렉터리를 건너뜁니다. |
| `counterparty_verifyvasp_id` | 명시적인 `VerifyVASP` 네트워크 ID입니다. 디렉터리를 건너뜁니다. |
| `counterparty_sygna_vasp_code` | 명시적인 `Sygna Bridge` VASP 코드입니다. 디렉터리를 건너뜁니다. |
| `counterparty_vasp_email` | `EMAIL` 레일용 명시적인 컴플라이언스 연락처입니다. 디렉터리를 건너뜁니다. |

### 체인 구조

계정 체인(EVM 체인, Solana, Tron)에서는 거래가 한 주소에서 다른 주소로 자금을 이동시키며, Didit은 거래에서 두 지갑을 직접 읽습니다.
그 밖의 세 가지 구조는 더 많은 정보가 필요하며, Didit이 이들에 대해 확인한 내용은 거래의 `chain_details`에 담깁니다(계정 체인에서는 비어 있음, `{}`).
`chain_details.originator`와 `chain_details.beneficiary`에는 각각 `address`, 해당 `memo`, 그리고 출처인 `source`가 담기며, 일치 및 라우팅은 먼저 여기에서 지갑을 읽습니다.

#### UTXO 거래

Bitcoin, Litecoin, Bitcoin Cash 또는 Dogecoin 거래는 임의의 개수의 주소에서 입력을 사용하고 임의의 개수의 출력으로 지급하며, 그중 일부는 송금인에게 되돌아가는 잔돈입니다.
입력과 출력을 `travel_rule_details.utxo`로 보내십시오(금액은 해당 코인의 고유 단위로, 10진수 문자열로 표기합니다). 그러면 Didit이 선언하지 않은 항목을 추론합니다:

* **송금인**: 보내신 경우 `originator_data.wallet_address`를 사용하고, 그렇지 않으면 가장 많이 기여한 입력 주소를 사용합니다(`source: largest_input`).
* **수취인**: 선언된 지갑이 있으면 그것을 사용하고, 없으면 입력 주소에 잔돈으로 되돌려 주지 않는 유일한 출력(`only_non_change_output`)을 사용합니다. 그것도 없으면 거래 금액을 지급하는 출력 하나(`amount_match`)를 사용합니다.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "btc-withdrawal-0042",
    "transaction_category": "travelRule",
    "transaction_details": {
      "direction": "outbound",
      "amount": "0.25",
      "currency": "BTC",
      "currency_kind": "crypto"
    },
    "subject": { "vendor_data": "user-042", "full_name": "Ana Diaz" },
    "counterparty": { "full_name": "Luis Perez" },
    "travel_rule_details": {
      "status": "AWAITING_COUNTERPARTY",
      "beneficiary_data": { "name": "Luis Perez", "counterparty_vasp_name": "Destination Exchange" },
      "utxo": {
        "inputs": [
          { "address": "bc1qsenderhotwallet01", "amount": "0.30" },
          { "address": "bc1qsendercoldwallet02", "amount": "0.05" }
        ],
        "outputs": [
          { "address": "bc1qbeneficiarydeposit01", "amount": "0.25" },
          { "address": "bc1qsenderhotwallet01", "amount": "0.0999" }
        ]
      }
    }
  }'
```

이후 전송에는 다음이 포함됩니다:

```json theme={null}
{
  "chain_details": {
    "chain": "BTC",
    "shape": "utxo",
    "originator": { "address": "bc1qsenderhotwallet01", "memo": "", "source": "largest_input" },
    "beneficiary": { "address": "bc1qbeneficiarydeposit01", "memo": "", "source": "only_non_change_output" },
    "utxo": {
      "inputs": [
        { "address": "bc1qsenderhotwallet01", "amount": "0.300000000000000000" },
        { "address": "bc1qsendercoldwallet02", "amount": "0.050000000000000000" }
      ],
      "outputs": [
        { "address": "bc1qbeneficiarydeposit01", "amount": "0.250000000000000000" },
        { "address": "bc1qsenderhotwallet01", "amount": "0.099900000000000000" }
      ],
      "change_output_indexes": [1],
      "beneficiary_output_index": 0
    }
  }
}
```

`chain_details.warnings`는 맞지 않는 부분을 표시합니다: `declared_originator_not_an_input`, `declared_beneficiary_not_an_output`, `beneficiary_output_ambiguous`(단일 출력을 구분할 수 없음), `beneficiary_output_amount_differs`.
다른 체인에서 `utxo`를 사용하면 `400`이 반환되며, 각 목록은 최대 1,000개 항목을 담을 수 있습니다.
보고된 입금도 동일한 `utxo` 객체를 사용합니다([입금 후 sunrise 등록](#입금-후-소급-등록) 참조).

#### 메모 및 태그 네트워크

XRP, Stellar(XLM), EOS, Cosmos(ATOM), Hedera(HBAR)에서는 거래소가 모든 고객의 입금을 하나의 공유 주소로 받고, 목적지 태그 또는 메모로 고객을 구분합니다.
따라서 메모는 지갑의 일부입니다. 참여자의 `payment_method.memo`로 보내거나, `originator_data` / `beneficiary_data`의 `memo`(별칭 `destinationTag`)로 보내면, Didit이 주소와 메모를 함께 비교합니다.

* 메모가 있는 주소록 항목은 해당 메모를 지정한 전송에만 일치합니다. 메모가 없는 항목은 주소 전체와 일치합니다. 메모를 지정하지 않은 전송은 메모가 있는 항목과 절대 일치하지 않습니다. 공유 주소에서 태그 없이 들어온 입금은 누구의 것인지 알 수 없습니다.
* XRP X-address에는 태그가 그 안에 포함되어 있습니다. Didit은 이를 클래식 주소와 태그로 분리하므로(`source: x_address`), 어느 형식으로 등록된 지갑이든 다른 형식의 전송과 일치합니다.
* 공유 주소의 각 메모는 거래에서 별도의 결제 수단으로 처리됩니다.
* 네트워크별 허용 형식은 다음과 같습니다. XRP 목적지 태그는 0에서 4294967295 사이의 정수입니다(JSON 숫자도 허용됩니다). Stellar 메모는 최대 28바이트의 텍스트, 64비트 ID 또는 16진수 32바이트 해시여야 합니다. EOS는 최대 256바이트, Cosmos는 최대 256자, Hedera는 최대 100바이트를 받습니다. 메모를 지원하지 않는 체인(Bitcoin, EVM 체인, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin, Lightning)에서 메모를 보내면 `400`이 반환됩니다.

#### 라이트닝

라이트닝 결제에는 온체인 수취인 주소가 없습니다. 결제자가 결제 요청을 가지고 있습니다.
bitcoin 또는 라이트닝 암호화폐 전송에서 `payment_method.account_id`에 해당 결제 요청을 넣을 수 있으며, Didit은 네트워크에 연결하지 않고 이 값으로 수취인을 식별합니다.

| 보내는 값 | 수취인 |
| - | - |
| BOLT11 인보이스(`lnbc...`) | 인보이스에 명시되었거나 서명에서 복구된 수취 노드. |
| BOLT12 오퍼 또는 인보이스(`lno1...`, `lni1...`) | 발행 노드. 단, 블라인드 경로 뒤에 숨겨져 있는 경우는 제외합니다. |
| 라이트닝 주소(`name@domain`) | 주소 자체. |
| 노드 ID | 노드 자체. |
| LNURL(`lnurl1...`, `lnurlp://`) | 없음. 수취인이 아니라 서비스를 가리키기 때문입니다. |

수취인은 계정과 `chain_details.beneficiary`(`source: invoice_payee`)에 `LIGHTNING` 체인으로 저장되며, 요청에 기재된 내용은 `chain_details.lightning`에 기록됩니다.
요청에 수취인이 명시되지 않은 경우, 발신 전송은 `NOT_APPLICABLE` 상태가 되며, 사유는 `chain_details.not_applicable_reason`과 `timeline`에 기록됩니다: `LIGHTNING_NO_PAYEE_IDENTIFIER`, `LIGHTNING_BOLT12_BLINDED_PAYEE`, `LIGHTNING_LNURL_NAMES_NO_PAYEE`.
값은 bitcoin에서, `LIGHTNING` 체인에서, 또는 체인이 지정되지 않았을 때만 라이트닝으로 해석됩니다(라이트닝 주소는 bitcoin 또는 `LIGHTNING`에서만 해석됩니다). 그 밖의 체인에서는 해당 체인의 고유 주소로 처리됩니다.

> [!NOTE]
>
> `INTERNAL` 경로는 다른 Didit 고객이 서명된 메시지나 테스트 입금으로 소유를 증명한 지갑을 통해서만 해당 고객에게 도달합니다. 현재 이러한 증명은 Bitcoin, EVM 체인, Solana, Tron에 대해서만 가능합니다.
> 메모 네트워크, 라이트닝, Litecoin, Bitcoin Cash, Dogecoin에서는 전송이 `INTERNAL`을 통해 다른 Didit 고객에게 도달하지 않습니다.
> 다만 메모, 태그, 라이트닝 매칭은 여전히 고객님의 주소록에 적용됩니다. 즉, 고객님의 지갑으로 들어오는 수신 전송, 보고된 입금, `TRP` 조회가 대상입니다.

### 발신자 지갑 업데이트

출금은 흔히 고객의 주 지갑에서 선언되지만, 실제로 코인을 보유하고 있던 다른 주소에서 브로드캐스트됩니다.
자금이 실제로 빠져나간 지갑은 `PATCH /v3/transactions/{transaction_id}/travel-rule/`로 기록합니다:

```bash theme={null}
curl -X PATCH "https://verification.didit.me/v3/transactions/$TRANSACTION_ID/travel-rule/" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "action": "update_originator_wallet", "wallet_address": "bc1qsendercoldwallet02" }'
```

* 발신 전송에만 사용할 수 있으며, `CANCELLED` 또는 `EXPIRED` 상태의 전송에는 사용할 수 없습니다(`409`).
* 전송의 UTXO 입력을 알고 있는 경우, 지갑은 그 입력 중 하나여야 합니다(그렇지 않으면 `400`). 메모 네트워크에서는 `memo`도 함께 보내십시오.
* 이 요청은 기록만 변경합니다. 거래 상대방에게 다시 전송되지 않으며 상태도 그대로 유지됩니다. `chain_details.originator`는 `source: updated`와 함께 새 지갑이 되고, `timeline`에는 이전 지갑과 새 지갑이 모두 기록되며, 전송의 `INTERNAL` 상대 기록도 같은 방식으로 업데이트됩니다.

### 블록체인 분석 귀속

지갑 주소만 전달하더라도 Didit은 거래 상대방이 누구인지 알아낼 수 있습니다.
어떤 경로로도 전송을 라우팅할 수 없는 경우, Didit은 목적지 지갑을 블록체인 분석 귀속 데이터와 대조합니다. 이는 [암호화폐 거래 스크리닝](https://docs.didit.me/ko/transaction-monitoring/aml-screening.md)을 뒷받침하는 것과 같은 분석으로, 어느 지갑이 어느 거래소와 커스터디 기관에 속하는지 알고 있습니다.
귀속 조회는 목적지 지갑과 `chain`이 모두 포함된 발신 전송에서만 실행됩니다([거래 상대방 식별 필드](#거래-상대방-확인-필드) 참조). 체인이 없으면 건너뜁니다.

지갑이 알려진 엔터티에 귀속되면 Didit은 다음을 수행합니다:

1. 전송에 귀속 정보를 기록합니다. 고객님이 값을 제공하지 않았다면 `beneficiary_data.counterparty_vasp_name`이 채워지며, 전송의 메타데이터에는 엔터티 이름, 엔터티 유형, 국가, `source: "blockchain_analytics"`를 담은 `vasp_attribution` 블록이 포함됩니다.
2. 엔터티를 [VASP 디렉터리](#디렉터리)에 추가하거나(기존 항목과 일치하는 경우 해당 항목을 사용), 이후 전송에서도 귀속 결과가 일관되도록 합니다.
3. 귀속된 이름으로 라우팅 경로를 한 번 더 실행합니다. 이미 TRP 엔드포인트를 가진 디렉터리 항목이 있으면, 전송은 `COUNTERPARTY_VASP_NOT_FOUND`로 넘어가지 않고 즉시 라우팅됩니다.

귀속 호출은 완전한 암호화폐 스크리닝 역할도 합니다. 거래는 위험 점수, 지갑 위험 범주, 네트워크 스냅샷을 포함한 전체 스크리닝 결과를 받으며, 암호화폐 모니터링이 실행된 것과 동일합니다. 또한 거래의 `custom_values.aml_screening_trigger`는 `travel_rule_attribution`으로 설정되므로, Travel Rule 라우팅이 필요해서 스크리닝이 실행되었음을 확인할 수 있습니다.
같은 거래에서 암호화폐 모니터링이 이미 같은 지갑을 스크리닝했다면, 저장된 결과가 재사용됩니다. 분석 호출이 한 번 더 일어나지 않으며, 요금도 두 번 부과되지 않습니다.
귀속 처리는 거래 생성을 절대 차단하지 않습니다. 분석 조회가 실패하거나 잔액이 비용을 감당할 수 없더라도, 전송은 `COUNTERPARTY_VASP_NOT_FOUND`로 그대로 진행되며, 이 시도에 대해서는 아무 요금도 부과되지 않습니다.
`action: "resend"`인 경우, 이전에 기록된 귀속 결과는 다시 조회하지 않고 재사용됩니다.

[settings](#설정)에서 `vasp_attribution_enabled: false`를 설정하면 이 단계 전체를 비활성화할 수 있습니다.
어트리뷰션 호출의 과금 방식은 [요금](#요금)을 참고하세요.

Travel Rule이 **비활성화**되어 있으면 `travel_rule_details`(호출자가 제공한 `status`(레거시 또는 현재 형식) 포함)가 거래에 계속 기록되지만, 수동적으로만 기록됩니다. 어떤 레일도 실행되지 않습니다.
자체 프로세스로 이미 추적하던 컴플라이언스 데이터를 소급해서 입력할 때 이 방식을 사용하세요.
수동 아웃바운드 전송은 거래 상대방과의 교환을 시작하지 않으므로, [Travel Rule 항목](#요금)이 아니라 Transaction Screening으로 과금됩니다.

Travel Rule이 활성화되어 있으면, 이 요청은 거래 상대방의 컴플라이언스 이메일을 지정하고 이전의 어떤 레일도 수취인에게 도달할 수 없으므로 `EMAIL` 레일로 라우팅됩니다.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "trv-2026-07-05-001",
    "transaction_category": "travelRule",
    "transaction_details": {
      "direction": "outbound",
      "action_type": "transfer",
      "amount": "0.25",
      "currency": "ETH",
      "currency_kind": "crypto"
    },
    "subject": {
      "entity_type": "individual",
      "vendor_data": "user-042",
      "full_name": "Jane Doe"
    },
    "travel_rule_details": {
      "status": "AWAITING_COUNTERPARTY",
      "originator_data": {
        "first_name": "Jane",
        "last_name": "Doe",
        "date_of_birth": "1990-04-12",
        "residence_country": "ES",
        "customer_identification": "user-042",
        "wallet_address": "0x135908330861442a814796ae99feac507925782a"
      },
      "beneficiary_data": {
        "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
        "name": "Ana Diaz",
        "counterparty_vasp_email": "compliance@bene-casp.example"
      }
    }
  }'
```

응답은 여기서 중요한 필드로 줄여서 보여 줍니다.

```json theme={null}
{
  "uuid": "22222222-3333-4444-5555-666666666666",
  "txn_id": "trv-2026-07-05-001",
  "transaction_type": "travelRule",
  "direction": "OUTBOUND",
  "status": "APPROVED",
  "amount": "0.25",
  "currency": "ETH",
  "currency_type": "crypto",
  "travel_rule": {
    "uuid": "33333333-4444-5555-6666-777777777777",
    "status": "AWAITING_COUNTERPARTY",
    "protocol": null,
    "required": true,
    "obligations_count": 0,
    "originator_data": {
      "first_name": "Jane",
      "last_name": "Doe",
      "date_of_birth": "1990-04-12",
      "residence_country": "ES",
      "customer_identification": "user-042",
      "wallet_address": "0x135908330861442a814796ae99feac507925782a"
    },
    "beneficiary_data": {
      "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
      "name": "Ana Diaz",
      "counterparty_vasp_email": "compliance@bene-casp.example",
      "chain": "ETH"
    },
    "metadata": {
      "compliance_email_source": "caller_supplied"
    }
  },
  "action_required": null
}
```

* `originator_data`는 전송하신 값입니다. 예시의 주체에게 승인된 KYC 인증이 없기 때문입니다. 인증된 사용자의 경우 Didit는 인증된 데이터로 이 값을 생성합니다([검증된 데이터 및 출처](#검증된-데이터와-출처) 참고).
  `EMAIL` 레일은 수신자에게 [`data_sharing_fields`](#데이터-공유)에서 공유하도록 설정한 송금인 필드만 보여 줍니다.
* `beneficiary_data.chain`은 `ETH`입니다. 요청에 체인이 지정되지 않아서 Didit가 통화에서 체인을 가져왔기 때문입니다.
* `status`는 `APPROVED`입니다. 어떤 규칙도 이 거래에 작용하지 않았기 때문입니다. 고객님의 규칙에 따라 다른 상태로 설정될 수 있습니다.
* 컴플라이언스 담당자는 수령 링크를 받습니다. 담당자가 응답하면 전송이 완료되거나, 이름 일치 검사에 실패하거나, 거부됩니다. 응답하지 않으면 만료됩니다([이메일 수령](#연결할-수-없는-거래-상대방을-위한-이메일-픽업) 참고).

`protocol`은 응답 이후에 레일이 선택되므로 여전히 `null`입니다(예를 들어 `counterparty_vasp_email`을 제공하면 `EMAIL`). 선택된 후에는 [거래 조회](https://docs.didit.me/ko/management-api/transactions/get.md)에서 확인할 수 있으며, 이후의 모든 `travel_rule.status.updated` webhook에는 `rail`에 포함되어 전달됩니다.
수취인 지갑이 소유권을 입증한 다른 Didit 고객의 지갑이면, `INTERNAL` 레일이 응답 직후 전체 교환을 정산하고, 해당 webhook은 이미 `COMPLETED`(또는 `COUNTERPARTY_MISMATCHED_DATA`)를 보고합니다. 이름이 일치하지 않으면 고객님의 전송은 `COUNTERPARTY_MISMATCHED_DATA`가 되며, 다른 고객은 아무것도 받지 않습니다.
`counterparty_vasp_email`이 없고, 레일과 [어트리뷰션](#블록체인-분석-귀속) 모두 수취인에게 도달할 수 없는 경우, 전송은 나중에 `COUNTERPARTY_VASP_NOT_FOUND`로 끝나고 거래에 `wallet_ownership` 작업이 생성됩니다.

> [!NOTE]
>
> 거래에 내장된 `travel_rule` 객체에는 전송 자체의 필드(`uuid`, `status`, `protocol`, `required`, `obligations_count`, `originator_data`, `beneficiary_data`, `metadata`), Didit가 확인하고 비교한 내용(`chain_details`, `name_match`, `data_match`, `mismatched_fields`, 그리고 [마스킹](#마스킹된-거래-상대방-데이터) 필드 `data_masked`와 `data_unmasked`), 그리고 전송의 현재 위치를 나타내는 필드(`direction`, `rail`, `counterparty_vasp`, `delivery`, `discovery`, `reason`, `next_actions`([전송이 멈춘 이유](#전송이-멈춘-이유) 참고))가 포함됩니다.
> [`GET /v3/transactions/`](https://docs.didit.me/ko/management-api/transactions/list)의 각 행에도 같은 블록이 포함되며, 전송이 없는 거래의 경우 `null`입니다.
> 아래의 전용 전송 엔드포인트도 이 필드들을 반환하며, 추가로 `ivms_version`, `ownership_confirmed`, `deadline_at`, `timeline`, `created_at`을 반환하지만, `obligations_count`와 `metadata`는 반환하지 않습니다.

`COMPLETED` 상태의 아웃바운드 전송을 온체인으로 브로드캐스트한 후 마감하려면 다음과 같이 호출합니다.

```bash theme={null}
curl -X PATCH https://verification.didit.me/v3/transactions/22222222-3333-4444-5555-666666666666/travel-rule/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"payment_txn_id": "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060"}'
```

이 요청은 전송이 **아웃바운드** 전송이고 `COMPLETED` 상태일 때만 가능합니다. 그렇지 않으면 API가 `409`를 반환합니다.
성공하면 아웃바운드 전송과 `INTERNAL` 레일의 대응 전송이 모두 `FINISHED`로 바뀌고, 거래의 `payment_txn_id`가 제공하신 해시로 설정됩니다.
`TRP` 레일의 경우 Didit는 해당 해시를 거래 상대방의 확인 콜백에도 게시합니다.

아직 최종 상태에 도달하지 않은 전송을 취소하려면 같은 엔드포인트에 `{"action": "cancel"}`을 보내세요.
`INTERNAL` 레일의 대응 전송도 함께 취소되며, `TRP` 레일의 경우 거래 상대방이 문의를 승인한 후 Didit가 취소 내용을 해당 확인 콜백에 게시합니다.
이미 `FINISHED`, `CANCELLED`, `EXPIRED` 상태인 전송에 대해서는 `409`가 반환됩니다.
`payment_txn_id`도 지원되는 `action`도 없는 요청은 `400`을 반환합니다.

`GTR`, `VERIFYVASP` 또는 `SYGNA`를 통해 전송된 경우, Didit는 응답 이후 온체인 해시(또는 취소)를 네트워크에 보고하며, 재시도 정책도 [동일하게](#전달-및-재시도) 적용됩니다. 응답의 `delivery`에 해당 메시지가 표시됩니다.

거래 상대방에게 도달하지 못한 아웃바운드 전송, 즉 현재 `COUNTERPARTY_VASP_NOT_FOUND`, `COUNTERPARTY_VASP_NOT_REACHABLE` 또는 `NOT_ENOUGH_COUNTERPARTY_DATA` 상태인 전송을 재시도하려면, 먼저 거래 상대방 데이터를 개선하세요. 예를 들어 대상 지갑 항목에 `travel_address`를 추가하거나, 거래 상대방의 디렉터리 항목에 [연락처](#연락처)를 추가하거나, [디렉터리](#디렉터리)에 VASP를 등록할 수 있습니다.
그런 다음 같은 엔드포인트에 `{"action": "resend"}`를 보내세요.
Didit는 확인 기한을 다시 시작하고, 전송에 저장된 데이터를 사용하여 라우팅 순서를 처음부터 다시 대기열에 넣습니다. 응답에는 전송이 다시 `AWAITING_COUNTERPARTY` 상태로 표시되며(`timeline`에 `resend queued`가 나타남), 결과는 `travel_rule.status.updated` webhook으로 전달됩니다.
이전 시도가 아직 진행 중일 때 보낸 재전송 요청은 그 시도가 끝난 후에 전송됩니다.
그 밖의 상태이거나 인바운드 전송인 경우 `409`가 반환됩니다.

```bash theme={null}
curl -X PATCH https://verification.didit.me/v3/transactions/22222222-3333-4444-5555-666666666666/travel-rule/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action": "resend"}'
```

[디스커버리](#거래-상대방-탐색)가 거래 상대방을 찾지 못했을 때 직접 지정하려면, `{"action": "beneficiary_vasp", "beneficiary_vasp": "<directory VASP id>"}`를 보내세요(`id`는 `GET /v3/travel-rule/vasps/`에서 가져옵니다).
전송은 해당 VASP로 라우팅되어 다시 발송됩니다.
`COUNTERPARTY_VASP_NOT_FOUND`, `COUNTERPARTY_VASP_NOT_REACHABLE` 또는 `NOT_ENOUGH_COUNTERPARTY_DATA` 상태의 아웃바운드 전송만 이 요청을 받을 수 있습니다. 다른 상태이면 `409`가 반환되고, 알 수 없는 id이면 `404`가 반환됩니다.

### 전송이 멈춘 이유

`COUNTERPARTY_VASP_NOT_FOUND` 또는 `COUNTERPARTY_VASP_NOT_REACHABLE` 상태의 전송은 거래를 조회할 때마다 멈춘 이유와 다음 진행 방법을 보여 줍니다.
[`GET /v3/transactions/{transaction_id}/`](https://docs.didit.me/ko/management-api/transactions/get)의 `travel_rule` 블록, [`POST /v3/transactions/`](https://docs.didit.me/ko/management-api/transactions/create)의 `201` 응답, 그리고 [`GET /v3/transactions/`](https://docs.didit.me/ko/management-api/transactions/list)의 각 행에 이 정보가 포함되며, [전송 엔드포인트](https://docs.didit.me/ko/management-api/travel-rule/update-transfer.md)가 반환하는 전송 객체에도 포함됩니다.

* `reason`은 기계가 읽을 수 있는 원인이며, 다른 상태에서는 `""`입니다.

* `next_actions`는 송금을 다음 단계로 진행시키는 작업 목록이며, 다른 상태에서는 `[]`입니다.

* `delivery`는 이 송금이 상대방에게 보내야 하는 가장 최근 메시지입니다. `kind`, `status`(`QUEUED`, `SENDING`, `RETRYING`, `DELIVERED`, `FAILED` 중 하나), `rail`, `attempts`, `last_error`, `next_attempt_at`, `delivered_at`으로 구성되며, 상대방에게 보낼 메시지가 없었다면 `null`입니다.

* `discovery`는 Didit이 거래 상대방을 찾아본 위치이며, 실행한 단계마다 항목이 하나씩 있습니다. `step`, `outcome`(`match`, `miss`, `skipped` 중 하나), `reason`, `vasp`, `rail`, `at`으로 구성됩니다.

`email_undeliverable`과 `delivery_failed`는 실패한 전송에서 발생하며, `COUNTERPARTY_VASP_NOT_REACHABLE`에서만 나타납니다.
실패한 전송이 `reason`을 먼저 결정하므로, Didit이 찾아낸 거래 상대방이 찾지 못한 상대방으로 보고되는 일은 결코 없습니다.
나머지 네 가지는 두 상태 모두에서 탐색 추적 기록에서 발생합니다.

| `reason` | 발생한 상황 | `next_actions` |
| - | - | - |
| `email_undeliverable` | 메일 제공업체가 컴플라이언스 이메일을 거부했고 시도할 주소가 남아 있지 않습니다. 발송 시점, 리마인더 시점, 또는 나중에 보고된 영구 반송 시점 모두 해당합니다. | `update_compliance_email`, 그다음 `resend` |
| `delivery_failed` | 전송을 담당한 레일이 최종적으로 실패했습니다. 네트워크 레일이 메시지를 거부했거나 재시도 횟수를 모두 소진했거나, `TRP` ID 호스트가 응답하지 않았거나, 거래 상대방의 `TRP` 호스트가 문의를 수락하지 않은 경우입니다. | `resend` |
| `awaiting_end_user` | 어떤 탐색 단계도 Didit이 연결할 수 있는 거래 상대방을 지목하지 못했으므로, 최종 사용자에게 상대방을 지정하도록 요청합니다. | `select_counterparty_vasp`, `declare_self_hosted`, `add_vasp_hint`, `resend` |
| `directory_match_no_route` | 한 단계가 [디렉터리](#디렉터리) 항목을 지목했지만, 그 항목에는 Didit이 사용할 수 있는 경로가 없습니다. | `awaiting_end_user`와 동일 |
| `picked_member_without_proven_wallet` | 최종 사용자가 다른 Didit 고객을 거래 상대방으로 선택했지만, 해당 고객은 지갑에 대한 소유권을 증명하지 않았습니다. | `awaiting_end_user`와 동일 |
| `no_directory_match` | 탐색 추적 기록에 다른 사유가 없습니다. | `awaiting_end_user`와 동일 |

| 다음 작업 | 수행할 작업 |
| - | - |
| `update_compliance_email` | 송금 endpoint에 `{"action": "update_compliance_email", "compliance_email": "..."}`를 보내 거래 상대방의 컴플라이언스 이메일을 다시 제공합니다. |
| `resend` | 무언가 바뀐 뒤 `{"action": "resend"}`로 라우팅을 다시 실행합니다. |
| `select_counterparty_vasp` | 최종 사용자가 [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)에서 목적지 VASP를 선택합니다. 송금에 열린 위젯 세션이 있는 동안 `url`은 위젯 링크입니다. |
| `declare_self_hosted` | 최종 사용자가 같은 `url`의 위젯에서 지갑을 자체 보관 지갑으로 선언합니다. |
| `add_vasp_hint` | VASP 이름, LEI 또는 Travel Address(예: 목적지 지갑의 [주소록 항목](#지갑-주소록)에 있는 `travel_address`)로 거래 상대방이 누구인지 Didit에 알린 다음, 재전송합니다. |

재전송을 하면 송금이 다음 단계로 진행되므로 `reason`이 지워집니다.
실패한 전송으로 발생한 상태 전이의 타임라인 항목에도 `reason` 아래에 같은 값이 기록됩니다.

[위의 예시 송금](#전송하기)의 컴플라이언스 이메일을 메일 제공업체가 거부하면, 해당 거래의 `travel_rule` 블록은 요약하면 다음과 같습니다:

```json theme={null}
{
  "uuid": "33333333-4444-5555-6666-777777777777",
  "status": "COUNTERPARTY_VASP_NOT_REACHABLE",
  "protocol": "EMAIL",
  "direction": "OUTBOUND",
  "rail": "EMAIL",
  "counterparty_vasp": null,
  "delivery": {
    "kind": "SEND",
    "status": "FAILED",
    "rail": "EMAIL",
    "attempts": 1,
    "last_error": "email undeliverable",
    "next_attempt_at": null,
    "delivered_at": null
  },
  "reason": "email_undeliverable",
  "next_actions": [
    { "action": "update_compliance_email" },
    { "action": "resend" }
  ]
}
```

여기서 `delivery`는 `reason`과 일치합니다. 전송 자체가 거부되었기 때문입니다.
리마인더 발송에서 거부되었거나 나중에 반송이 보고된 경우에는 `delivery.status`가 `DELIVERED`로 남습니다. 첫 이메일은 실제로 발송되었기 때문입니다.
송금이 멈춘 이유를 알려면 `delivery`가 아니라 `reason`을 확인하십시오.

문제를 해결하려면 거래 상대방의 컴플라이언스 이메일을 다시 입력한 다음 재전송하십시오:

```bash theme={null}
curl -X PATCH https://verification.didit.me/v3/transactions/22222222-3333-4444-5555-666666666666/travel-rule/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action": "update_compliance_email", "compliance_email": "desk@bene-casp.example"}'

curl -X PATCH https://verification.didit.me/v3/transactions/22222222-3333-4444-5555-666666666666/travel-rule/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action": "resend"}'
```

`update_compliance_email`은 해당 주소를 이 송금의 `beneficiary_data.counterparty_vasp_email`로 보관합니다. 이 주소는 이 송금에만 속하며, 어떤 디렉터리 항목이나 연락처 목록에도 추가되지 않습니다.
상태는 바뀌지 않으며, 타임라인에는 `compliance email updated`가 추가되고, 재전송이 새 주소로 요청을 보낼 때까지 송금은 `reason: email_undeliverable`을 유지합니다.
`COUNTERPARTY_VASP_NOT_FOUND`, `COUNTERPARTY_VASP_NOT_REACHABLE`, `NOT_ENOUGH_COUNTERPARTY_DATA` 중 하나의 상태에 있는 발신 송금만 이 요청을 받아들입니다. 그 밖의 송금에는 `409`로 응답하며, `compliance_email`이 없거나 형식이 잘못된 경우에는 `400`으로 응답합니다.

### 선언된 자체 보관 지갑

고객이 목적지가 본인 소유의 자체 보관(언호스티드) 지갑이라고 알려 오면, 라우팅이 뒤에 VASP가 없다는 사실을 찾아내도록 두지 말고 거래에서 이를 선언하십시오. 거래 상대방의 결제 수단 유형을 `unhosted_wallet`으로 설정하십시오.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "trv-2026-07-07-002",
    "transaction_category": "travelRule",
    "transaction_details": {
      "direction": "outbound",
      "action_type": "transfer",
      "amount": "0.25",
      "currency": "ETH",
      "currency_kind": "crypto"
    },
    "subject": {
      "entity_type": "individual",
      "vendor_data": "user-042",
      "full_name": "Jane Doe"
    },
    "counterparty": {
      "entity_type": "external",
      "full_name": "Jane Doe",
      "payment_method": {
        "method_type": "unhosted_wallet",
        "account_id": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9"
      }
    },
    "travel_rule_details": {
      "status": "AWAITING_COUNTERPARTY",
      "beneficiary_data": {
        "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
        "name": "Jane Doe"
      }
    }
  }'
```

이 선언은 라우팅을 완전히 건너뜁니다:

* 레일은 시도되지 않습니다. 정보를 주고받을 거래 상대방 VASP가 없기 때문입니다. 송금은 `UNCONFIRMED_OWNERSHIP` 상태로 바로 생성됩니다.
* `auto_wallet_verification`이 켜져 있으면(기본값), Didit은 거래에 연결된 [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md) 세션을 자동으로 생성하고 이를 거래의 `action_required` 블록에 반환합니다. SDK는 [클라이언트 측 제출](https://docs.didit.me/ko/transaction-monitoring/sdk-transaction-submission.md) 시 이 위젯을 자동으로 실행합니다.
* 고객이 지갑에 대한 소유권을 증명하면, 송금은 수취인 이름 대조를 실행하고 `COMPLETED`로 진행합니다(대조에 실패하면 `COUNTERPARTY_MISMATCHED_DATA`로 진행합니다).

이는 EU TFR의 자체 보관 지갑 의무를 API 형태로 구현한 것입니다. 거래 상대방 VASP와 데이터를 주고받는 대신 언호스티드 지갑의 소유권을 확인합니다.
이 선언은 발신 송금의 **목적지** 측 결제 수단과 수신 송금의 **소스** 측 결제 수단에서 읽힙니다. 어느 경우든 거래 상대방 참여자입니다.
언호스티드로 선언된 송금은 이미 레일을 거치지 않는 최종 경로에 있으므로, `UNCONFIRMED_OWNERSHIP`은 재전송할 수 있는 상태가 아닙니다. `action: "resend"`를 호출하는 대신 소유권 증명을 완료하거나(또는 취소) 하십시오.

## 수신

### 지갑 소유권 확인

송금은 다음 두 가지 경우에 `UNCONFIRMED_OWNERSHIP` 상태로 대기합니다:

* 수신 송금이 아직 소유권이 확인되지 않은 [주소록](#지갑-주소록) 항목으로 확인되는 경우.
* 거래 상대방의 지갑이 자체 보관 지갑으로 선언된 경우(`counterparty.payment_method.method_type: "unhosted_wallet"`). 출금 송금이든 입금 송금이든 해당합니다. [선언된 자체 보관 지갑](#선언된-자체-보관-지갑)을 참고하세요.

귀사의 애플리케이션이 송금을 확인하거나 거부합니다:

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/{transaction_uuid}/travel-rule/ownership/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirmed": true}'
```

요청 본문에는 `confirmed`(필수, 불리언)와 선택 항목인 `wallet_address`를 전달합니다.

이 endpoint는 다음 상태의 송금을 받아들입니다. 그 밖의 상태에서는 `409`와 `{"detail": "Transfer is not awaiting ownership confirmation."}`을 반환합니다:

* 방향과 관계없이 `UNCONFIRMED_OWNERSHIP` 상태인 송금.
* 수신 송금의 `ON_HOLD` 상태.
* 수신 `TRP` 송금의 `UNCONFIRMED_OWNERSHIP`, `ON_HOLD`, `COUNTERPARTY_MISMATCHED_DATA` 상태. 컴플라이언스 담당자가 거래 상대방의 문의를 직접 해결할 수 있도록 하기 위함입니다.

Didit이 직접 생성한 송금(`INTERNAL` 레일, 선라이즈 등록, 또는 선언된 자체 보관 지갑)의 경우:

* `confirmed: true`는 일치하는 지갑 주소록 항목을 소유권 확인 완료로 표시한 다음, 해당 항목의 `holder_name`으로 이름 일치 검사를 실행합니다. 통과하면 `COMPLETED`, 통과하지 못하면 `COUNTERPARTY_MISMATCHED_DATA`가 됩니다.
  `INTERNAL` 레일의 대응 송금이 있는 경우에도 같은 결과가 적용됩니다.
  선언된 자체 보관 지갑의 경우 `confirmed: true`는 귀사의 고객이 거래 상대방 지갑의 실제 제어권자를 확인했음을 기록하고, 출금이든 입금이든 송금을 완료(`COMPLETED`)합니다.
  입금이 이미 정산된 수신 송금은 곧바로 `FINISHED`로 넘어갑니다.
* `confirmed: false`는 송금 양쪽을 모두 `COUNTERPARTY_VASP_GENERAL_DECLINE` 상태로 바꿉니다.
* 일치하는 항목은 체인을 고려하지 않고 지갑 주소만으로 귀사의 주소록에서 조회합니다.
  일치하는 항목이 없는 수신 `ON_HOLD` 송금을 해결하면, 송금의 `beneficiary_data.wallet_address`로 항목을 만들고 확인 완료로 표시합니다.
  선언된 자체 보관 지갑은 예외입니다. 아래에서 설명하듯이 이 지갑은 소유권이 확인된 상태로 귀사의 주소록에 등록됩니다.
  그 밖의 경우 항목이 없으면 `409`와 `{"detail": "wallet address entry no longer available"}`을 반환합니다. 삭제된 항목에 대해서도 같은 응답이 반환됩니다.

수신 `TRP` 송금의 경우 같은 호출이 거래 상대방의 문의에 응답합니다:

* `confirmed: true`에는 반환할 결제 주소가 필요합니다. 이 주소는 요청의 `wallet_address`, 그다음 일치한 항목, 마지막으로 `beneficiary_data.wallet_address` 순서로 가져옵니다.
  세 곳 모두에 주소가 없으면 요청은 `400`과 `{"wallet_address": ["wallet_address is required to approve."]}`를 반환합니다.
  Didit은 항목을 생성하거나 확인 완료로 표시하고, 거래 상대방에게 문의 해결 콜백을 보내며, `ownership_confirmed`를 설정한 뒤 송금을 `COMPLETED`로 이동합니다.
* `confirmed: false`는 거부 응답을 보내고 송금을 `COUNTERPARTY_VASP_GENERAL_DECLINE`로 이동합니다.
* 거래 상대방의 콜백 endpoint에 연결할 수 없으면 요청은 `502`를 반환하고 송금은 현재 상태를 유지합니다.
  승인하는 경우에도 지갑 주소록 항목은 여전히 생성되거나 확인 완료로 표시됩니다.

선언된 자체 보관 지갑의 경우, 확인하는 지갑은 거래 상대방의 지갑입니다. 출금 송금에서는 출금 대상 지갑이고, 입금 송금에서는 출처 지갑(송금인의 지갑)입니다.
Didit은 이 지갑을 거래 상대방의 이름으로 소유권 확인 완료 상태에서 지갑 주소록에 추가하므로, 지갑은 거래 상대방의 이름(입금의 경우 송금인의 이름)과 대조됩니다.
주소록에 같은 지갑이 이미 다른 보유자 이름으로 들어 있으면, 송금은 대신 `COUNTERPARTY_MISMATCHED_DATA`로 이동합니다.

송금에 거래 상대방 지갑이 전혀 명시되어 있지 않다면 요청 본문에 `wallet_address`를 전달하세요.
송금과 요청 모두 지갑 주소를 제공하지 않으면 요청은 `400`으로 거부됩니다.

```json theme={null}
{"confirmed": true, "wallet_address": "0xCounterpartyWallet01"}
```

* 네트워크를 통해 이미 응답이 끝난 송금(VerifyVASP 송금, 또는 이미 결정된 GTR 또는 Sygna Bridge 송금)은 `409`를 반환합니다([네트워크를 통한 송금](#네트워크를-통한-송금) 참고).

응답은 위에서 설명한 더 상세한 송금 객체입니다.
새로운 `INTERNAL` 송금은 여기서 대기하지 않습니다. 지갑 소유를 이미 입증한 고객에게만 전달되기 때문입니다. 이 규칙이 적용되기 전에 생성되어 아직 대기 중인 이전 `INTERNAL` 송금은 지갑 제어권(서명된 메시지 또는 테스트 입금)을 증명할 때까지 발신자의 정보를 숨기며, 증명하기 전까지 확인 요청은 `409`를 반환합니다. 거부는 여전히 가능합니다.
거래 상대방이 지갑이 자체 보관 지갑이라고 응답한 경우(현재 샌드박스에서는 [Didit Test VASP](https://docs.didit.me/ko/transaction-monitoring/travel-rule-sandbox.md)가 이 응답을 제공합니다), 확인하면 수취인의 이름으로 지갑을 주소록에 추가하고 이름 일치 검사를 실행합니다. 일치하면 송금이 완료되고 지갑은 확인 완료로 표시됩니다.

### 네트워크를 통한 송금

Didit은 거래 상대방이 `GTR`, `VERIFYVASP` 또는 `SYGNA`를 통해 보내는 Travel Rule 송금도 수신할 수 있습니다.
수신 기능은 Didit이 네트워크별로 따로 켭니다. **현재 어떤 네트워크도 켜져 있지 않습니다.** 각 네트워크에 Didit의 endpoint를 등록해야 하며, Didit은 아직 이 네트워크들의 회원 자격이 없기 때문입니다.
`TRUST` 송금은 수신할 수 없습니다.

네트워크의 수신 기능이 켜지면, 네트워크가 전달하는 메시지는 다음과 같이 처리됩니다:

* **라우팅.** 메시지가 해당 네트워크에서 귀사가 연결한 회원 자격의 참가자 코드를 지정하면 귀사의 애플리케이션에 전달됩니다. Didit 자체 회원 자격을 수신인으로 지정한 메시지는 수취 지갑의 제어권을 입증한 활성 Didit 고객이 귀사뿐인 경우에만 귀사에 전달됩니다. 그 밖의 메시지는 모두 거부되므로, 어떤 고객도 다른 고객의 송금을 받지 않습니다.
* **결정.** Didit은 귀사의 애플리케이션에 수신 송금을 생성하고, [TRP 문의](https://docs.didit.me/ko/transaction-monitoring/travel-rule-interoperability.md#trp-endpoint)와 같은 방식으로 결정합니다. 지갑 일치, 소유권, 이름 일치 검사, 그리고 `inbound_auto_accept`를 적용합니다.
* **한 번만.** 네트워크는 응답을 받을 때까지 메시지를 다시 보냅니다. 재전송된 메시지는 첫 번째 메시지가 받은 것과 같은 응답을 받으며, 어떤 것도 두 번 생성되거나 결정되지 않습니다.
* **귀사의 검토(`GTR` 및 `SYGNA`).** 귀사의 검토를 기다리는 송금(`UNCONFIRMED_OWNERSHIP`, `ON_HOLD` 또는 `COUNTERPARTY_MISMATCHED_DATA`)은 [`POST .../travel-rule/ownership/`](#지갑-소유권-확인)로 결정합니다. `confirmed: true`는 승인하며(송금에 지갑이 명시되지 않은 경우 `wallet_address`를 전달하세요), `confirmed: false`는 거부합니다. Didit은 응답 후 [동일한 재시도 방식](#전달-및-재시도)으로 네트워크에 답합니다. 이미 응답된 송금에 대해서는 `409`를 반환합니다.
* **`VERIFYVASP`에서는 나중에 응답할 수 없습니다.** VerifyVASP는 요청할 때 응답을 받으므로, 귀사의 검토가 필요한 송금은 즉시 거부되며 나중에 결정할 수 없습니다(`409`).

### 마스킹된 거래 상대방 데이터

지갑 소유권이 확인되기 전까지 인바운드 송금의 거래 상대방 개인 정보는 누락되지 않고 마스킹되어 반환됩니다.
각 단어의 첫 글자만 남기고, 지갑, 체인, VASP 라우팅 정보는 그대로 읽을 수 있게 유지됩니다.
`data_masked`는 `true`이며, 거래 상세, 목록, CSV 내보내기의 거래 상대방 정보도 같은 방식으로 마스킹됩니다.

소유권이 확인되면 실제 값이 표시되고, `data_unmasked`에 감사 로그 항목이 기록됩니다:

```json theme={null}
{
  "data_masked": false,
  "data_unmasked": { "at": "2026-10-17T10:00:00Z", "reason": "wallet_proof", "actor": "" }
}
```

`reason`은 `ownership_confirmed`, `wallet_proof`, `address_book_match`, `own_verified_wallet`, `inquiry_approved`(`TRP` 조회 또는 `GTR`이나 `Sygna` 메시지를 승인한 경우), `known_payment_method`(자사 기록에 있는 최종 사용자의 지갑이 응답한 경우), `travel_address`(지갑별 Travel Address가 해당 지갑을 지정한 경우) 중 하나입니다.
`INTERNAL` 인바운드 송금은 생성 시점부터 `reason: wallet_proof`가 적용된 `data_unmasked`를 가집니다. 지갑 소유를 증명한 수신자만 이 값을 받기 때문입니다.
`actor`는 콘솔 사용자의 이메일 또는 API 키 레이블이며, 자동 결정인 경우에는 비어 있습니다.
**Travel Rule 송금 로그** 보고서(CSV 및 PDF)는 소유권이 확인되기 전까지 인바운드 송금의 이름을 같은 방식으로 마스킹합니다. 규제 기관에 제출하는 보고서인 goAML XML과 규제 보고서 PDF에는 거래 상대방의 실제 데이터가 포함됩니다.

### 불일치 피드백

`COUNTERPARTY_MISMATCHED_DATA` 송금은 이제 서로 다른 필드를 알려줍니다.
`data_match`에는 비교 결과가, `mismatched_fields`에는 필드 목록이 담깁니다.
두 값 모두 필드 이름과 결과만 포함하고 실제 값은 포함하지 않으므로, 데이터가 마스킹된 상태에서도 읽을 수 있습니다:

```json theme={null}
{
  "status": "COUNTERPARTY_MISMATCHED_DATA",
  "mismatched_fields": ["date_of_birth"],
  "data_match": {
    "matched": false,
    "mismatched_fields": ["date_of_birth"],
    "fields": {
      "name": { "result": "matched", "strictness": "DEFAULT" },
      "date_of_birth": { "result": "mismatched", "strictness": "STRICT" },
      "address": { "result": "not_compared", "strictness": "NONE" },
      "account": { "result": "matched", "strictness": "STRICT" }
    }
  }
}
```

타임라인 항목은 필드를 알려줍니다. 예를 들어 "수취인의 생년월일이 지갑 소유자와 일치하지 않음"처럼 표시됩니다.
불일치 후 발생한 `TRP` 거절은 발신 VASP에 서로 다른 필드를 알려줍니다. 예를 들어 "수취인 데이터가 지갑 소유자와 일치하지 않음: name"이라고 표시됩니다.
상대 VASP가 알게 되는 정보는 의도적으로 제한됩니다. 이름이 일치하지 않는 동안에는 이름만 보고되며(`mismatched_fields: ["name"]`, 다른 모든 필드는 `not_compared`), 이름이 일치하면 일치하지 않는 모든 필드가 보고됩니다. 이 규칙은 `INTERNAL` 교환에서 발신자 측과 `TRP` 거절에 적용됩니다.
양쪽 모두 이름이 없는 경우에는 `not_compared`로 표시되며 불일치로 간주되지 않습니다. 국가는 가리키는 국가를 기준으로 비교하므로 `ES`와 `ESP`는 일치하는 것으로 봅니다.

#### 이름 일치 점수

이름이 비교된 모든 송금에는 `name_match`가 포함되며, 그 결과를 결정한 타임라인 항목에는 `detail` 옆에 같은 객체가 포함됩니다:

```json theme={null}
{
  "name_match": {
    "score": 90,
    "matched": false,
    "method": "sequence_ratio",
    "strictness": "FUZZY",
    "compared_with": "wallet_holder",
    "transfer_name": "Ana Diaz",
    "compared_name": "Ana Diaz Garcia",
    "matched_names": ["Ana", "Diaz"],
    "mismatched_names": ["Garcia"]
  }
}
```

* `score`는 두 전체 이름의 유사도를 0에서 100 사이로 나타내며, 어느 한 쪽이 비어 있으면 `0`입니다. `matched`는 `strictness` 기준의 결과이므로 두 값이 서로 다를 수 있습니다. 예를 들어 `FUZZY`는 `0.85`인 자체 유사도 비율만으로 판단합니다.
* `matched_names`는 송금 이름의 단어 중 상대 이름에서 찾은 단어이고, `mismatched_names`는 한쪽에만 있고 다른 쪽에는 없는 단어입니다.
* `method`는 `none`, `exact_token`, `token_subset`, `token_containment`, `sequence_ratio` 중 하나이며, 각각 `NONE`, `STRICT`, `DEFAULT`, `CONTAINS`, `FUZZY`에 대응합니다.
* `compared_with`는 `wallet_holder`(지갑에 기록된 소유자) 또는 `counterparty_vasp`(이메일 응답 페이지에서 거래 상대방이 응답한 이름, 또는 Didit Test VASP의 응답)입니다.
* 송금 결과는 모든 필드를 비교하는 `data_match`를 따릅니다. 따라서 `name_match.matched`가 `true`여도 송금은 `COUNTERPARTY_MISMATCHED_DATA` 상태일 수 있습니다.
* `name_match`는 Travel Rule endpoint가 반환하는 송금 객체와 거래에 포함된 `travel_rule`에 있으며, 이름이 비교되기 전까지는 `null`입니다. 송금 목록과 webhook에는 포함되지 않습니다.
* 당사자가 [마스킹](#마스킹된-거래-상대방-데이터)된 상태에서는 이름이 숨겨지며, `INTERNAL` 교환에서 다른 고객이 갖는 사본에서도 숨겨집니다.

### 메시지 및 문서 요청

두 VASP는 이메일 대신 Didit 안에서 분쟁 중인 송금을 해결할 수 있습니다.
`GET`과 `POST /v3/transactions/{transaction_id}/travel-rule/messages/`로 송금의 메시지를 조회하고 보냅니다:

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/{transaction_uuid}/travel-rule/messages/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind": "DOCUMENT_REQUEST", "body": "Please confirm the beneficiary date of birth.", "requested_documents": ["Proof of address"]}'
```

* `kind`는 `MESSAGE`(기본값) 또는 `DOCUMENT_REQUEST`입니다. 문서 요청에는 `requested_documents`에 최소 하나의 항목이 필요하며 최대 10개까지 가능합니다. 메시지에는 `body`(최대 5000자) 또는 파일이 필요합니다.
* `reply_to`는 같은 송금의 이전 메시지를 가리킵니다. 문서가 첨부된 상대 측 답장은 그 답장이 응답하는 문서 요청을 충족하며, 해당 답장에는 `fulfilled_at`이 설정됩니다. 자신의 답장으로는 자신의 요청을 충족할 수 없습니다.
* `files`와 함께 multipart로 전송하면 파일을 최대 5개까지, 파일당 10MB 이하의 PDF, PNG, JPEG 또는 WebP 형식으로 첨부할 수 있습니다. Didit은 선언된 파일 형식뿐만 아니라 각 파일의 내용을 확인하며, 그 밖의 파일은 `files`에 대해 `400`으로 응답합니다("Attach PDF, PNG, JPEG or WebP files only.").
* 송금 하나에는 메시지를 최대 100개까지 담을 수 있으며, 그중 최대 20개까지 첨부 파일을 포함할 수 있습니다(양측 합산, 각 메시지당 파일 최대 5개). 어느 한도든 초과하면 요청은 `400`으로 응답합니다. 메시지 첨부 파일은 거래 자체의 문서로 집계되지 않습니다.
* 메시지는 `INTERNAL`과 `EMAIL` 경로에서 작동합니다. `INTERNAL`에서는 다른 Didit 고객이 자신이 속한 쪽에서 메시지를 봅니다. `EMAIL`에서는 거래 상대방에게 이메일이 발송되며, 상대방은 수령 포털에서 응답합니다. 알림 이메일을 보낼 수 없어도 메시지는 저장되며 요청은 `201`로 응답합니다.
* 다음의 경우 요청은 `409`로 응답하며, `detail`에 이유가 담깁니다. `TRP`, `GTR`, `TRUST`, `VerifyVASP` 또는 `Sygna`를 통한 송금(해당 프로토콜은 메시지를 전달하지 않음), 아직 거래 상대방이 없는 송금, 데이터가 지갑 소유자와 일치하지 않은 `INTERNAL` 송금(수신자에게 아무것도 전달되지 않음), 수신자가 지갑 통제권을 아직 증명하지 않은 `INTERNAL` 송금(증명 전까지는 양측 모두에 해당), 거래 상대방에게 더 이상 컴플라이언스 이메일 주소가 없는 `EMAIL` 송금, 수령 링크가 만료되었거나 송금이 종료된 `EMAIL` 송금.
* 거래 상대방이 회신해도 webhook은 발생하지 않습니다. 메시지 엔드포인트를 조회하십시오.
* 거래 상대방에게 새 메시지를 알리는 이메일은 영어로 발송됩니다.

### 지갑 주소록

인바운드 송금(`INTERNAL` 경로, 인바운드 `TRP` 조회, [입금 후 등록](#입금-후-소급-등록))이 이 지갑들과 대조될 수 있도록, 직접 관리하는 지갑을 등록하십시오. `POST`/`GET /v3/travel-rule/wallet-addresses/`와 `PATCH`/`DELETE /v3/travel-rule/wallet-addresses/{entry_uuid}/`를 사용합니다.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/wallet-addresses/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
    "chain": "ETH",
    "holder_name": "Ana Diaz",
    "holder_vendor_data": "user-042",
    "self_declared": true
  }'
```

| 필드 | 유형 | 설명 |
| - | - | - |
| `address` | string | 지갑 주소입니다. 애플리케이션, `chain`, `memo` 조합마다 고유합니다(주소는 대소문자를 구분하지 않고 비교합니다). 중복되면 `400`을 반환합니다. XRP X-address는 클래식 주소와 태그로 저장됩니다. [해시로 가져온](#지갑-일괄-가져오기) 항목에서는 비어 있습니다. |
| `address_hash` | string | 읽기 전용입니다. 소문자로 변환한 주소의 SHA-256 값을 소문자 16진수로 나타냅니다. |
| `memo` | string | 선택 사항입니다. [메모 네트워크](#메모-및-태그-네트워크)에서 지갑의 일부인 목적지 태그 또는 메모입니다. 전체 주소에 대해서는 비어 있습니다. 별칭: `destinationTag`. |
| `chain` | string | 선택 사항인 체인 식별자이며 최대 32자입니다. 전송에 포함되는 것과 같은 토큰(예: `ETH` 또는 `BTC`)을 사용하거나, 모든 체인의 전송과 일치시키려면 비워 두십시오. 아래 참고 사항을 확인하십시오. bitcoin에 등록된 Lightning 수취인(노드 ID 또는 Lightning 주소)은 소문자로 `LIGHTNING`에 저장되며, 여기서 [Lightning](#라이트닝) 전송과 일치합니다. |
| `holder_name` | string | 지갑 보유자의 이름이며, 수취인 이름 일치 검사에 사용됩니다. |
| `holder_vendor_data` | string | 보유자에 대한 귀사의 내부 식별자입니다. |
| `entity_type` | string | 최대 16자의 자유 텍스트이며, 기본값은 `"individual"`입니다. |
| `travel_address` | string | 선택 사항입니다. 이 지갑에 대한 거래 상대방 VASP의 Travel Address를 알고 있다면 입력하십시오. 입력하면 Didit이 이메일 대신 `TRP` 경로를 통해 이 수취인에게 전송을 라우팅할 수 있습니다. |
| `receiving_travel_address` | string | 읽기 전용입니다. 이 지갑에 대한 귀사의 지갑별 Travel Address입니다. 이 지갑으로 송금하는 VASP에 제공하면, 해당 VASP의 TRP 조회에서 지갑이 직접 지정됩니다. 다른 VASP의 지갑으로 기록된 경우, Travel Rule이 꺼져 있는 경우, VASP 프로필을 저장하기 전에는 비어 있습니다. 지갑이 삭제되면 더 이상 응답하지 않습니다. |
| `is_ownership_verified` | boolean | 읽기 전용입니다. 이 주소의 소유권이 증명되었는지 여부입니다([지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md) 또는 자기 선언을 통해). |
| `self_declared` | boolean | 쓰기 전용이며 `POST`에서만 사용합니다. `true`이면 항목이 `SELF_DECLARATION` 증명을 통해 이미 인증된 상태로 생성되며, 이 주소로의 수신 전송은 `UNCONFIRMED_OWNERSHIP` 단계를 완전히 건너뛰고 바로 이름 일치 검사로 진행합니다. 설정에서 `allow_self_declaration`이 꺼져 있으면 `400`을 반환합니다. |

`PATCH`는 `holder_name`, `holder_vendor_data`, `entity_type`, `travel_address`를 받으며, 이 네 필드만 반환합니다.
`DELETE`는 항목을 삭제합니다(`204`). 주소는 다시 등록할 수 있도록 해제되며, 새 전송과의 일치도 즉시 중단됩니다.

> [!NOTE]
>
> 주소 일치 검사는 대소문자를 구분하지 않으며, 메모 네트워크에서는 `memo`도 포함하고, `chain`에 따라 범위가 정해집니다.
> 전송에 체인이 포함된 경우, 항목은 `chain`이 같은 값(대소문자 무시)이거나 비어 있을 때만 일치합니다.
> 체인 이름은 번역하지 않고 텍스트로 비교합니다. 따라서 `ethereum`으로 저장된 항목은 체인이 `ETH`인 전송과 일치하지 않습니다.
> 발신 전송의 체인은 `beneficiary_data.chain`, `transaction_details.crypto_params.crypto_chain`, `custom_properties.crypto_chain`, 통화(예: `ETH`, 또는 `USDT`와 `USDC`의 경우 `payment_reference_id`에서 추론한 체인) 순서로 가져오며, Didit이 인식하는 체인인 경우에만 설정합니다. 입금 후 등록은 `chain` 필드에서 소문자로 가져옵니다.
> 수신 `TRP` 조회는 체인을 무시합니다. 주소로 일치시키며, 조회에 지갑이 지정되지 않은 경우에는 확인된 항목의 수취인 이름과 대조하여 일치시킵니다.
> [`POST .../travel-rule/ownership/`](#지갑-소유권-확인)도 주소만으로 일치시킵니다.
> 여러 항목이 일치하면 `INTERNAL` 경로와 입금 후 등록은 소유권이 확인된 항목을 우선하고, 그다음 가장 오래된 항목을 선택합니다. 발신 `TRP` 조회는 Travel Address를 저장한 항목 중 가장 오래된 것을 선택합니다.
> 수신 `TRP` 조회와 소유권 엔드포인트는 일치하는 항목 중 하나를 사용하므로, 가능하면 주소당 항목을 하나만 유지하십시오.
> 이 주소에 대해 확인된 항목이 다른 애플리케이션 둘 이상에 존재하면, `INTERNAL` 경로는 어떤 항목과도 일치하지 않습니다.

#### 지갑 일괄 가져오기

[`POST /v3/travel-rule/wallet-addresses/import/`](https://docs.didit.me/ko/management-api/travel-rule/import-wallet-addresses)를 사용하면 한 번의 호출로 지갑을 최대 10,000개까지 가져올 수 있습니다. 형식은 JSON(`{"entries": [...]}` 또는 배열만 있는 형태) 또는 헤더 행이 있는 UTF-8 CSV 파일(최대 5MB)입니다.
각 행은 `address`, `address_hash`(소문자로 변환한 주소의 SHA-256, 64자리 16진수 또는 44자리 base64 문자열), 또는 둘 다로 지갑을 지정하며, 위의 항목 필드를 원하는 만큼 추가할 수 있습니다. 주소를 공개하지 않고 지갑을 등록하려면 해시만 보내십시오. 이 항목도 다른 항목과 마찬가지로 해당 주소로의 전송과 일치하며, [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)으로 소유권을 증명할 수 있습니다.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/wallet-addresses/import/ \
  -H "x-api-key: YOUR_API_KEY" \
  -F "file=@wallets.csv;type=text/csv"
```

```text theme={null}
address,address_hash,chain,memo,holder_name,holder_vendor_data
0xBeneficiaryWallet01,,ethereum,,Ana Diaz,user-042
,4f7d2e1c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e,bitcoin,,,user-043
rExchangeSharedAddress01,,XRP,104729,Ana Diaz,user-042
```

가져오기는 멱등성이 있으며, 보낸 순서대로 모든 행의 결과를 보고합니다. 결과는 `created`, `existing`(같은 지갑, 체인, 메모가 이미 주소록에 있으며 변경하지 않음), `duplicate`(앞선 행과 중복되며, `duplicate_of_row`에 해당 행이 표시됨), 또는 `failed`(오류는 `errors`에 표시됨) 중 하나입니다.
잘못된 행 하나가 다른 행을 실패하게 만들지는 않습니다. 실패한 행이 없으면 `200`, 하나라도 실패하면 `207`을 응답하며, 개수는 `total`, `created`, `existing`, `duplicate`, `failed`로 반환합니다.
CSV 셀이 비어 있으면 전송되지 않은 것으로 간주하며, 가져오기가 읽지 않는 열은 무시하므로 다른 도구에서 내보낸 파일도 그대로 가져올 수 있습니다.

`GET /v3/travel-rule/transfers/`에서 각 행에는 두 지갑과 함께 `originator_memo`와 `beneficiary_memo`가 포함됩니다.

### 연결할 수 없는 거래 상대방을 위한 이메일 픽업

어떤 프로토콜 경로로도 거래 상대방에게 도달할 수 없으면, Didit은 `EMAIL`로 대체합니다. 이 경우 `beneficiary_data.counterparty_vasp_email`(또는 `beneficiary_data.counterparty_vasp_name`을 VASP 디렉터리 레지스트리에서 조회해 얻은 이메일)로 일회용 픽업 링크를 보냅니다.
수신자에게 Didit 계정이나 API 키가 필요하지 않습니다. 링크의 토큰이 인증 수단입니다.
요청이 발송되는 방식은 다음과 같습니다:

* **올바른 주소로 발송합니다.** 전달한 `counterparty_vasp_email`이 유일한 수신자입니다. 그 외의 경우 Didit은 최대 세 개의 주소로 발송합니다. 먼저 거래 상대방의 [연락처](#연락처) 중 반송되지 않은 주소(귀사의 연락처가 먼저, 다음은 플랫폼 연락처, 그리고 이전에 응답한 연락처 순서)를 사용하고, 빈 슬롯이 남아 있으면 디렉터리 항목의 컴플라이언스 이메일을 사용합니다. 각 주소로 별도의 이메일이 발송됩니다.
* **귀사의 이름으로 발송합니다.** 이메일은 Didit 주소에서 "*귀사의 법적 명칭 via Didit*"으로 발신되며, 거래 상대방의 관할 지역 언어로 작성됩니다(알려진 언어가 없으면 영어). 링크를 열면 해당 언어로 된 응답 페이지가 열립니다. 귀사는 사본을 받지 않으므로 전송 타임라인을 확인하십시오.
* **리마인더를 보냅니다.** 거래 상대방이 응답할 때까지 Didit은 마감 72시간, 24시간, 6시간 전에 리마인더를 보냅니다. 리마인더는 요청 또는 마지막 리마인더 이후 최소 12시간이 지난 뒤에만 보내며, 마감 이후에는 절대 보내지 않습니다.
* **반송을 처리합니다.** 반송 또는 스팸 신고가 발생하면 해당 연락처를 사유와 함께 유효하지 않은 것으로 표시하며, 전송 타임라인에 기록됩니다. 플랫폼 연락처는 모든 Didit 고객에 대해 유효하지 않게 되며, 귀사 애플리케이션의 연락처는 귀사 애플리케이션에 대해서만 유효하지 않게 됩니다. Didit의 이메일 발송이 특정 주소에 전혀 이루어지지 않게 되면(영구 반송, 스팸 신고 또는 수신 거부 이후), 그 주소의 모든 연락처는 `suppressed`로 유효하지 않음 표시되며, 해당 주소로의 발송이 재개되면 다시 유효해집니다. 발송할 주소가 남아 있지 않으면 전송은 `COUNTERPARTY_VASP_NOT_REACHABLE` 상태가 되며, 타임라인에 수신 메일 서버의 사유가 기록됩니다. 전달한 `counterparty_vasp_email`에서 영구 반송이나 스팸 신고가 발생하면, 그것이 유일한 수신자이므로 즉시 이 상태가 됩니다. 반면 그 주소 또는 디렉터리 항목의 컴플라이언스 이메일에서 일시 반송(`soft_bounce`)이 발생하면 요청은 열린 상태로 유지되어 리마인더가 계속 발송됩니다.

링크를 열면 보류 중인 교환 내역을 보여 주는 호스팅 페이지가 나타나며, 이 페이지는 수취인을 위해 두 개의 공개 endpoint를 호출합니다.
[위의 예시 전송](#전송하기)의 수령 세부 정보는 다음과 같습니다:

```bash theme={null}
curl https://verification.didit.me/v3/travel-rule/pickup/{token}/
```

```json theme={null}
{
  "reference": "33333333-4444-5555-6666-777777777777",
  "requested_at": "2026-07-05T10:00:00Z",
  "originator_vasp_name": "Origin CASP SL",
  "originator_vasp_lei": "529900EXAMPLEORIG001",
  "recipient_vasp_name": "Example Counterparty VASP",
  "language": "es",
  "amount": "0.250000000000000000",
  "currency": "ETH",
  "fiat_amount": "",
  "fiat_currency": "",
  "chain": "ETH",
  "beneficiary_wallet": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
  "status": "AWAITING_COUNTERPARTY",
  "ivms_version": "ivms101.2023",
  "deadline_at": "2026-07-07T10:00:00Z",
  "reminders_sent": 0,
  "contact": "",
  "decline_reasons": ["data_mismatch", "not_our_customer", "wallet_not_hosted", "sanctions", "risk_appetite", "jurisdiction", "insufficient_data", "other"],
  "join_invitation": { "eligible": false, "url": "" },
  "originator_data": { "name": "Jane Doe", "wallet_address": "0x77ccff0cc624a53cd8b8d8703ced826710211ec7" },
  "beneficiary_data": { "name": "Ana Diaz", "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb" },
  "ivms_request": {
    "originator": {
      "originatorPersons": [{ "naturalPerson": { "name": { "nameIdentifier": [{ "primaryIdentifier": "Doe", "secondaryIdentifier": "Jane", "nameIdentifierType": "LEGL" }] } } }],
      "accountNumber": ["0x77ccff0cc624a53cd8b8d8703ced826710211ec7"]
    },
    "beneficiary": {
      "beneficiaryPersons": [{ "naturalPerson": { "name": { "nameIdentifier": [{ "primaryIdentifier": "Diaz", "secondaryIdentifier": "Ana", "nameIdentifierType": "LEGL" }] } } }],
      "accountNumber": ["0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb"]
    }
  },
  "data_masked": false
}
```

`reference`는 전송의 `uuid`입니다. `originator_data`와 이를 바탕으로 만들어지는 IVMS 101 `ivms_request`에는 [`data_sharing_fields`](#데이터-공유)에서 공유하도록 설정한 송금인 필드만 포함됩니다. 단, 이름과 지갑은 항상 포함됩니다. 변환된 금액을 보내지 않았다면 `fiat_amount`와 `fiat_currency`는 비어 있습니다. `contact`는 링크의 `?contact=` 값을 그대로 반환하고, `decline_reasons`에는 `decline_reason`이 받을 수 있는 값이 나열됩니다. `join_invitation.eligible`은 거래 상대방이 Didit 가입 초대를 유효하게 보유하는 동안 `true`입니다. 초대 링크 자체는 이메일로만 전달됩니다.

이 endpoint는 저장된 금액을 그대로 반환하므로, 거래 상세 객체가 사용하는 축약된 형식이 아니라 소수점 18자리 전체를 포함합니다.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/pickup/{token}/respond/ \
  -H "Content-Type: application/json" \
  -d '{"decision": "accept", "beneficiary_name": "Ana Diaz"}'
```

`decision`은 `accept` 또는 `decline`입니다.
`accept`인 경우 전달된 `beneficiary_name`은 귀사의 이름 규칙에 따라 전송의 `beneficiary_data.name`과 비교됩니다. 일치하면 `COMPLETED`, 일치하지 않으면 `COUNTERPARTY_MISMATCHED_DATA`가 됩니다. 거래 상대방은 대신 전체 IVMS 101 응답을 `ivms_response`로 보낼 수 있으며, 이 경우 그 안의 수취인 이름이 `beneficiary_name`보다 우선합니다.
`decline`인 경우 전송은 `COUNTERPARTY_VASP_GENERAL_DECLINE`으로 전환됩니다. 거래 상대방은 `decline_reasons`에서 `decline_reason`을 고를 수 있고, `decline_detail`(최대 512자, `other`인 경우 필수)을 함께 보낼 수 있습니다. 이 내용은 귀사 전송의 타임라인에 기록되며, 예를 들면 `counterparty declined: The beneficiary is not our customer`와 같이 표시됩니다.
응답은 결과 `{"status": "..."}`이며, 거부된 경우에는 여기에 `decline_reason`이 추가됩니다. 거래 상대방이 응답한 후에는 두 endpoint 모두 `410`을 반환합니다.
알 수 없는 토큰이면 두 endpoint 모두 `404`를 반환합니다. 응답 기간이 끝나면 `410`을 반환하며, 기간이 끝나는 경우는 전송이 `AWAITING_COUNTERPARTY` 상태를 벗어났거나, 확인 기한(생성 시점 또는 마지막 재전송 이후 `confirmation_timeout_hours`)이 지난 경우입니다.

거래 상대방은 pickup 링크를 통해 `GET`과 `POST /v3/travel-rule/pickup/{token}/messages/`로 읽고 응답합니다. API 키는 필요하지 않으며, PDF, PNG, JPEG, WebP 첨부 파일만 받습니다. 링크가 만료되거나 전송이 종료되면 `410`을 반환합니다.
귀사가 보낸 메시지는 `author`와 첨부 파일의 `uploaded_by`에 해당 메시지를 보낸 사람이나 API 키가 아니라 귀사의 VASP 이름으로 표시됩니다.

### 매칭되지 않은 입금

송신 VASP의 Travel Rule 데이터보다 인바운드 입금이 먼저 도착할 수 있습니다.
이런 입금은 데이터가 도착할 때까지 `AWAITING_COUNTERPARTY` 또는 `NOT_ENOUGH_COUNTERPARTY_DATA` 상태로 남습니다. transactions 권한이 있는 API 키로 아직 대기 중인 입금을 조회하세요:

```bash theme={null}
curl "https://verification.didit.me/v3/travel-rule/unmatched/" \
  -H "x-api-key: YOUR_API_KEY"
```

응답은 `GET /v3/travel-rule/transfers/`와 같은 행을 사용하며, 송신 VASP의 데이터를 기다리는 인바운드 입금으로 범위가 제한됩니다.

데이터가 별도의 수신 메시지로 도착했다면 직접 두 항목을 연결하세요:

```bash theme={null}
curl -X POST "https://verification.didit.me/v3/travel-rule/unmatched/{transaction_uuid}/match/" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"transaction_id": "<uuid of the received message transaction>"}'
```

`{transaction_uuid}`는 입금입니다. `transaction_id`는 수신한 Travel Rule 메시지의 거래입니다.
호출 결과는 `{ "deposit": ..., "message": ... }`입니다.
두 거래 중 하나라도 더 이상 존재하지 않으면 `404`를 반환합니다. 어느 한쪽이라도 이미 다른 상태로 넘어갔으면 `409`와 `travel_rule_match_conflict`("This deposit can no longer be matched.")를 반환합니다.

### 입금 후 소급 등록

암호화폐 입금이 Travel Rule 교환이 이루어지기 **전에** 온체인에서 확정되는 경우(송신 VASP에 연결할 수 없었거나 과거 입금을 소급 등록하는 경우)에는 `POST /v3/travel-rule/inbound/`로 사후에 등록하세요.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/inbound/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chain": "eth",
    "txid": "0xf103cfc37f6d390b61827987b9dc631b5e1d2bfeb008a780c1e38047ae5d557b",
    "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
    "amount": "500.00",
    "currency": "USDC",
    "originator_data": { "name": "Carlos Ruiz", "wallet_address": "0x77ccff0cc624a53cd8b8d8703ced826710211ec7" },
    "beneficiary_data": { "name": "Ana Diaz" },
    "originating_vasp": {
      "name": "Northwind Digital Assets Ltd",
      "lei": "5493001KJTIIGC8Y1R12",
      "travel_address": "tareGfwAzkySLkFzcqr7AKno3K4VsT3jayMwPvj3ovbFZHzpayHLmgCrAMoLqjfCYRPpvLqr9dSr2VVTFAncwChcMjL4AheNuHCJM8NRcUg"
    }
  }'
```

`originating_vasp.travel_address`는 실제이며 체크섬이 유효한 [Travel Address](#travel-address)여야 합니다. 자리 표시자를 넣으면 `400`과 `"Invalid travel address."`가 반환됩니다.
`originating_vasp.lei`는 비어 있거나 LEI 형식(영문자 또는 숫자 18자, 이어서 숫자 2자)의 20자여야 합니다. 그 밖의 값은 `400`과 `"Invalid LEI: expected 20 alphanumeric characters (ISO 17442)."`를 반환합니다.
Didit은 이 형식만 확인하며, ISO 17442 체크 디지트는 확인하지 않습니다.

| 필드 | 유형 | 설명 |
| - | - | - |
| `chain` | string | 입금이 확정된 체인으로, 최대 32자입니다. Didit은 이 값을 소문자로 바꾼 뒤 [주소록](#지갑-주소록) 항목과 매칭하므로, 항목에 저장된 것과 같은 토큰을 사용하세요(`ETH`로 저장된 항목은 `eth`와 매칭됩니다). 이 값은 입금을 스크리닝할 수 있는지도 결정합니다: `bitcoin`/`btc`, `ethereum`/`eth`, `solana`/`sol`, `tron`/`trx`, `polygon`/`matic`, `bsc`/`bnb`, `litecoin`/`ltc`, `ripple`/`xrp`, `bitcoin-cash`/`bch`, `dogecoin`/`doge`. |
| `txid` | string | 온체인 거래 해시입니다. 중복 제거에 사용되며, 같은 `txid`와 `wallet_address` 조합이면 중복 전송을 만들지 않고 기존 전송을 반환합니다. |
| `wallet_address` | string | 입금을 받은 대상 지갑입니다. XRP X-address는 클래식 주소와 태그로 나뉩니다. |
| `memo` | string | 선택 사항입니다. [메모 네트워크](#메모-및-태그-네트워크)의 입금 목적지 태그 또는 메모입니다. 공유 입금 주소에서는 이 값이 입금이 속한 고객을 식별합니다. |
| `amount` | string (decimal) | 입금 금액입니다. |
| `currency` | string | 입금 자산입니다. |
| `originator_data` | object | 송신 VASP가 알고 있는 경우의 송금인 정보로, 평면 객체입니다. Didit은 `name`(또는 `full_name`), `first_name`, `last_name`, `vendor_data`를 읽습니다. |
| `beneficiary_data` | object | 수취인(귀사 고객)의 정보로, 평면 객체입니다. Didit은 `name`을 매칭된 주소록 항목의 `holder_name`과 비교하고, 여기에 `wallet_address`와 `chain`을 추가합니다. |
| `utxo` | object | 선택 사항이며 [UTXO 체인](#utxo-거래)에만 해당합니다. 입금 거래의 `inputs`와 `outputs`입니다. 송신 지갑은 `originator_data.wallet_address`를 선언한 경우 그 값이며, 그렇지 않으면 가장 큰 입력입니다. |
| `originating_vasp` | object | 송신자를 설명하는 선택 사항 `{ name, lei, travel_address }`입니다. `lei`는 위의 LEI 형식과 일치해야 하고 `travel_address`는 유효한 Travel Address여야 하며, 그렇지 않으면 요청이 `400`을 반환합니다. 이 값은 이 전송의 `metadata.originating_vasp`로 보관되며, 공유 [디렉터리](#디렉터리-항목이-등록되는-방법) 항목을 만들거나 변경하지 않습니다. |

Didit는 인바운드 `travelRule` 거래를 생성합니다. 아직 Travel Rule 송금이 없고 같은 해시와 지갑으로 이미 제출한 입금이 있으면, 그 입금을 재사용합니다. 그런 다음 주소와 메모를 기준으로 송금을 [지갑 주소록](#지갑-주소록)과 대조해 처리합니다:

* 일치하는 항목이 없으면 -> `ON_HOLD`.
  [`POST .../travel-rule/ownership/`](#지갑-소유권-확인)로 해결합니다. `confirmed: true`를 보내면 지갑이 확인된 상태로 주소록에 추가되고 이름 일치 확인이 실행됩니다. 일치하면 송금은 `COMPLETED`를 거쳐 `FINISHED`로 바뀝니다.
* 항목은 있으나 소유권이 증명되지 않았으면 -> `UNCONFIRMED_OWNERSHIP`.
* 수취인 이름이 항목의 `holder_name`과 일치하지 않으면 -> `COUNTERPARTY_MISMATCHED_DATA`.
* 확인된 항목이 있고 이름이 일치하면 -> `COMPLETED`, 이어서 즉시 `FINISHED`(입금은 이미 정산되었습니다).

[위의 예시](#지갑-주소록)에서 등록한 확인된 주소록 항목을 기준으로 응답은 다음과 같습니다:

```json theme={null}
{
  "transaction_id": "44444444-5555-6666-7777-888888888888",
  "txn_id": "tr-in-0xf103cfc37f6d390b61827987b9dc631b5e1d2bfeb008a780c1e38047ae5d557b-6151d81b366e",
  "created": true,
  "travel_rule": {
    "uuid": "55555555-6666-7777-8888-999999999999",
    "status": "FINISHED",
    "direction": "INBOUND",
    "rail": null,
    "protocol": null,
    "ivms_version": "ivms101.2023",
    "required": true,
    "ownership_confirmed": true,
    "deadline_at": null,
    "timeline": [
      { "status": "COMPLETED", "at": "2026-07-06T10:15:02.114208+00:00", "detail": "matched verified wallet address book entry" },
      { "status": "FINISHED", "at": "2026-07-06T10:15:02.131544+00:00", "detail": "on-chain deposit already settled" }
    ],
    "counterparty_vasp": null,
    "originator_data": { "name": "Carlos Ruiz", "wallet_address": "0x77ccff0cc624a53cd8b8d8703ced826710211ec7" },
    "beneficiary_data": { "name": "Ana Diaz", "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb", "chain": "eth" },
    "created_at": "2026-07-06T10:15:02.087412Z"
  }
}
```

새 거래의 `txn_id`는 `tr-in-`에 `txid`의 처음 80자를 붙이고, 여기에 `txid`와 지갑으로 만든 12자리 해시를 더한 값입니다.
`created`는 이 호출이 입금을 등록했을 때 `true`(HTTP `201`)이고, 같은 `txid`와 `wallet_address`가 이미 등록되어 있었을 때는 `false`(HTTP `200`)입니다. 이 경우 기존 송금이 변경 없이 반환됩니다.
확인된 항목 경로에서는 두 상태 전환이 모두 엔드포인트가 응답하기 전에 일어나므로, 동기 응답에 이미 최종 상태인 `FINISHED`가 표시됩니다. 중간 단계인 `COMPLETED`는 송금의 `timeline`에 그대로 기록됩니다.
각 전환은 `travel_rule.status.updated` webhook을 보냅니다(`COMPLETED`와 `FINISHED`가 각각 하나씩 보냅니다).
Didit가 새 거래를 생성했을 때는 먼저 [`transaction.created`](https://docs.didit.me/ko/integration/webhooks#이벤트-유형)를 보내고, 그 뒤에 이 이벤트들을 보냅니다.
등록에는 Travel Rule 또는 모니터링 수수료가 없습니다.
크립토 모니터링이 활성화되어 있고 `chain`이 Didit가 스크리닝할 수 있는 체인이면, 새로 생성된 입금도 스크리닝되며 Wallet Verification & Screening 요금으로 한 번 청구됩니다.

### 지갑 소유권 위젯

송금을 `UNCONFIRMED_OWNERSHIP`에서 벗어나게 하거나, 송금이 도착하기 전에 지갑을 미리 확인하려면 `POST /v3/travel-rule/widget-session/`으로 호스팅 지갑 소유권 위젯을 발급하고, 반환된 `url`을 고객에게 보내세요. 위젯은 메시지 서명(ERC-1271 및 ERC-6492를 통한 EVM 스마트 컨트랙트 계정 포함), Satoshi test, 스크린샷 또는 자가 선언으로 지갑 소유를 증명합니다. 그런 다음 일치하는 주소록 항목을 확인됨으로 표시하여 거래소가 진행할 수 있게 합니다.
스크린샷은 예외입니다. 스크린샷만으로는 지갑이 확인되지 않으므로, 컴플라이언스 팀이 검토한 뒤 [`POST .../travel-rule/ownership/`](#지갑-소유권-확인)로 `UNCONFIRMED_OWNERSHIP` 상태의 송금을 확정합니다.

전체 흐름, 증명 방법, 그리고 중요한 안내(위젯은 반드시 실제 브라우저에서 열어야 하며, 모바일 SDK 웹뷰 안에서는 절대 열면 안 됩니다)는 [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)을 참고하세요.

## 디렉터리

`GET /v3/travel-rule/vasps/`는 공유 디렉터리 항목과 [Didit 네트워크](#didit-네트워크) 멤버를 함께 검색합니다. 각 행에는 현재 애플리케이션이 해당 VASP에 어떤 방식으로 도달할 수 있는지가 표시됩니다.

```bash theme={null}
curl "https://verification.didit.me/v3/travel-rule/vasps/?search=counterparty" \
  -H "x-api-key: YOUR_API_KEY"
```

```json theme={null}
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": "0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91",
      "kind": "directory",
      "name": "Example Counterparty VASP",
      "lei": "529900EXAMPLECPTY001",
      "jurisdiction": "DE",
      "domains": ["counterparty.example"],
      "reachable": true,
      "aliases": [{ "name": "Example Counterparty", "kind": "trading_name" }],
      "networks": [
        {
          "network": "GTR",
          "code": "EXAMPLE01",
          "listed_name": "Example Counterparty VASP",
          "country": "DE",
          "verified_at": "2026-10-12T09:30:00Z",
          "last_seen_at": "2026-10-12T09:30:00Z",
          "source": "staff"
        }
      ],
      "routes": [
        { "rail": "GTR", "status": "unavailable", "reason": "no_membership", "via": "" },
        { "rail": "EMAIL", "status": "available", "reason": "", "via": "contact" }
      ],
      "umbrella": null,
      "entities": [],
      "contacts": [
        {
          "id": "6c1f9b8e-2d4a-4f7e-8b3c-5a9d0e1f2a3b",
          "email": "travelrule@counterparty.example",
          "name": "Travel Rule desk",
          "source": "staff",
          "scope": "platform",
          "verified_at": null
        }
      ],
      "dd_status": "NOT_STARTED",
      "dd_score": 62,
      "dd_score_breakdown": {
        "jurisdiction": { "points": 25.0, "max": 25, "reason": "jurisdiction_not_high_risk", "detail": "DEU is not on the high-risk jurisdiction list" },
        "verification": { "points": 3.8, "max": 15, "reason": "dd_status_not_started", "detail": "due diligence status NOT_STARTED" },
        "protocol": { "points": 6.0, "max": 15, "reason": "email_only", "detail": "reachable over the email fallback only" },
        "lei": { "points": 10.0, "max": 10, "reason": "lei_valid", "detail": "valid LEI on record" },
        "history": { "points": 5.0, "max": 10, "reason": "no_history", "detail": "no exchange history" },
        "essentials": { "points": 7.5, "max": 15, "reason": "no_essential_checks", "detail": "no decided essential check yet" },
        "responsiveness": { "points": 5.0, "max": 10, "reason": "responsiveness_unknown", "detail": "0 decided recent requests, too few to judge" }
      },
      "dd_assessed_at": "2026-10-12T09:30:00Z",
      "dd_score_semantic": "higher_is_safer",
      "risk_level": "MEDIUM",
      "responsiveness": { "status": "UNKNOWN", "answered": 0, "total": 0, "computed_at": "2026-10-12T09:30:00Z" },
      "essential_checks": {},
      "reachable_rails": ["GTR", "EMAIL"],
      "is_didit_customer": false,
      "decision": {
        "decision": "APPROVED",
        "reason": "Licensed in DE",
        "decided_by": "",
        "updated_at": "2026-10-14T10:05:00Z",
        "inherited_from": null
      },
      "entity_screening": {
        "id": "7a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
        "status": "Approved",
        "total_hits": 0,
        "score": null,
        "screened_at": "2026-10-14T09:58:00Z",
        "fresh": true,
        "flagged": false
      },
      "questionnaires": []
    }
  ]
}
```

`search`는 이름이나 별칭의 일부, 정확한 LEI 또는 도메인(`counterparty.example` 또는 해당 도메인의 URL)과 일치합니다. 다음 매개변수로 결과를 좁힐 수 있습니다.

| 매개변수 | 효과 |
| - | - |
| `network` | `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA` 또는 `TRISA`: 해당 네트워크의 멤버 디렉터리에 현재 등재된 항목만 표시합니다. |
| `jurisdiction` | ISO 3166 alpha-2 또는 alpha-3 국가 코드입니다. |
| `reachable` | `true`: 애플리케이션이 사용 가능한 경로를 가진 VASP만 표시합니다. `false`: 현재 도달할 수 없는 항목만 표시합니다. |
| `kind` | `directory`는 공유 항목, `didit`는 Didit 네트워크 멤버를 뜻합니다. |
| `risk` | `LOW`, `MEDIUM` 또는 `HIGH`: `risk_level`이 해당 구간인 디렉터리 항목만 표시합니다. [점수 및 위험 수준](#점수와-위험-수준)을 참고하십시오. |
| `responsiveness` | `RESPONSIVE`, `UNRESPONSIVE` 또는 `UNKNOWN`: `responsiveness.status`가 해당 값인 디렉터리 항목만 표시합니다. [응답성](#응답성)을 참고하십시오. |

Didit 네트워크 멤버는 점수가 매겨지지 않으므로, `risk`와 `responsiveness`는 디렉터리 항목에만 적용됩니다. 결과는 이름순으로 정렬되며, `limit`(기본값 `50`)과 `offset`으로 페이지를 나눌 수 있습니다. 알 수 없는 네트워크, 국가 코드, 위험 구간 또는 응답성 상태는 필드 이름을 명시한 `400`을 반환합니다. `GET /v3/travel-rule/vasps/{vasp_id}/`는 `id`로 행 하나를 반환합니다. 자신의 VASP 프로필은 자신의 결과에 나타나지 않습니다.

`dd_status`, `dd_score`, `dd_score_breakdown`, `dd_assessed_at`, `dd_score_semantic`, `risk_level`, `responsiveness`, `essential_checks`는 디렉터리 항목의 공유 실사 평가를 담습니다. `decision`, `entity_screening`, `questionnaires`는 애플리케이션이 해당 VASP에 대해 직접 기록한 정보이며, 다른 고객은 이를 볼 수 없습니다. 각 항목의 의미는 [거래 상대방 실사](#거래-상대방-실사)를 참고하고, 값에 따라 분기하기보다 [거래 상대방 규칙 필드](#규칙-필드와-프리셋)로 조건을 거십시오. Didit 네트워크 멤버는 점수가 매겨지지 않습니다. `dd_status`는 `COMPLETED`, `dd_score`는 `null`, `risk_level`은 비어 있고, 분석 내역도 비어 있습니다.

### 경로

`routes`는 라우팅 순서에 따라 애플리케이션이 VASP에 전달할 수 있는 방법을 나열하며, 최소 하나가 `available`이면 `reachable`은 `true`입니다.

* 항목에 디렉터리가 검증한 TRP endpoint가 있으면 `TRP`입니다.
* 네트워크가 코드로 VASP를 등재한 경우 `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`입니다. 애플리케이션이 현재 해당 네트워크로 전송할 수 있을 때만 `available`입니다(자체 연결 멤버십은 `via: own_membership`, Didit 멤버십은 `via: didit_membership`). 그렇지 않으면 `reason: no_membership` 또는 `reason: network_switched_off`와 함께 `unavailable`이 되며, VASP에 도달하는 방법으로 표시되지 않습니다. Didit의 자체 네트워크 멤버십은 아직 사용할 수 없으므로, 현재 네트워크 경로는 Marketplace에서 연결한 멤버십을 통해서만 사용할 수 있습니다.
* 항목에 준법 연락처가 있으면 `EMAIL`입니다.
* Didit 네트워크 멤버이면 `INTERNAL`입니다.

`TRISA` 등재는 `networks` 아래에 표시되며, 해당 VASP가 TRISA에서 어떤 식별자를 갖는지 알려 줍니다. 하지만 Didit에는 TRISA 경로가 없으므로 경로로 사용되지 않습니다.

샌드박스 애플리케이션은 [Didit 테스트 VASP](https://docs.didit.me/ko/transaction-monitoring/travel-rule-sandbox.md)에만 전송하므로, 디렉터리 경로를 사용할 수 없습니다. 모든 경로는 `reason: sandbox_application`과 함께 `unavailable`이 되고, 모든 행에는 `reachable: false`가 표시됩니다. `reachable=true`를 지정하면 결과가 반환되지 않습니다.

### 브랜드와 법인

여러 규제 법인을 통해 운영되는 브랜드는 하나의 행으로 표시되며, 그 `entities`는 해당 브랜드의 송금을 받는 법인들입니다. 법인 행은 `umbrella`를 통해 브랜드 행을 가리킵니다. 브랜드를 지정한 송금은 모든 법인 중 하나로 전송되며, 법인은 다음 순서로 선택됩니다. 먼저 `beneficiary_data.counterparty_vasp_jurisdiction`에 명시된 관할 지역의 법인, 그다음 목적지 지갑에 연결된 [블록체인 분석 귀속](#블록체인-분석-귀속)의 법인, 관할 지역이 명시되지 않은 경우 브랜드의 유일한 법인입니다. 선택된 법인에 도달할 수 없으면 다른 법인이나 브랜드로 대신 전송되지 않습니다. 적합한 법인이 없으면(둘 이상이 똑같이 맞거나, 명시된 관할 지역에 해당하는 법인이 없는 경우) 브랜드 자체의 경로를 사용하며, 경로가 없으면 송금은 `COUNTERPARTY_VASP_NOT_FOUND`가 됩니다. 송금인의 데이터를 받을 법인은 추측으로 정하지 않으며, 연락처가 있는 법인이 우연히 선택되는 방식으로 결정되지 않습니다.

거래 명칭이 같은 여러 항목(예: 같은 상호를 쓰는 그룹사)도 같은 규칙으로 구분합니다. 명시된 관할 지역의 항목을 선택하고, 없으면 블록체인 분석 귀속이 연결한 항목을 선택합니다. 둘 다 하나를 고르지 못하면 어떤 항목도 사용하지 않으며, 해당 정보는 디렉터리 항목을 지정하지 않은 것으로 간주합니다.

### 연락처

`contacts`는 `EMAIL` 경로가 메일을 보내는 주소입니다. `scope: platform` 연락처는 모든 Didit 고객에게 제공됩니다. Didit이 직접 관리하거나(`source: staff`), VASP 자체 도메인의 주소가 응답 링크를 통해 요청에 응답한 경우입니다(`source: learned`). `scope: application` 연락처는 애플리케이션 전용입니다. 직접 추가한 연락처(`source: application`)와 VASP 자체 도메인 외부의 주소 중 요청에 응답한 주소(`source: learned`)입니다. 송금 시에는 먼저 애플리케이션 자체 연락처로, 다음으로 이전에 응답한 적이 있는 플랫폼 연락처(`verified_at`이 설정됨), 그다음 나머지 연락처 순으로 메일을 보냅니다.

거래 상대방 연락처 API로 애플리케이션 자체 연락처를 관리합니다.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/counterparty-contacts/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"counterparty": "0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91", "email": "compliance@counterparty.example", "name": "Compliance desk"}'
```

* `GET /v3/travel-rule/counterparty-contacts/`는 애플리케이션이 메일을 보낼 수 있는 모든 연락처를 최신순으로 나열하며, 플랫폼 연락처와 반송된 연락처도 포함합니다. `limit`와 `offset`으로 페이지를 나눕니다. `?counterparty=`와 디렉터리 항목 `id`로 필터링할 수 있으며, UUID가 아닌 값은 `400`을 반환합니다.
* `POST`는 비공개 연락처(`counterparty`, `email`, 선택 사항인 `name`)를 추가하고 `201`을 반환합니다. 항목에 이미 자체 연락처로 있는 주소를 추가하면 해당 연락처를 반환합니다. 존재하지 않는 항목은 `400`(`"No directory entry with this id."`)을 반환합니다.
* `DELETE /v3/travel-rule/counterparty-contacts/{contact_uuid}/`는 자체 연락처 하나를 삭제하며(`204`), 플랫폼 연락처는 삭제할 수 없어 `404`를 반환합니다.

반송되었거나 수신 거부 신고가 접수된 연락처는 `is_invalid: true`이며, `invalid_reason`(`hard_bounce`, `soft_bounce`, `spam_complaint` 또는 `suppressed`), `invalid_detail`, `invalid_at`이 함께 제공됩니다. `EMAIL` 레일은 이러한 연락처를 건너뜁니다. 플랫폼 연락처와 API 키로 추가된 연락처에서는 `created_by`가 비어 있습니다.

### 디렉터리 항목이 등록되는 방법

* ESMA의 암호자산 서비스 제공자(CASP)용 MiCA 임시 등록부를 매주 읽어, 각 제공자의 이름, LEI, 소재 회원국을 가져옵니다.
* 공개 TRISA 디렉터리를 매주 읽어, 각 회원의 이름, 국가, TRISA 등록 정보를 가져옵니다. 디렉터리가 3주 동안 등록 정보를 표시하지 않으면 해당 항목은 삭제됩니다.
* 글로벌 LEI 등록부는 항목의 LEI, 기타 법적 명칭, 국가 정보를 채웁니다. 또한 국가 내 정확한 법적 명칭으로 항목의 LEI를 찾습니다.
* [블록체인 분석 귀속](#블록체인-분석-귀속)은 대상 지갑을 소유한 VASP를 추가하거나 기존 항목과 매칭하며, 귀속된 이름을 별칭으로 유지합니다.
* Didit 직원이 항목을 관리합니다. 직원은 확인된 라우팅 식별자와 네트워크 목록, 컴플라이언스 연락처, 별칭, 브랜드와 그 법인, 중복 항목을 병합하고 항목을 숨깁니다.

고객이 거래 상대방에 대해 보고한 데이터(예: 수신 `originating_vasp`나 Travel Address)는 해당 고객의 거래에만 남으며, 공유 항목을 변경하지 않습니다. 항목은 먼저 LEI로, 그다음 정확한 이름이나 별칭으로 매칭되며, 중복으로 만들지 않고 정보를 보강합니다.

### Didit 네트워크

VASP 프로필은 Didit 네트워크의 구성원이므로, 다음 조건을 모두 충족할 때에만 다른 Didit 고객이 디렉터리와 최종 사용자의 VASP 선택기에서 찾을 수 있습니다.

* 애플리케이션이 프로덕션 환경(샌드박스가 아님)이고 Travel Rule이 켜져 있어야 합니다.
* `is_discoverable`이 `true`이고 `legal_name`이 설정되어 있어야 합니다.
* `compliance_email`이 설정되고 확인되어야 합니다. `POST /v3/travel-rule/settings/compliance-email/confirmation/`을 호출하여 확인 메일을 보내고, 메일의 링크를 열어야 합니다. 주소를 변경하면 새로 확인해야 합니다.
* Didit이 해당 애플리케이션을 테스트, 데모 또는 내부용으로 표시하지 않아야 합니다.

`INTERNAL` 레일 자체는 [거래 전송](#전송하기)에서 설명한 대로 확인된 지갑으로 전달합니다. 이 경우에는 애플리케이션이 프로덕션 환경이고, Travel Rule이 켜져 있으며, `is_discoverable`이 설정되어 있어야 합니다. 위 목록의 나머지 조건은 필요하지 않습니다.

`GET /v3/travel-rule/settings/`는 `didit_network`을 반환합니다. 여기에는 `listed`, `compliance_email_confirmed`, 그리고 아직 충족되지 않은 각 조건이 담긴 `missing`이 포함됩니다(`travel_rule_off`, `sandbox_application`, `not_discoverable`, `legal_name_missing`, `compliance_email_missing`, `compliance_email_unconfirmed`, `excluded_by_staff`, `internal_organization`).

### 라우팅 식별자

거래에서 `counterparty_vasp_lei` 또는 `counterparty_vasp_name`으로 거래 상대방을 지정하면, 아웃바운드 라우팅은 항상 디렉터리 항목을 참조합니다(별칭도 사용할 수 있습니다). 항목에는 도달 가능한 네트워크마다 하나의 식별자, 검증된 TRP endpoint, 컴플라이언스 연락처가 포함됩니다. `routes`는 애플리케이션이 사용할 수 있는 경로를 보여줍니다. 단일 거래에서 디렉터리를 우회하려면 [거래 상대방 식별 필드](#거래-상대방-확인-필드)에 나온 명시적 필드를 전달하면 됩니다.

## 거래 상대방 실사

고객의 자금이 다른 VASP에 도달하기 전에, 그 VASP가 누구이며 거래를 받아들일지 여부를 알아야 합니다. 거래 상대방에 대한 실사는 두 개의 계층으로 이루어집니다.

* **공유 평가.** 모든 디렉터리 항목에는 실사 점수, 위험 수준, 응답성 상태, 필수 점검 보고서가 포함됩니다. Didit은 플랫폼의 자체 데이터로 이를 계산하므로, 모든 고객이 동일한 값을 봅니다.
* **귀사의 자체 기록.** VASP에 대한 귀사의 결정, 해당 VASP에 대한 제재 및 부정적 언론 보도 스크리닝, 그리고 귀사가 해당 VASP와 주고받은 실사 설문지는 귀사의 애플리케이션에만 속합니다. 다른 고객은 이를 보지 못하며, 공유 항목을 변경하지도 않습니다.

귀사의 자체 기록은 디렉터리 항목과 [Didit 네트워크](#didit-네트워크)의 멤버 모두에 대해 동일하게 작동합니다. [디렉터리](#디렉터리)에 표시되는 `id`로 지정하십시오.

| endpoint | 기능 |
| - | - |
| `GET` / `PUT` / `DELETE /v3/travel-rule/vasps/{vasp_id}/decision/` | 거래 상대방에 대한 [귀사의 결정](#거래-상대방에-대한-결정)을 조회, 설정 또는 삭제합니다. |
| `GET` / `POST /v3/travel-rule/vasps/{vasp_id}/screening/` | 귀사의 최신 [거래 상대방 스크리닝](#거래-상대방-스크리닝) 결과를 조회하거나 새로 실행합니다. |
| `GET` / `POST /v3/travel-rule/vasps/{vasp_id}/questionnaires/` | 거래 상대방과 주고받은 [실사 설문지](#실사-설문지)를 목록으로 보거나 새 설문지를 보냅니다. |
| `GET /v3/travel-rule/transfers/?counterparty={vasp_id}` | 해당 거래 상대방과의 [거래 내역](#특정-거래-상대방과의-이체)을 조회합니다. |

### 점수와 위험 수준

`dd_score`는 0에서 100까지이며 **높을수록 안전함**입니다. 모든 결과는 `dd_score_semantic`으로 이를 나타내며, 이 값은 항상 `higher_is_safer`입니다. `risk_level`은 점수가 속한 구간입니다.

| `risk_level` | `dd_score` |
| - | - |
| `LOW` | 70 이상 |
| `MEDIUM` | 40에서 69 |
| `HIGH` | 40 미만 |

점수는 일곱 개 구성 요소의 합계입니다. `dd_score_breakdown`은 각 구성 요소를 `{points, max, reason, detail}` 형식으로 보여 줍니다. `max`는 해당 구성 요소의 가중치이고, `reason`은 번역할 수 있는 기계 판독용 코드이며, `detail`은 한 줄짜리 영어 설명입니다.

| 구성 요소 | 가중치 | 점수 산정 방식 | `reason` 코드 |
| - | - | - | - |
| `jurisdiction` | 25 | 항목의 국가가 기본 고위험 관할 목록(`IRN`, `PRK`, `SYR`, `AFG`, `RUS`, `MMR`)에 없으면 전체 점수, 목록에 있으면 0점, 국가를 알 수 없으면 절반을 받습니다. | `jurisdiction_not_high_risk`, `jurisdiction_high_risk`, `jurisdiction_unknown` |
| `verification` | 15 | 항목의 Didit 인증 상태(`dd_status`)에 따릅니다. `COMPLETED`는 전체, `IN_PROGRESS`는 절반, `NOT_STARTED`는 4분의 1, `REJECTED`는 0점입니다. | `dd_status_completed`, `dd_status_in_progress`, `dd_status_not_started`, `dd_status_rejected` |
| `protocol` | 15 | 네이티브 프로토콜 경로(TRP endpoint 또는 `INTERNAL` 경로)가 항목에 닿으면 전체 점수, 이메일 대체 경로만 닿으면 40퍼센트, 닿는 경로가 없으면 0점입니다. | `native_rail`, `email_only`, `no_rail` |
| `lei` | 10 | 유효한 LEI가 기록되어 있으면 전체 점수, 없으면 0점입니다. | `lei_valid`, `lei_missing` |
| `history` | 10 | 항목의 결정된 교환 중 성공한 비율입니다. 결정된 교환이 하나도 없으면 절반을 받습니다. | `history`, `no_history` |
| `essentials` | 15 | 결정된 [필수 점검](#필수-점검) 중 통과한 비율입니다. 아직 결정된 점검이 없으면 절반을 받습니다. | `essential_checks`, `no_essential_checks` |
| `responsiveness` | 10 | `RESPONSIVE`는 전체 점수, `UNRESPONSIVE`는 0점, `UNKNOWN`은 절반입니다. [응답성](#응답성)을 참고하십시오. | `responsive`, `unresponsive`, `responsiveness_unknown` |

점수는 소수점 첫째 자리까지 반올림하고, 합계는 정수로 반올림합니다. `history`는 [응답성](#응답성)과 같은 요청을 기준으로 하며 시간 범위를 두지 않습니다. `COMPLETED` 또는 `FINISHED` 교환은 성공으로, `EXPIRED`, `COUNTERPARTY_MISMATCHED_DATA`, `COUNTERPARTY_VASP_GENERAL_DECLINE` 교환은 실패로 계산합니다. 취소는 발신자 본인의 행위이므로 어느 쪽으로도 계산되지 않습니다.

Didit은 항목과의 교환이 완료, 종료, 만료, 불일치 또는 거절될 때, 항목을 새로 고치거나 큐레이션할 때, 그리고 주간 디렉터리 새로 고침 시 모든 항목에 대해 평가를 다시 계산합니다. `dd_assessed_at`은 마지막으로 실행된 시점을 나타냅니다. 아직 평가되지 않은 항목에는 `dd_assessed_at`이 없고, `dd_score`는 `0`, `risk_level`은 비어 있습니다. 따라서 원시 점수가 아니라 `risk_level`을 기준으로 판단하십시오. Didit 네트워크 멤버는 점수가 매겨지지 않습니다. `dd_score`는 `null`, `risk_level`은 비어 있고, `dd_score_breakdown`도 비어 있습니다.

### 필수 점검

`essential_checks`는 Didit이 모든 디렉터리 항목에 대해 수행하는 네 가지 점검에 대한 간략한 보고서입니다. 이 점검은 플랫폼이 확인한 출처에서만 수행하며, 고객이 보고한 데이터는 사용하지 않습니다.

| 점검 `id` | 통과 조건 |
| - | - |
| `registration` | 공식 등록부에 VASP가 등재되어 있습니다. 유럽연합의 임시 MiCA 암호자산 서비스 제공자 등록부, 공개 TRISA 디렉터리, 또는 LEI가 발급된 글로벌 LEI 등록부가 해당합니다. |
| `licence` | MiCA 등록부에 VASP의 인가 기록이 있고, 그 인가가 종료되지 않았습니다. |
| `domain` | 항목의 도메인이 TRP endpoint 또는 등록부에 기재된 웹사이트와 일치합니다. |
| `lei` | 항목의 LEI 기록이 활성 상태이고 발급되었으며, 법적 명칭과 국가가 항목과 일치합니다. |

`checks`의 각 점검은 `status`(`pass`, `fail`, 또는 해당 사항을 판단할 수 있는 기록상의 출처가 없으면 `unknown`), 기계 판독용 `reason`, 한 줄짜리 영어 `detail`을 보고합니다. `passed`, `failed`, `unknown`, `total`은 점검 개수를 셉니다. `level`은 `essential`이며, `checked_at`은 보고서가 작성된 시점을 나타냅니다. `essentials` 구성 요소는 결정된 점검 중 통과한 비율을 점수로 반영하므로, `unknown` 점검은 항목에 유리하게도 불리하게도 계산되지 않습니다. 주간 디렉터리 새로 고침이 항목을 처음 점검하기 전까지 보고서는 비어 있으며, Didit 네트워크 멤버에 대해서는 항상 비어 있습니다.

### 응답성

`responsiveness`는 VASP가 Travel Rule 요청에 응답하는지 여부를 나타냅니다.

```json theme={null}
{ "status": "UNRESPONSIVE", "answered": 0, "total": 6, "computed_at": "2026-10-12T09:30:00Z" }
```

`total`은 최근 90일 동안 Didit 고객이 해당 VASP를 대상으로 시작했고 결과가 확정된 요청의 수이며, `answered`는 그중 VASP가 응답한 요청의 수입니다.

* **집계 대상:** 네트워크 또는 프로토콜 경로로 보낸 요청과, 공유 주소로 이메일을 보낸 요청입니다. 공유 주소는 항목의 컴플라이언스 주소 또는 플랫폼 연락처입니다.
* **집계 제외:** 고객이 직접 입력한 주소나 고객의 개인 연락처로 보낸 이메일 요청입니다. 해당 교환은 고객 본인의 거래 내역에만 남으며, 다른 모든 고객에게 해당 VASP의 평가를 표시하지 않습니다.
* **응답함:** `COMPLETED`, `FINISHED`, `COUNTERPARTY_VASP_GENERAL_DECLINE`, `COUNTERPARTY_MISMATCHED_DATA`. 거절이나 불일치도 응답으로 봅니다. VASP가 관여했다는 뜻이기 때문입니다.
* **응답하지 않음:** `EXPIRED`.
* **둘 다 아님:** 그 밖의 모든 상태입니다. `CANCELLED` 요청은 발신자 본인의 행위이고, `COUNTERPARTY_VASP_NOT_REACHABLE`은 발신자 측의 문제일 수 있으며, 아직 진행 중인 요청은 결정되지 않은 상태입니다.

| `status` | 조건 |
| - | - |
| `UNKNOWN` | 기간 내 결정된 요청이 5건 미만이므로, 만료된 요청 하나로 VASP에 레이블이 붙지 않습니다. |
| `UNRESPONSIVE` | VASP가 결정된 요청의 10퍼센트 미만에 응답했습니다. |
| `RESPONSIVE` | 그 밖의 경우입니다. |

Didit 네트워크 멤버는 Didit 내부에서 응답하며, 항상 건수가 0인 `UNKNOWN`으로 표시됩니다. 응답하지 않는 거래 상대방에 대해서는 [무응답 정책](#초기-시행-및-응답-없음-정책) 또는 `travel-rule-counterparty-unresponsive` [프리셋](#규칙-필드와-프리셋)으로 조치할 수 있습니다.

### 거래 상대방에 대한 결정

`PUT /v3/travel-rule/vasps/{vasp_id}/decision/`를 사용하여 거래 상대방에 대한 컴플라이언스 팀의 판단을 기록합니다:

```bash theme={null}
curl -X PUT https://verification.didit.me/v3/travel-rule/vasps/0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91/decision/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"decision": "BLOCKED", "reason": "Unlicensed in its home jurisdiction"}'
```

```json theme={null}
{
  "decision": {
    "decision": "BLOCKED",
    "reason": "Unlicensed in its home jurisdiction",
    "decided_by": "",
    "updated_at": "2026-10-14T10:05:00Z",
    "inherited_from": null
  }
}
```

| `decision` | 의미 | 이를 적용하는 프리셋 |
| - | - | - |
| `APPROVED` | 팀이 거래 상대방을 승인했습니다. | 없음 |
| `RESTRICTED` | 검토 후 해당 거래 상대방으로의 거래가 진행될 수 있습니다. | `travel-rule-counterparty-restricted`가 해당 거래를 검토를 위해 보류합니다. |
| `BLOCKED` | 팀이 거래 상대방을 거절했습니다. | `travel-rule-counterparty-blocked`가 해당 거래를 거절합니다. |

* 결정은 애플리케이션 내에서만 공개됩니다. 다른 고객에게는 보이지 않으며, 공유 항목은 변경되지 않습니다.
* 결정만으로는 아무것도 바뀌지 않으며, 거래의 라우팅 방식도 변경되지 않습니다. 결정은 `counterparty_dd_status` [규칙 필드](#규칙-필드와-프리셋)를 통해 거래에 반영됩니다. 프리셋을 설치하거나 이 필드를 사용하는 규칙을 직접 작성할 수 있습니다.
* 브랜드에 대한 결정은 자체 결정이 없는 해당 브랜드의 각 [법인](#브랜드와-법인)에 적용되며, 법인은 `inherited_from`에 해당 브랜드를 표시합니다. 법인에 대한 결정은 그 법인에 대해 브랜드의 결정을 재정의합니다.
* `GET`은 적용되는 결정을 반환하거나 `{"decision": null}`을 반환합니다. `DELETE`는 귀하의 결정을 삭제하고, 이후 적용되는 결정을 반환합니다. 법인의 경우 브랜드의 결정이며, 그렇지 않으면 `null`입니다. 애플리케이션에 해당 거래 상대방에 대한 자체 결정이 없으면 `404`를 반환합니다.
* `reason`은 선택 사항이며 최대 255자입니다. `PUT`을 할 때마다 이 값이 대체됩니다. `decided_by`는 결정을 내렸거나 마지막으로 변경한 콘솔 사용자이며, API 키로 결정한 경우 비어 있습니다.

### 거래 상대방 스크리닝

거래 상대방 스크리닝은 거래 상대방 VASP 자체를 법인으로서 제재 및 부정적 언론 보도에 대해 점검합니다. **선택 사항이며 과금됩니다.** [설정](#설정)에서 `counterparty_screening_enabled`를 켜면 활성화됩니다. 꺼져 있으면 아무것도 스크리닝되거나 과금되지 않습니다.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/vasps/0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91/screening/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"refresh": false}'
```

```json theme={null}
{
  "outcome": "screened",
  "screening": {
    "id": "7a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "status": "In Review",
    "total_hits": 2,
    "score": 85.0,
    "screened_at": "2026-10-14T09:58:00Z",
    "fresh": true,
    "flagged": true
  }
}
```

* **과금.** 새 스크리닝마다 조직의 기업 AML 스크리닝 [가격](https://docs.didit.me/ko/getting-started/pricing.md)이 청구됩니다. `GET /v3/travel-rule/vasps/{vasp_id}/screening/`는 추가 비용 없이 해당 `price`, `fresh_days`, 스크리닝의 `enabled` 여부를 반환합니다. 30일보다 짧은 기간 전에 수행된 스크리닝은 최신 상태로 간주됩니다. `refresh: true`를 보내지 않는 한, `POST`는 이러한 스크리닝을 `200`과 `outcome: reused`로 다시 반환하며 과금하지 않습니다. 새 스크리닝은 `201`과 `outcome: screened`로 응답합니다. 샌드박스 애플리케이션은 스크리닝되거나 과금되지 않습니다.
* **자동 스크리닝.** 스크리닝이 켜져 있으면, 아웃바운드 거래가 시작될 때 Didit은 해당 거래가 도달하는 거래 상대방도 규칙이 거래를 평가하기 전에 스크리닝합니다. 재사용 및 과금 방식은 동일합니다. 이 단계에서 거부되거나 실패하더라도 송금은 실패하지 않습니다.
* **결과.** 주의가 필요한 가능한 일치 항목이 없으면 `status`는 `Approved`입니다. 가능한 제재 또는 부정적 언론 보도 일치는 `In Review` 또는 `Declined`입니다. 30일보다 짧은 기간 전에 수행된 최신 스크리닝이 `In Review` 또는 `Declined`인 동안 `flagged`는 `true`입니다.
* **효과.** 거래 상대방에 대한 최신 스크리닝에 플래그가 지정되어 있고 스크리닝이 계속 켜져 있으면, 규칙에서 `counterparty_risk_level`이 `HIGH`가 됩니다. 따라서 설치된 `travel-rule-counterparty-high-risk` 프리셋이 해당 거래를 보류합니다. 디렉터리의 공유 `risk_level`은 변경되지 않습니다.
* **개인정보 보호.** 스크리닝은 애플리케이션 내에서만 공개됩니다. 다른 고객에게는 보이지 않으며, 공유 항목은 변경되지 않습니다. 디렉터리는 최신 스크리닝을 `entity_screening`으로 표시합니다.
* **거부.** 스크리닝이 꺼져 있거나, 샌드박스 애플리케이션이거나, 같은 거래 상대방의 스크리닝이 이미 실행 중이면 `409`를 반환합니다. 잔액이 가격을 감당할 수 없으면 `code: insufficient_credits`와 함께 `403`을 반환합니다. 스크리닝을 완료할 수 없으면 `502`를 반환합니다. 어느 경우에도 과금되지 않습니다.

### 실사 설문지

Didit의 표준 설문지로 거래 상대방에게 실사 답변을 요청할 수 있습니다. `POST /v3/travel-rule/vasps/{vasp_id}/questionnaires/`는 거래 상대방의 등록된 컴플라이언스 주소로 설문지를 이메일로 보내며, 답변 페이지로 연결되는 일회용 링크를 함께 보냅니다:

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/vasps/0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91/questionnaires/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sent_to": "travelrule@counterparty.example"}'
```

```json theme={null}
{
  "id": "3f9e8d7c-6b5a-4c3d-9e2f-1a0b9c8d7e6f",
  "status": "SENT",
  "form_version": "1",
  "sent_to": "travelrule@counterparty.example",
  "sent_at": "2026-10-14T10:00:00Z",
  "expires_at": "2026-11-13T10:00:00Z",
  "answered_at": null,
  "valid_until": null,
  "current": false,
  "answers": {}
}
```

* **발송 위치.** `sent_to`는 등록된 컴플라이언스 주소여야 합니다. 디렉터리 항목의 경우 해당 항목의 컴플라이언스 주소이거나 `contacts`에서 애플리케이션이 확인할 수 있는 연락처여야 하며, Didit 네트워크 멤버의 경우 확인된 컴플라이언스 이메일이어야 합니다. 생략하면 거래 상대방에게 보내는 Travel Rule 이메일이 가는 주소를 사용합니다. 우선순위는 귀하의 연락처, 플랫폼 연락처, 그다음 항목의 컴플라이언스 주소 순입니다. Didit은 설문지를 다른 곳으로 보내지 않으며, 거래 상대방에 등록된 컴플라이언스 주소가 없으면 `400`을 반환합니다.
* **답변.** 링크는 `/v3/travel-rule/questionnaires/{token}/` 페이지를 엽니다. 이 페이지는 거래 상대방의 담당자가 사용하며, 계정이나 API 키가 필요하지 않습니다. 담당자는 모든 질문에 답하고, 각 답변은 최대 2,000자입니다(해당하지 않는 경우 `N/A`). 제출은 한 번만 할 수 있습니다. 거래 상대방은 같은 링크에 `{"answers": {"<question id>": "<answer>"}}`를 POST할 수도 있습니다. 사용되었거나, 교체되었거나, 기한이 지난 링크는 대신 만료 페이지를 표시합니다. 이메일과 페이지는 번역되지만, 질문 자체는 항상 영어로 제공되므로 언어에 따라 의미가 바뀌지 않습니다.
* **기한.** 거래 상대방은 답변을 위해 30일이 있습니다(`expires_at`). 답변은 365일 동안 유효하며(`valid_until`), 그 기간 동안 `current`는 `true`입니다. 기한이 지난 미답변 설문지와 유효 기간이 지난 답변은 `EXPIRED`가 됩니다.
* **거래 상대방당 열린 설문지는 하나.** 새로 보내면 같은 거래 상대방에게 보낸 이전의 미답변 설문지가 대체됩니다. 즉 이전 설문지는 `EXPIRED`가 되고 해당 링크는 작동을 멈춥니다. 한 주소로는 1분에 이메일 한 통만 보낼 수 있으며, 그보다 빠른 발송은 `429`를 반환합니다.
* **답변 조회.** `GET /v3/travel-rule/vasps/{vasp_id}/questionnaires/`는 거래 상대방에 대한 설문지를 최신순으로 나열합니다. 답변이 완료되면 질문 ID를 키로 하는 `answers`가 포함됩니다. 디렉터리는 이를 `questionnaires`로 표시합니다. 이 정보는 애플리케이션 내에서만 공개됩니다.

폼 버전 `1`에는 다음 12개 질문이 있습니다:

| 질문 ID | 질문 |
| - | - |
| `legal_name` | 법인명과 설립 관할 지역입니다. |
| `licensing` | 가상자산 서비스에 대해 보유한 라이선스 또는 등록 정보입니다. 각 관할 지역의 규제 기관, 라이선스 번호, 상태를 포함합니다. |
| `jurisdictions` | 운영하는 관할 지역과, 제외되었거나 제한된 관할 지역이 있다면 그 지역입니다. |
| `aml_program` | AML/CFT 프로그램 요약입니다. 정책, 절차, 마지막 독립 감사 또는 검토 날짜를 포함합니다. |
| `compliance_officer` | 컴플라이언스 담당자의 이름과 역할, 그리고 연락 방법입니다. |
| `sanctions_screening` | 고객과 거래 상대방을 제재 및 부정적 언론 보도와 대조하는 방법입니다. 제공업체 또는 목록, 빈도, 에스컬레이션을 포함합니다. |
| `transaction_monitoring` | 의심스러운 활동을 찾기 위해 거래를 모니터링하는 방법입니다. 도구, 임계값, 신고 관행을 포함합니다. |
| `travel_rule` | Travel Rule을 준수하는 방법입니다. 사용하는 프로토콜과 네트워크, 요청에 대한 일반적인 응답 시간을 포함합니다. |
| `custody` | 고객 자산의 보관 구조입니다. 적격 수탁자, 보험, 자산 분리를 포함합니다. |
| `ownership` | 25퍼센트 이상을 보유한 실소유자와, 상위 또는 포괄 법인이 있다면 그 법인입니다. |
| `record_keeping` | Travel Rule 및 거래 데이터의 기록 보관 관행과 보존 기간입니다. |
| `data_protection` | 컴플라이언스를 위해 공유되는 개인 데이터를 보호하는 방법과 저장 위치입니다. |

### 특정 거래 상대방과의 이체

[`GET /v3/travel-rule/transfers/?counterparty={vasp_id}`](https://docs.didit.me/ko/management-api/travel-rule/list-transfers)는 애플리케이션의 Travel Rule 이체 중 특정 거래 상대방과 관련된 내역을 최신순으로 나열합니다. 디렉터리에 표시된 `id`로 지정하며, 해당 디렉터리 항목이 거래 상대방인 이체와, 해당 Didit 네트워크 멤버와의 `INTERNAL` 거래가 포함됩니다. 유효하지 않은 id 값은 `400`을 반환합니다.
각 행은 `status` 바로 다음에 `reason`을 포함합니다. 이는 이체 자체의 [`reason`](#전송이-멈춘-이유)으로, 각 이체를 열지 않고도 반송된 컴플라이언스 이메일과 경로가 없는 거래 상대방을 구별할 수 있게 해 줍니다.

### 초기 시행 및 응답 없음 정책

이체가 만료되면(`AWAITING_COUNTERPARTY` 상태에서 `confirmation_timeout_hours`가 경과한 경우), `timeout_outcome`이 해당 거래의 처리 방식을 결정합니다. 두 가지 설정을 사용하면 흔한 타임아웃 사유 두 가지를 다르게 처리할 수 있습니다:

| 설정 | 거래 상대방이 다음 조건을 충족하는 디렉터리 항목일 때 적용됩니다 |
| - | - |
| `sunrise_policy` | `sunrise_jurisdictions`에 나열된 관할 지역에 속합니다. 즉, 아직 Travel Rule을 시행하지 않아 VASP가 응답할 의무가 없는 국가입니다. |
| `non_responsive_policy` | `responsiveness.status`가 `UNRESPONSIVE`입니다. |

두 설정 모두 `HOLD`, `REJECT`, `PROCEED` 중 하나를 값으로 가지며, 의미는 `timeout_outcome`과 같습니다. 또는 빈 문자열을 설정하여 `timeout_outcome`을 따르도록 할 수 있습니다. 두 설정 모두 기본값은 비어 있으므로, 직접 설정하기 전까지는 아무것도 바뀌지 않습니다:

```bash theme={null}
curl -X PUT https://verification.didit.me/v3/travel-rule/settings/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sunrise_policy": "PROCEED", "non_responsive_policy": "HOLD"}'
```

적용되는 설정이 여러 개일 경우 처음으로 해당되는 설정이 결과를 결정합니다:

1. `sunrise_policy`: 설정되어 있고 거래 상대방의 관할 지역이 `sunrise_jurisdictions`에 포함된 경우.
2. `non_responsive_policy`: 설정되어 있고 거래 상대방이 `UNRESPONSIVE`인 경우.
3. 그 밖의 모든 경우에는 `timeout_outcome`이 적용됩니다. Didit 네트워크 멤버로의 이체나 디렉터리 항목이 없는 거래 상대방으로의 이체도 여기에 포함됩니다.

`sunrise_jurisdictions`는 읽기 전용이며, 현재 값은 `["CO", "KH", "VN"]`입니다. 이 국가만 초기 시행 기간에 해당하는 것으로 간주됩니다. 다른 관할 지역이나 비어 있거나 알 수 없는 관할 지역은 모두 `timeout_outcome`을 따르므로, 거래 상대방이 실수로 초기 시행 정책을 적용받는 일은 없습니다. 만료된 이체의 타임라인에는 적용된 결과가 표시됩니다. 예를 들어 `sunrise_policy=PROCEED (counterparty jurisdiction CO does not enforce the Travel Rule yet)`와 같이 표시됩니다. 규칙은 만료 후에도 계속 실행되므로, EU TFR `tr_eu_expired_review`와 같은 규칙은 `PROCEED` 결과에서도 거래를 검토 대상으로 옮길 수 있습니다.

### 규칙 필드와 프리셋

[규칙](https://docs.didit.me/ko/transaction-monitoring/rules.md) 필드 네 개는 거래의 Travel Rule 교환이 도달한 거래 상대방에 대해 애플리케이션이 알고 있는 정보를 담습니다. 해당 정보는 디렉터리 항목이거나, `INTERNAL` 교환의 경우 교환을 받은 Didit 고객입니다. 알려진 거래 상대방에 도달하지 않은 교환에서는 각 필드가 비어 있습니다.

| 필드 | 값 |
| - | - |
| `counterparty_dd_status` | 귀하의 [결정](#거래-상대방에-대한-결정)입니다: `APPROVED`, `RESTRICTED` 또는 `BLOCKED`이며, 엔터티는 해당 브랜드의 결정을 따릅니다. 결정을 내리지 않았다면 비어 있습니다. 다른 고객의 결정이 표시되는 경우는 없습니다. |
| `counterparty_risk_level` | 항목의 `risk_level`(`LOW`, `MEDIUM` 또는 `HIGH`)입니다. 거래 상대방에 대한 귀하의 [스크리닝](#거래-상대방-스크리닝)에 플래그가 지정되어 있으면 `HIGH`가 표시됩니다. 아직 평가되지 않은 항목은 비어 있으며, Didit 네트워크 멤버의 경우 귀하의 스크리닝에 플래그가 지정되지 않았다면 비어 있습니다. |
| `counterparty_jurisdiction` | 디렉터리 항목 또는 Didit 네트워크 멤버 프로필에 있는 거래 상대방의 국가입니다. 다른 국가 필드와 마찬가지로 ISO-2 코드와 ISO-3 코드 모두 비교됩니다. |
| `counterparty_responsiveness` | 항목의 `responsiveness.status`입니다: `RESPONSIVE`, `UNRESPONSIVE` 또는 `UNKNOWN`입니다. Didit 네트워크 멤버의 경우 비어 있습니다. |

`travel_rule` 번들의 프리셋 5개가 이 필드에 따라 작동합니다. **Transactions → Rules → Library**에서 설치하거나 [규칙 라이브러리 API](https://docs.didit.me/ko/management-api/transactions/rules-install.md)로 설치할 수 있습니다:

| `library_key` | 트리거 | 기본 작업 |
| - | - | - |
| `travel-rule-counterparty-blocked` | `counterparty_dd_status`가 `BLOCKED`입니다 | +90 점수, `DECLINED` |
| `travel-rule-counterparty-restricted` | `counterparty_dd_status`가 `RESTRICTED`입니다 | +50 점수, `IN_REVIEW` |
| `travel-rule-counterparty-high-risk` | `counterparty_risk_level`이 `HIGH`입니다 | +40 점수, `IN_REVIEW` |
| `travel-rule-counterparty-high-risk-jurisdiction` | `counterparty_jurisdiction`이 기본 고위험 목록(`IRN`, `PRK`, `SYR`, `AFG`, `RUS`, `MMR`)에 포함됩니다 | +60 점수, `IN_REVIEW` |
| `travel-rule-counterparty-unresponsive` | `counterparty_responsiveness`가 `UNRESPONSIVE`입니다 | +30 점수, `IN_REVIEW` |

다섯 프리셋은 모두 Travel Rule 거래에만 적용됩니다.

## webhook

`travel_rule.status.updated` 이벤트를 구독하도록 webhook 대상을 설정하면, 애플리케이션의 모든 전송에 대한 모든 상태 전환을 아웃바운드와 인바운드 모두 받아볼 수 있으며, `INTERNAL` 레일 구간도 포함됩니다.
라우팅은 거래 생성 응답이 반환된 후에 실행되므로, `COUNTERPARTY_VASP_NOT_FOUND`, `COUNTERPARTY_VASP_NOT_REACHABLE`, 또는 즉시 정산되는 `INTERNAL` 교환과 같은 결과는 생성 응답이 아니라 이 이벤트를 통해 전달됩니다.

```json theme={null}
{
  "event_id": "9c0c8b8a-1111-4222-9333-666666666666",
  "webhook_type": "travel_rule.status.updated",
  "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"
}
```

`rail`은 레일이 선택되기 전까지 비어 있습니다.
하나의 거래에 대해 Didit는 `transaction.created` 다음에 `travel_rule.status.updated`를 전송합니다. Didit가 거래를 생성하는 경우(`POST /v3/transactions/` 호출, sunrise 등록, 인바운드 `TRP` 문의, 또는 `INTERNAL` 레일 인바운드 구간)에는 `transaction.created`가 먼저 전송됩니다.
각 이벤트는 개별적으로 전송되고 개별적으로 재시도되므로, 재시도가 필요했던 `transaction.created`가 상태 이벤트 뒤에 도착할 수 있습니다. 따라서 핸들러는 `transaction_id`를 기준으로 처리하고, 상태 이벤트가 아직 확인하지 않은 거래를 가리키면 그 거래를 조회하십시오.
`TRUST`로 전송된 거래에는 `COMPLETED` 이벤트가 없습니다. TRUST는 Didit가 읽을 수 있는 응답을 보내지 않으므로, 전송이 실패하거나 취소되지 않는 한 마지막 이벤트는 `EXPIRED`입니다. `GTR`, `VERIFYVASP`, `SYGNA`의 경우에는 Didit가 네트워크에서 읽은 응답에 따라 이벤트가 전송됩니다.
샌드박스 애플리케이션에도 이 이벤트가 전송되며, `"environment": "sandbox"`가 포함됩니다. [Didit Test VASP](https://docs.didit.me/ko/transaction-monitoring/travel-rule-sandbox.md)가 응답한 전송에는 `rail: "TEST_VASP"`, `test_vasp: true`, `test_vasp_scenario`가 포함됩니다.

전체 이벤트 목록, 서명 검증, 재시도 정책은 [webhook 가이드](https://docs.didit.me/ko/integration/webhooks.md#이벤트-유형)를 참고하십시오.

## 요금

Travel Rule 요금은 **시작되는 아웃바운드 교환 건당 \$0.02**이며, 거래가 생성될 때 한 번 청구됩니다. 이후 교환이 상태 전환을 몇 번 거치든 추가로 청구되지 않습니다. `resend`는 이 요금을 다시 청구하지 않습니다.

* Travel Rule이 켜진 애플리케이션의 아웃바운드 `travelRule` 거래 중 금액이 `threshold_amount` 이상이어서 Didit가 관리형 교환을 시작하는 거래에 적용됩니다. 자체 보관 지갑 또는 사용자 본인의 인증된 지갑으로의 전송도 포함되며, 이 경우 소유권 증명이 곧 교환입니다.
* Travel Rule 데이터가 포함된 그 밖의 아웃바운드 거래는 표준 Transaction Screening으로 청구됩니다. 요금제에 포함된 [월간 거래 허용량](https://docs.didit.me/ko/getting-started/pricing.md#거래-모니터링-kyt)까지는 무료이며, 초과분은 \$0.001로 청구됩니다. 임계값 미만의 전송, Travel Rule이 적용되지 않는 Lightning 결제, Travel Rule이 꺼진 상태에서 수동으로 기록된 전송이 여기에 해당합니다. 거래는 두 항목 중 한쪽에만 청구되며, 둘 다에 청구되는 일은 없습니다.
* 인바운드 `travelRule` 거래에는 Travel Rule 요금이나 모니터링 요금이 없습니다. 여기에는 `INTERNAL` 레일을 통해 수취인의 애플리케이션에 Didit가 생성하는 인바운드 구간, 인바운드 `TRP` 문의, [입금 후 등록](#입금-후-소급-등록)이 포함됩니다.
* 무료로 Travel Rule에 가입한 애플리케이션은 `POST /v3/travel-rule/workspace/billing/`로 아웃바운드 과금을 켜기 전까지 아웃바운드 `travelRule` 거래에 대해 두 요금 모두 청구되지 않습니다. 인바운드 수신은 계속 무료입니다.
* 샌드박스 애플리케이션에는 절대 요금이 청구되지 않습니다.
* Travel Rule 요금의 정가는 $0.02이고, Transaction Screening은 허용량을 초과한 경우 $0.001입니다. 각 거래는 청구되는 항목의 가격으로 과금됩니다.
* 같은 거래에서 암호화폐 AML/지갑 스크리닝도 실행되는 경우(예: `currency_kind: "crypto"`이고 모니터링이 켜진 인바운드 입금), 이 요금은 Travel Rule 요금에 포함되지 않고 표준 Wallet Verification & Screening 요금으로 별도 청구됩니다.
* [거래 상대방 스크리닝](#거래-상대방-스크리닝)은 선택 사항이며, 스크리닝 건당 기업 AML 스크리닝 가격으로 별도 청구됩니다. 30일 이내의 스크리닝 결과는 요금 없이 재사용됩니다. 결정, 설문지, 디렉터리의 실사 평가는 비용이 들지 않습니다.

### 어트리뷰션 요금

[블록체인 분석 어트리뷰션](#블록체인-분석-귀속)에는 분석 제공업체에 대한 지갑 조회가 한 번 필요하며, 이 조회의 결과 전체를 항상 받으실 수 있습니다.

* **암호화폐 모니터링이 이 거래의 지갑을 이미 스크리닝한 경우** - 저장된 결과를 재사용합니다. 분석 호출이 한 번, Wallet Verification & Screening 요금이 한 번 청구되며(암호화폐 모니터링이 이미 부과한 요금), 어트리뷰션이 추가로 부과하는 비용은 없습니다.
* **암호화폐 모니터링이 실행되지 않은 경우** - Didit가 필요한 분석 호출을 한 번 실행하고 그 결과 전체를 전달합니다. 거래에는 Didit 관리형 분석 기준 표준 Wallet Verification & Screening 요금(\$0.15)이 청구되며, 위험 점수, 지갑 위험 범주, 네트워크 스냅샷 등 암호화폐 스크리닝 결과 전체를 받습니다. VASP 이름만 받는 것이 아닙니다. 사용량 항목에는 `travel_rule_attribution` 트리거가 표시되어 청구 내역을 확인할 수 있습니다.
* **자체 분석 키 사용** - 자체 키로 암호화폐 모니터링을 사용할 때 적용되는 플랫폼 요금과 동일하게, 호출 건당 \$0.02가 적용됩니다. 기반 조회 비용은 제공업체가 직접 청구합니다.
* **실패한 조회는 절대 청구되지 않습니다.** 재전송은 새로운 호출이나 요금 없이 기록된 어트리뷰션 결과를 재사용하며, 샌드박스 애플리케이션에는 절대 요금이 청구되지 않습니다.

따라서 어트리뷰션이 필요한 아웃바운드 전송의 비용은 \$0.02(Travel Rule) + Wallet Verification & Screening 요금 1회입니다. 두 제품을 모두 켜 두면 분석 호출과 그 요금은 한 번만 발생합니다.

## EU TFR 규칙 번들

EU TFR 의무를 위해 설계된 사전 설정 규칙 4개가 `travel_rule_eu_tfr` 번들에 포함되어 있습니다.
다른 모든 [사전 설정 번들](https://docs.didit.me/ko/transaction-monitoring/rules-library.md)과 마찬가지로, Business Console에서 **Transactions → Rules → Library**를 통해 활성화하십시오.

| 규칙(`library_key`) | 트리거 | 작업 |
| - | - | - |
| `tr_eu_mismatch_hold` | 전송 상태가 `COUNTERPARTY_MISMATCHED_DATA`인 경우 | `+50` 점수, 거래를 `IN_REVIEW`로 설정 |
| `tr_eu_unverified_selfhosted_review` | 전송 상태가 `UNCONFIRMED_OWNERSHIP`이고 기본 통화 금액이 `1000` 이상인 경우 | 거래를 `IN_REVIEW`로 설정 |
| `tr_eu_vasp_not_found_score` | 전송 상태가 `COUNTERPARTY_VASP_NOT_FOUND`인 경우 | `+25` 점수, `travel-rule-sunrise` 태그 부여 |
| `tr_eu_expired_review` | 전송 상태가 `EXPIRED`인 경우 | 거래를 `IN_REVIEW`로 설정 |

이 규칙은 전송 생성 시, 소유권 확인 시, 이메일 수신 응답 시, 그리고 만료 처리기에서 발생하는 관련 상태 전환마다 실행됩니다. 따라서 끝까지 해결되지 않는 전송도 조용히 정산되지 않고 컴플라이언스 검토 대상으로 표시됩니다.

## 단일 플랫폼에서 이 방식이 더 나은 이유

Travel Rule 준수는 결제의 옷을 입은 신원 확인 문제이며, Didit은 이미 그 신원 정보를 보유하고 있습니다:

* **검증된 데이터 활용.** 각 전송의 대상자는 `vendor_data`를 통해 온보딩 시 Didit이 검증한 KYC 프로필(문서 OCR, 라이브니스, 얼굴 대조)과 연결되므로, 같은 플랫폼에서 검증된 필드로 `originator_data`를 구성할 수 있습니다.
  승인된 KYC 인증을 받은 사용자의 경우, Didit은 입력하신 값이 아니라 검증된 신원 정보를 전송합니다([검증된 데이터 및 출처](#검증된-데이터와-출처) 참조).
* **위험한 전송에 대한 추가 생체 인증.** [규칙](https://docs.didit.me/ko/transaction-monitoring/rules.md)으로 모든 고액 전송을, 사용자가 온보딩 시 촬영한 얼굴과 대조하는 생체 재확인을 통과할 때까지 보류할 수 있습니다. 출금 시점의 계정 탈취에 대한 가장 강력한 대응책입니다. [실제 예시](https://docs.didit.me/ko/transaction-monitoring/sdk-transaction-submission.md#예시-고액-출금-시-추가-생체-인증)를 참조하십시오.
* **하나의 기기 그래프.** SDK로 제출된 전송에는 KYC 세션과 동일한 기기 인텔리전스가 포함되므로, "새 기기 + 고액 + 첫 수취인"은 데이터 웨어하우스 조인이 아니라 하나의 규칙으로 구성됩니다.
* **스크리닝, 규칙, 케이스, 정보 교환을 한곳에서.** 상대방 VASP를 식별하는 지갑 스크리닝은 규칙과 분석가가 보는 것과 같은 스크리닝이므로, 과금, 콘솔, 감사 추적이 하나로 통합됩니다.
* **공개된 요금.** [발신 교환 건당 \$0.02](#요금), 사용량 등급 없음, 영업 상담 없음.

## FAQ

### 거래 교환 방식

> [accordion] Didit이 VASP 간 교환을 직접 실행하나요, 아니면 다른 네트워크가 핸드셰이크를 완료한 뒤 Travel Rule 데이터만 기록하나요?
>
> Didit이 교환을 직접 실행합니다.
> `travelRule` 전송을 제출하면 Didit은 실시간으로 거래 상대방을 탐색하고, 송금인 및 수취인 데이터를 이를 전달하는 경로를 통해 수신 VASP에 보냅니다. 상대방 측이 확인한 후에만 전송을 `COMPLETED`로 표시합니다. 이는 정산 후 로그가 아니라 관리되는 핸드셰이크입니다.
> 상대방의 응답은 `TRUST`를 제외한 모든 경로에서 처리됩니다. TRUST는 전송만 하므로, 이 경로를 사용한 전송은 완료되지 않고 만료됩니다. `GTR`, `VERIFYVASP`, `SYGNA`에서는 Didit이 네트워크의 응답에서 답변을 읽거나 5분마다 조회하며, 아직 이들 경로로 실시간 네트워크를 통해 완료된 교환은 없습니다. `TRP`에서는 상대방이 즉시 응답하지 않는 경우 해당 응답을 처리하려면 Didit의 공개 TRP 호스트가 필요하며, 이 호스트는 아직 가동 중이 아닙니다([Travel Rule 네트워크](https://docs.didit.me/ko/transaction-monitoring/travel-rule-networks.md) 참조).
> Didit은 모든 교환의 전체 기록과 컴플라이언스 상태도 보관합니다(13개 상태의 [수명 주기](#수명-주기), [webhook](#webhook), 감사 추적).
> Travel Rule을 자체 프로세스로 운영하길 원하면, Didit은 귀사의 단일 기준 시스템으로 데이터를 수동적으로 기록하여 사용할 수 있습니다(아래의 수동 기록 질문 참조).
> 두 가지 모드를 모두 사용할 수 있습니다. [설정](#설정)에서 `is_enabled: true`를 설정하면 능동 교환이 실행되며, 기본값은 `false`입니다. 그 전까지 Didit은 데이터를 수동으로 기록합니다.

> [accordion] 교환은 Didit 고유 기능인가요, 아니면 외부 상호운용성 제공자(Notabene, Sygna, VerifyVASP, TRP)에 의존하나요?
>
> 고유 기능입니다. Didit은 자체 Travel Rule Protocol(TRP) 구현을 운영하며, 공개 호스트가 가동되면 `trp.didit.me`에서 제공됩니다(아직 응답하지 않으므로 현재 상대방은 TRP로 귀하에게 연결할 수 없습니다). 또한 업계 네트워크 프로토콜을 직접 구현합니다. `GTR`(Global Travel Rule), `TRUST`, `VerifyVASP`, `Sygna Bridge`(Sygna의 ECIES 암호화 IVMS 101 및 서명된 허가 요청을 포함하여 네이티브로 처리)입니다. Didit은 단일 제3자 상호운용성 계층의 재판매업체가 아닙니다. 특정 멤버십 네트워크에 있는 상대방에게 도달하려면 반드시 해당 네트워크를 거쳐야 합니다. Travel Rule에는 단일한 보편 채널이 없습니다. 그러나 탐색, 핸드셰이크, 검증, 기록 관리는 모두 Didit이 실행합니다.
> 오늘날 모든 네트워크 경로에는 귀사의 멤버십이 필요하며, `TRUST`는 전송만 합니다. [개요](#개요)를 참조하십시오.

> [accordion] Didit은 어떤 Travel Rule 네트워크와 프로토콜을 지원하나요?
>
> 네이티브로 구현되어 있으며, 엄격한 우선순위에 따라 시도됩니다. `INTERNAL`(두 VASP 모두 Didit 사용), `TRP`(OpenVASP Travel Rule Protocol), `GTR`, `TRUST`, `VerifyVASP`, `Sygna Bridge`, 그리고 도구가 없는 상대방을 위한 `EMAIL` 대체 경로입니다.
> 네트워크 경로에는 오늘날 귀사의 멤버십이 필요하고, `TRUST`는 전송만 하며, 네트워크를 통한 수신은 아직 활성화되지 않았습니다. [Travel Rule 네트워크](https://docs.didit.me/ko/transaction-monitoring/travel-rule-networks.md)를 참조하십시오.
> CODE, TRISA, Notabene, Veriscope는 Didit Marketplace에 액세스 요청 타일로 표시되며, 아직 연동되어 있지 않습니다.
> 전체 라우팅 표는 [Didit이 교환을 라우팅하는 방식](https://docs.didit.me/ko/transaction-monitoring/travel-rule-interoperability.md#디뎃이-거래를-라우팅하는-방식)을, 각 네트워크가 현재 도달할 수 있는 범위는 [Travel Rule 네트워크](https://docs.didit.me/ko/transaction-monitoring/travel-rule-networks.md)를 참조하십시오.

> [accordion] 어떤 네트워크를 사용해야 하며, 자체 멤버십이 필요한가요?
>
> `TRP`를 기본으로 권장합니다. 공유 멤버십이 필요하지 않으며, [Travel Address](#travel-address)를 공유하면 TRP를 지원하는 모든 VASP가 귀사에 도달할 수 있습니다. 상대방을 지갑 주소로만 알고 있는 경우, 귀속된 디렉터리 항목에 이미 TRP 엔드포인트가 있을 때만 [블록체인 분석 귀속](#블록체인-분석-귀속)을 통해 TRP로 라우팅됩니다.
> 아웃바운드의 경우 Didit은 [TRP 자산 식별자](#trp-자산-식별자)에 나열된 자산을 이 경로로 전송하며, 즉시 응답하지 않는 상대방에게는 아직 가동되지 않은 Didit의 공개 TRP 호스트가 필요합니다.
> 멤버십 네트워크의 경우 상대방이 활동하는 지역에 따라 선택하십시오. GTR은 폭넓은 글로벌 범위를 제공하고, TRUST는 미국 VASP에 중점을 두며, VerifyVASP(현재 Sygna Bridge도 운영)는 아시아에서 강력한 범위를 보유하고 있습니다.
> 예, 오늘날에는 자체 멤버십이 필요합니다. Didit은 아직 이들 네트워크에 플랫폼 멤버십을 보유하고 있지 않으며, 제공자로 온보딩 중입니다.
> **Marketplace**에서 멤버십을 연결하십시오. 단일 네트워크에만 묶여 있지 않습니다.
> `TRUST`는 전송만 하므로 이를 사용한 전송은 완료되지 않으며, 아직 실시간 네트워크를 통해 완료된 네트워크 교환은 없다는 점을 기억하십시오.

> [accordion] 지갑 주소만 있을 때 Didit은 상대방 VASP를 어떻게 찾나요?
>
> 해결 체인을 통해 찾습니다. 먼저 지갑의 소유권을 증명한 다른 Didit 고객(`INTERNAL` 경로), 다음으로 저장된 Travel Address에 대한 [주소록](#지갑-주소록), 그다음 프로토콜 및 네트워크 경로, 마지막으로 [블록체인 분석 귀속](#블록체인-분석-귀속) 순서입니다.
> 전송에 대상 지갑만 포함된 경우, Didit은 블록체인 분석 데이터와 대조하여 상대방 거래소 또는 커스터디 서비스를 귀속시키고 이를 [VASP 디렉터리](#디렉터리)에 등록합니다.
> 전송은 해당 디렉터리 항목에 이미 경로(예: `TRP` 엔드포인트)가 있을 때에만 자동으로 라우팅됩니다. 그렇지 않으면 상대방 이름이 기록된 채 `COUNTERPARTY_VASP_NOT_FOUND`로 종료되며, 경로가 생기면 다시 전송할 수 있습니다. 예를 들어 해당 지갑의 주소록 항목에 상대방의 `travel_address`를 저장한 후 다시 전송할 수 있습니다([TRP 자산 식별자](#trp-자산-식별자)에 나열된 자산에 해당).

### 요금 및 라이선스

> [accordion] Travel Rule의 비용은 얼마이며, KYC 또는 인증 요금제에 포함되나요?
>
> Travel Rule은 사용량 기반으로 별도 판매되는 제품입니다.
> 신원 인증에 포함되어 있지 않습니다.
> \*\*시작되는 아웃바운드 교환 건당 $0.02**가 부과됩니다([공개 요금](#요금), 사용량 등급 없음, 영업 담당자 상담 불필요).
>         라우팅을 위해 상대방 VASP를 식별하는 블록체인 분석 조회가 필요한 경우, 해당 조회는 [귀속 요율](#어트리뷰션-요금)에 따라 한 번 청구됩니다. 관리형은 $0.15부터, 기존 제공자 키가 있으면 \$0.02입니다.
> 암호화폐 모니터링이 같은 지갑을 이미 스크리닝했다면 다시 청구되지 않습니다.
> 임계값 미만 전송이나 Travel Rule이 꺼진 상태에서 기록된 전송은 대신 거래 스크리닝으로 청구됩니다. 수신 전송(예: Didit이 수취인 측에서 생성하는 레그)에는 Travel Rule 요금이 부과되지 않습니다.

> [accordion] Travel Rule과 암호화폐 모니터링이 모두 실행되면 이중 청구되나요?
>
> 아니요.
> `travelRule` 거래는 한 줄로 청구됩니다. 아웃바운드 교환을 시작하는 경우에는 Travel Rule 요금(\$0.02)이, 그 외에는 표준 거래 스크리닝 요금이 부과됩니다. 블록체인 분석 조회는 한 번만 청구됩니다. 암호화폐 모니터링이 해당 거래의 대상 지갑을 이미 스크리닝했다면 [귀속](#블록체인-분석-귀속)은 저장된 결과를 추가 비용 없이 재사용합니다.
> Didit이 수취인 측에서 생성한 수신 레그에는 Travel Rule 요금이 절대 청구되지 않습니다.

> [accordion] VASP 라이선스 신청이나 규제 기관 제출 서류를 위해 Didit이 문서나 역량 설명서를 제공할 수 있나요?
>
> 예. Didit의 KYT 및 거래 모니터링, AML 및 제재 스크리닝, 블록체인 분석 기반 지갑 위험, Travel Rule 기능은 모두 docs.didit.me에 공개 문서화되어 있으며, 이 페이지들을 규제 관련 파일에 자유롭게 인용하실 수 있습니다. AIFC/AFSA, MiCA 또는 기타 VASP 라이선스 신청과 같이 특정 제출물에 맞춘 공식 제품 역량 설명서가 필요하다면, 규제 기관이 요구하는 형식을 알려 Didit 계정 팀에 문의하십시오.

### 운영 및 데이터

> [accordion] 임계값 미만 전송은 어떻게 처리되나요?
>
> 이 전송은 즉시 `NOT_APPLICABLE`로 처리됩니다. 어떤 연동 경로도 시도되지 않고, 신원 데이터는 Didit 외부로 전송되지 않습니다.
> 이 거래는 Travel Rule 수수료가 아니라 표준 거래 스크리닝으로 과금됩니다.
> Didit는 임계값을 애플리케이션의 기본 통화로 환산한 금액과 비교합니다.
> Didit가 금액을 환산할 수 없으면 임계값은 적용되지 않고 정보가 교환됩니다.
> EU TFR에는 최소 금액이 없으므로, EU CASP는 `threshold_amount`를 `0.00`으로 유지해야 합니다.

> [accordion] 거래 상대방이 응답하지 않으면 어떻게 되나요?
>
> `confirmation_timeout_hours`가 지날 때까지 전송은 `AWAITING_COUNTERPARTY` 상태에 머무릅니다. 그 후 스위퍼가 전송을 `EXPIRED`로 옮기고, 원거래에 `timeout_outcome` 설정을 적용합니다. 값은 검토를 위한 `HOLD`, 거부를 위한 `REJECT`, 거래를 그대로 두는 `PROCEED`입니다. `tr_eu_expired_review` 사전 정의 규칙은 만료된 모든 건을 분석가에게 추가로 알립니다. 거래 상대방이 메시지를 아예 받지 못하는 경우는 다릅니다. Didit가 재시도하며, 여섯 번째 실패 시도 후에는 전송이 `COUNTERPARTY_VASP_NOT_REACHABLE` 상태로 옮겨집니다([전송 및 재시도](#전달-및-재시도) 참고).

> [accordion] 거래 상대방에게 Travel Rule 도구가 전혀 없습니다. 그래도 준수할 수 있나요?
>
> 네, 그럴 수 있습니다. 이때 사용하는 것이 `EMAIL` 경로입니다. 상대방의 컴플라이언스 담당자는 일회용 수령 링크를 받고, Didit 계정, API 키, 연동 없이 호스팅 페이지에서 정보 교환을 확인하거나 거부할 수 있습니다. 교환이 가능해지기 전에 이미 정산된 입금에는 [입금 후 sunrise 등록](#입금-후-소급-등록)을 사용하세요.

> [accordion] 사용자 본인의 지갑으로 출금하는 경우는 어떻게 작동하나요?
>
> 대상을 자체 보관 지갑으로 선언하세요(`payment_method.method_type: "unhosted_wallet"`). 그러면 Didit는 VASP 라우팅을 건너뛰고, [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)을 통해 고객이 해당 지갑을 통제한다는 증명을 수집합니다. 증명 방식은 메시지 서명, Satoshi 테스트, 스크린샷 또는 자기 선언입니다. 이는 API 형태로 구현된 EU TFR 자체 보관 지갑 의무입니다.

> [accordion] 정보 교환 중에도 거래 상대방의 개인정보는 보호되나요?
>
> 네.
> 귀사 측에서 지갑 소유권이 확인되기 전까지는, 인바운드 전송의 거래 상대방 개인정보가 전송 객체와 거래의 거래 상대방 객체 모두에서 마스킹됩니다([마스킹된 거래 상대방 데이터](#마스킹된-거래-상대방-데이터) 참고).
> 이메일 수령 페이지에는 전송 요약(송금인 VASP 이름, 금액, 대상 지갑)만 표시되며, 개인 정보 페이로드는 표시되지 않습니다.
> 연동 경로의 정보 교환은 HTTPS로 이루어지며, 네트워크 경로(`GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`)는 IVMS 페이로드를 수신 VASP의 공개 키로 추가 암호화합니다.

> [accordion] 이미 자체 절차로 Travel Rule 준수를 관리하고 있습니다. 기록만 남길 수 있나요?
>
> 네. Travel Rule을 비활성화한 상태에서 제출하는 `travel_rule_details`는 모두 거래에 별도의 처리 없이 그대로 기록됩니다. 여기에는 호출자가 지정한 `status`(현재 값 또는 레거시 값)도 포함됩니다. 연동 경로는 호출되지 않으며, 협상도 이루어지지 않습니다. 이 방식은 과거 기록을 채우거나 자체 절차를 운영하면서도 하나의 기록 시스템을 유지하려는 경우에 사용하세요.

> [accordion] 실서비스 전에 이를 어떻게 테스트하나요?
>
> Travel Rule이 켜진 샌드박스 애플리케이션을 사용하세요. 샌드박스에서는 규칙, 과금, `transaction.created` 및 `transaction.status.updated` 웹훅이 건너뛰어집니다. 모든 **아웃바운드** 교환은 `TEST_VASP` 경로를 통해 [Didit 테스트 VASP](https://docs.didit.me/ko/transaction-monitoring/travel-rule-sandbox.md)로 전달되며, 테스트 VASP는 선택한 시나리오(수락, 거부, 이름 불일치, 도달 불가, 시간 초과, 자체 보관 지갑)에 따라 Didit 내부에서 응답합니다. `environment: sandbox`로 표시된 `travel_rule.status.updated` 웹훅을 받게 되며, 실제 거래 상대방에게는 어떤 메시지도 전달되지 않습니다.
> **인바운드** 테스트의 경우 [테스트 VASP 인바운드 전송 트리거](https://docs.didit.me/ko/management-api/travel-rule/trigger-test-vasp-inbound.md)를 사용해 테스트 VASP가 전송을 보내게 하거나, `POST /v3/travel-rule/inbound/`로 전송을 등록하세요. 두 방식 모두 지갑 주소록을 기준으로 결정되며, 결과는 `ON_HOLD`, `UNCONFIRMED_OWNERSHIP`, `COUNTERPARTY_MISMATCHED_DATA`, 또는 `COMPLETED` 후 `FINISHED`입니다.
> 실제 다른 애플리케이션과 두 당사자 간 교환을 테스트하려면, `INTERNAL` 경로에서 본인 소유의 **라이브** 애플리케이션 두 개를 사용하세요. 수신 측 애플리케이션은 검색 가능해야 하며, 서명된 메시지나 테스트 입금으로 대상 지갑에 대한 통제권을 증명해야 합니다.

## 다음 단계

> [card] [지갑 소유권 위젯](https://docs.didit.me/ko/transaction-monitoring/wallet-ownership-widget.md)
>
> 서명, Satoshi 테스트, 스크린샷 또는 자기 선언으로 고객이 지갑을 통제한다는 사실을 증명합니다.

> [card] [상호 운용성](https://docs.didit.me/ko/transaction-monitoring/travel-rule-interoperability.md)
>
> 다른 VASP가 귀사에 연결할 때 사용하는 거래 상대방용 엔드포인트입니다: TRP, 콜백, 이메일 수령, sunrise.

> [card] [네트워크](https://docs.didit.me/ko/transaction-monitoring/travel-rule-networks.md)
>
> 각 경로와 네트워크가 현재 도달할 수 있는 범위와 멤버십 승인을 기다리는 항목을 확인합니다.

> [card] [샌드박스에서 테스트](https://docs.didit.me/ko/transaction-monitoring/travel-rule-sandbox.md)
>
> 실서비스 전에 Didit 테스트 VASP에서 모든 결과를 실행해 보세요.

> [card] [거래 제출](https://docs.didit.me/ko/transaction-monitoring/transactions.md)
>
> 거래 생성 페이로드에 Travel Rule 데이터를 포함하는 방법입니다.

> [card] [규칙 라이브러리](https://docs.didit.me/ko/transaction-monitoring/rules-library.md)
>
> Travel Rule 번들을 포함한 전체 사전 정의 규칙 카탈로그입니다.

> [card] [웹훅](https://docs.didit.me/ko/integration/webhooks.md)
>
> travel\_rule.status.updated와 다른 모든 이벤트 계열을 구독하세요.

> [card] [콘솔 가이드](https://docs.didit.me/ko/transaction-monitoring/console.md)
>
> 거래 상세 보기에서 Travel Rule 상태를 확인하는 방법입니다.


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