메인 콘텐츠로 건너뛰기
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로 마무리합니다.
이번 릴리스에는 일곱 개의 경로가 포함되며, 엄격한 우선순위 순서대로 시도됩니다. 거래 상대방에 가장 먼저 도달할 수 있는 경로가 선택됩니다. 네트워크 경로(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에서 확인하고, 각 네트워크가 현재 도달할 수 있는 범위는 Travel Rule 네트워크에서 확인하십시오. 네 네트워크 모두 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 네트워크를 참조하세요. 그 밖의 네트워크인 CODE(한국 얼라이언스), TRISA, Notabene, Veriscope는 콘솔의 Marketplace에 접근 요청 타일로 나열되어 있습니다. Didit은 아직 이들과 연동되어 있지 않으므로, 현재는 이들 네트워크를 통해 어떤 전송도 라우팅할 수 없습니다. 7개의 네트워크 모두 거래 상대방을 확인하지 못하는 경우, Didit은 포기하기 전에 한 단계를 더 수행합니다. 바로 블록체인 분석 귀속입니다. 수취 지갑을 블록체인 분석 데이터와 대조하며, 해당 지갑이 알려진 거래소 또는 수탁 기관에 속하면 Didit은 귀속된 VASP를 전송 정보에 기록하고, 이를 디렉터리에 추가한 뒤 새 이름으로 네트워크 조회를 한 번 다시 실행합니다. 전체 동작 방식과 요금은 블록체인 분석 귀속을 참조하세요. 목적지가 자체 보관 지갑으로 선언된 전송은 7개의 네트워크를 모두 건너뜁니다. 교환할 거래 상대방 VASP가 없기 때문입니다. 이 경우 바로 최종 사용자의 지갑 소유권 증명 단계로 넘어갑니다.
Didit은 상대방 측 교환 기능도 운영합니다. 다른 VASP가 귀하에게 연결하기 위해 호출하는 공개 TRP 식별 및 조회 endpoint, 콜백 endpoint, 이메일 수신 흐름이 여기에 포함됩니다. 이들은 관리형 인프라이며, 귀하는 이들을 직접 호출하지 않습니다. 다만 거래 상대방 VASP가 귀하와 연결할 수 있도록, Travel Rule 상호운용성 페이지에서 이들 각각을 문서화하고 있습니다. 귀하는 이 페이지에 설명된 /v3/ endpoint를 통해서만 Travel Rule과 상호작용합니다.

전송 요청 시 실행되는 작업

검사는 POST /v3/transactions/ 호출 한 번 안에서 실행됩니다. 응답을 받을 때쯤이면 스크리닝이 끝났고, 전송이 저장되었으며, 규칙 평가도 완료되어 있습니다. 교환 자체는 응답 이후에 시작됩니다. Didit이 경로를 선택하고 요청 외부에서 거래 상대방에게 연락하므로, 네트워크나 거래 상대방 때문에 생성 요청이 느려지거나 실패하는 일은 없습니다. 그 이후의 모든 상태 변경은 경로 정보를 포함한 travel_rule.status.updated webhook으로 전달됩니다. Travel Rule 전송 endpoint가 반환하는 전송 객체에는 메시지가 전달되었는지 보여주는 delivery 필드가 있습니다. 폴링해야 하는 파이프라인은 없습니다. 비동기로 처리되는 부분은 교환, 거래 상대방의 응답, 그리고 최종 사용자의 작업뿐입니다. 단계별 설명:
  1. 검증 및 주체 연결. 페이로드를 검증합니다. 애플리케이션에서 이미 사용된 transaction_id는 400으로 거부되며, 같은 id를 가진 두 요청이 경합하면 그중 하나는 409를 받습니다. subject는 vendor_data를 통해 사용자 또는 기업 프로필에 연결되므로, 전송이 모니터링을 위해 해당 프로필에 표시됩니다. 차단 목록에 있는 주체나 거래 상대방 vendor_data는 400으로 거부되며, 거래는 생성되지 않습니다. 차단 목록에 있는 지갑, 은행 계좌, IP, 기기는 6단계에서 확인되며, 라우팅이 실행된 후 그 단계에서 거래를 거부합니다.
  2. 기기 및 네트워크 보강. 당사자 IP에 위치 정보가 추가되고, 기기 컨텍스트(SDK 제출 시 자동으로)는 기기 그래프로 정규화됩니다. 이는 KYC 세션이 채우는 것과 같은 그래프이므로, “기기로 연결됨” 기능이 온보딩과 거래 전반에서 작동합니다.
  3. 암호화폐 스크리닝. 모니터링이 활성화된 암호화폐 전송의 경우, 교환이 이루어지기 전에 관련 지갑(또는 온체인 해시)을 블록체인 분석 서비스로 스크리닝합니다. 제재 노출, 믹서 근접성, 도난 자금 여부를 확인합니다. 암호화폐 및 지갑 스크리닝을 참조하십시오.
  4. Travel Rule 전송. 교환 엔진은 임계값을 확인하고 자체 보관 지갑 선언을 반영한 뒤 전송을 저장합니다. 임계값 미만이면 NOT_APPLICABLE, 선언된 자체 보관 지갑이면 UNCONFIRMED_OWNERSHIP, 거래 상대방의 지갑이 사용자의 검증된 지갑이면 COMPLETED, 그 외에는 AWAITING_COUNTERPARTY가 설정되며, 이때 경로는 아직 비어 있습니다. 각 상태 변경은 travel_rule.status.updated 이벤트를 대기열에 넣으며, Didit은 이 이벤트를 거래의 transaction.created(7단계) 다음에 전송합니다.
  5. 지갑 소유권 확인 발급. 전송에 최종 사용자의 소유 증명이 필요하고 auto_wallet_verification이 켜져 있으면, 지갑 소유권 위젯 세션이 발급되어 action_required에 반환됩니다.
  6. 규칙 평가. 활성화된 모든 규칙이 실행됩니다. EU TFR 번들은 4단계에서 설정된 전송 상태에 반응하고, 사용자 정의 규칙은 3단계의 스크리닝 결과를 확인하며, 규칙은 거래를 보류(IN_REVIEW)하거나, 거부하거나, 추가 인증(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에서 거래 조회로 읽을 수 있습니다.

각 단계가 충족하는 의무

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

수명 주기

모든 전송에는 13개 값 중 하나의 status가 있습니다. 일반적인 경우 교환은 AWAITING_COUNTERPARTY 상태에서 시작합니다. 금액이 threshold_amount보다 작거나 Lightning 결제 요청에 수취인이 지정되지 않은 경우(Lightning 참고)에는 NOT_APPLICABLE 상태에서 시작합니다. 대상 지갑이 자체 보관 지갑으로 선언된 경우에는 UNCONFIRMED_OWNERSHIP 상태에서 시작합니다. 교환은 최종 상태 중 하나인 FINISHED, CANCELLED, EXPIRED에서 끝납니다. 전송은 기본 거래의 검토 상태와 별도로 자체 상태 머신을 실행합니다. 교환 상태가 단독으로 거래 상태를 바꾸는 일은 없으며, 예외가 하나 있습니다. 만료되면 설정하신 timeout_outcome이 적용됩니다(해당하는 경우 선라이즈 또는 무응답 정책이 대신 적용됩니다). 대신 모든 전환마다 규칙이 다시 실행되므로, EU TFR 규칙이나 travel_rule_status를 키로 하는 규칙이 거래를 IN_REVIEW 같은 상태로 옮길 수 있습니다. 최종 사용자의 지갑 통제 증명이 필요한 전송은 거래에 action_required 블록을 추가하지만, 그 status는 규칙이 설정한 상태 그대로 유지됩니다. 교환 상태는 travel_rule.status에서 읽고, 허용 / 보류 / 거부 판정은 거래 자체의 status에서 읽습니다. 모든 전환(그리고 수동으로 보고된 모든 상태)은 규칙이 키로 사용할 수 있도록 거래의 사용자 지정 값에도 반영됩니다. 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은 항상 파생 값보다 우선합니다.
COMPLETED 전에는 절대 브로드캐스트하지 마십시오. 전송이 AWAITING_COUNTERPARTY, UNCONFIRMED_OWNERSHIP 또는 불일치/거부 상태에 있는 동안 온체인에서 자금을 내보내면 이 정보 교환의 목적이 무너집니다. 브로드캐스트 단계는 status == "COMPLETED"일 때만 진행하도록 제어한 뒤, 결과로 나온 payment_txn_id와 함께 PATCH .../travel-rule/를 호출해 FINISHED로 전환하십시오.
상태 전환 한눈에 보기(CANCELLED는 종료되지 않은 모든 상태에서 action: "cancel"로도 도달할 수 있습니다):

전달 및 재시도

Didit은 전송이 보내야 하는 모든 메시지를 해당 메시지를 유발한 요청과 별도로 전송하며, 상대방이 수신할 때까지 재시도합니다. 완료, 취소 또는 재전송, 소유권 확인, sunrise 등록의 응답으로 반환되는 전송 객체는 최신 메시지를 delivery에 담고, 거래에 포함된 travel_rule 객체도 마찬가지입니다. 전송이 메시지를 보낼 의무가 없었다면 null입니다. 네트워크가 복구할 수 있는 실패(시간 내 응답이 없음, 연결 실패, 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)도 제출 시와 규칙의 조건 값에서 허용되며, 정식 형식으로 정규화됩니다.

설정

애플리케이션마다 한 번 GET/PUT /v3/travel-rule/settings/를 사용해 VASP 프로필과 협상 정책을 구성합니다.
PUT은 부분 업데이트입니다. 변경하려는 필드만 보내면 됩니다.

이름 일치 엄격도

소유권 검증 모드

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

데이터 공유

data_sharing_fields는 개인과 기업에 대해 따로, Didit이 거래 상대방에게 귀하의 사용자에 관해 보낼 필드를 선택합니다:
  • 개인은 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는 거래 상대방 데이터의 각 필드를 귀하가 보유한 정보와 얼마나 엄격하게 비교할지 설정합니다:
각 값은 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 입장)이므로, 직접 올리지 않으면 모든 전송에 대해 정보를 교환합니다: 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에는 각 필드의 출처와 여러분이 보낸 값이 검증된 값과 달랐던 위치가 기록됩니다:
*_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는 같은 측의 블록보다 우선합니다.
잘못된 블록은 경로별 키와 함께 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."와 같습니다: 송금인의 경우, 검사는 애플리케이션이 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 네트워크를 참조하십시오. 거래 상대방의 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자입니다:
Didit이 Travel Address를 검증하는 경우, 올바른 형식이 아니거나 체크섬이 맞지 않는 값은 400과 "Invalid travel address."를 반환합니다. 실제 주소는 본인의 설정 응답이나 거래 상대방에게서 받아 사용하십시오.

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

멤버십 없이 가능한 경로를 테스트하는 데 GTR, TRUST, VerifyVASP 또는 Sygna 멤버십이 필요하지 않습니다. 다만 테스트 대상 애플리케이션의 Didit API 키는 여전히 필요합니다.
  • 샌드박스 발신: 샌드박스 애플리케이션에서 travelRule 거래를 제출합니다. 임계값과 자체 보관 지갑 검사를 거친 후, Didit Test VASP가 수취인 지갑 또는 이름으로 선택한 시나리오(승인, 거부, 이름 불일치, 연결 불가, 시간 초과 또는 자체 보관)에 따라 Didit 내부에서 응답합니다. 네트워크, 이메일 또는 다른 Didit 고객에게는 아무것도 전송되지 않습니다.
  • 샌드박스 수신: 테스트 VASP가 테스트 VASP 수신 송금 트리거로 송금을 보내도록 하거나, 수신 Travel Rule 송금 등록으로 직접 등록합니다. 알 수 없는 지갑, 검증되지 않은 지갑, 수취인 이름 불일치, 검증되었고 일치하는 지갑을 테스트하십시오. 네트워크에 연결하지 않고도 실제 수신 결정 흐름을 테스트할 수 있습니다.
  • 통제된 프로덕션 애플리케이션 2개: 두 개의 자체 프로덕션 애플리케이션 사이에서 INTERNAL 방식을 사용하거나(수신 측은 검색 가능하며 서명된 메시지 또는 테스트 입금으로 대상 지갑에 대한 통제권을 증명합니다), 통제 가능한 TRP 거래 상대방을 사용하여 상업용 네트워크 자격 증명 없이 두 당사자 간 교환을 테스트합니다. 프로덕션 애플리케이션에는 일반 요금과 알림이 발생할 수 있으므로, 직접 통제하는 테스트용 신원과 거래 상대방만 사용하십시오.
모든 시나리오와 정확한 결과는 샌드박스에서 Travel Rule 테스트를 참조하십시오. 성공한 샌드박스 또는 로컬 프로토콜 테스트는 상업용 네트워크의 자격 증명, 멤버십 또는 프로덕션 상호 운용성을 검증하지 않습니다. 이를 검증하려면 승인된 네트워크 샌드박스 또는 유효한 멤버십 자격 증명을 갖춘 통제된 프로덕션 교환이 필요합니다.

전송하기

POST /v3/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 고객이 정확히 한 명 있어야 하며, 이 증명은 지갑 보유자가 마지막으로 바뀐 이후에 만들어진 것이어야 합니다. 귀하의 애플리케이션 자체는 계산에 포함되지 않습니다. 최종 사용자가 지갑 소유권 위젯에서 Didit 네트워크 멤버를 선택했다면 그 멤버의 증명만 인정됩니다. 해당 고객의 애플리케이션은 프로덕션 환경(샌드박스 아님)이어야 하고, Travel Rule이 켜져 있으며 is_discoverable이 설정되어 있어야 하고, counterparty_vasp_lei 또는 counterparty_vasp_name으로 지정한 VASP여야 합니다. 자체 선언만 되어 있거나, 스크린샷으로 증명되었거나, 검증되지 않았거나, 주소록에만 등록된 지갑은 INTERNAL을 통해 아무것도 받지 못합니다.
    • 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은 지갑 소유권 위젯을 자동으로 발급하므로, 전송은 자체 보관 지갑 전송으로 여전히 완료될 수 있습니다. 더 나은 거래 상대방 데이터를 확보한 후에는 action: "resend"로 경로 지정을 다시 실행할 수 있습니다.
1단계와 2단계는 요청 안에서 처리됩니다. 3단계부터 5단계까지는 응답 직후에 실행됩니다. 전송은 protocol: null과 함께 AWAITING_COUNTERPARTY 상태로 돌아오며, 각 결과는 travel_rule.status.updated webhook으로 전달됩니다.

TRP 자산 식별자

TRP는 전송되는 자산을 ISO 24165 디지털 토큰 식별자(DTI)로 지정합니다. DTI는 하나의 블록체인에 있는 토큰을 식별하므로, 여러 체인에 존재하는 토큰은 네트워크마다 DTI가 하나씩 있습니다. Didit은 다음 자산에 대해 TRP로 전송합니다. 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를 찾는 첫 번째 단계에서 멈춥니다. 거래의 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 목록이 포함됩니다: 위젯 링크는 최종 사용자가 수행하는 작업의 url입니다. 지갑 소유권 작업이 대기 중일 때는 거래의 action_required 블록(type wallet_ownership)에 동일한 reason과 next_actions가 담깁니다. 따라서 이미 action_required를 따르는 클라이언트는 추가 작업 없이 선택 화면을 표시합니다.

거래 상대방 확인 필드

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

체인 구조

계정 체인(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)를 사용합니다.
이후 전송에는 다음이 포함됩니다:
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은 네트워크에 연결하지 않고 이 값으로 수취인을 식별합니다. 수취인은 계정과 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에서만 해석됩니다). 그 밖의 체인에서는 해당 체인의 고유 주소로 처리됩니다.
INTERNAL 경로는 다른 Didit 고객이 서명된 메시지나 테스트 입금으로 소유를 증명한 지갑을 통해서만 해당 고객에게 도달합니다. 현재 이러한 증명은 Bitcoin, EVM 체인, Solana, Tron에 대해서만 가능합니다. 메모 네트워크, 라이트닝, Litecoin, Bitcoin Cash, Dogecoin에서는 전송이 INTERNAL을 통해 다른 Didit 고객에게 도달하지 않습니다. 다만 메모, 태그, 라이트닝 매칭은 여전히 고객님의 주소록에 적용됩니다. 즉, 고객님의 지갑으로 들어오는 수신 전송, 보고된 입금, TRP 조회가 대상입니다.

발신자 지갑 업데이트

출금은 흔히 고객의 주 지갑에서 선언되지만, 실제로 코인을 보유하고 있던 다른 주소에서 브로드캐스트됩니다. 자금이 실제로 빠져나간 지갑은 PATCH /v3/transactions/{transaction_id}/travel-rule/로 기록합니다:
  • 발신 전송에만 사용할 수 있으며, CANCELLED 또는 EXPIRED 상태의 전송에는 사용할 수 없습니다(409).
  • 전송의 UTXO 입력을 알고 있는 경우, 지갑은 그 입력 중 하나여야 합니다(그렇지 않으면 400). 메모 네트워크에서는 memo도 함께 보내십시오.
  • 이 요청은 기록만 변경합니다. 거래 상대방에게 다시 전송되지 않으며 상태도 그대로 유지됩니다. chain_details.originator는 source: updated와 함께 새 지갑이 되고, timeline에는 이전 지갑과 새 지갑이 모두 기록되며, 전송의 INTERNAL 상대 기록도 같은 방식으로 업데이트됩니다.

블록체인 분석 귀속

지갑 주소만 전달하더라도 Didit은 거래 상대방이 누구인지 알아낼 수 있습니다. 어떤 경로로도 전송을 라우팅할 수 없는 경우, Didit은 목적지 지갑을 블록체인 분석 귀속 데이터와 대조합니다. 이는 암호화폐 거래 스크리닝을 뒷받침하는 것과 같은 분석으로, 어느 지갑이 어느 거래소와 커스터디 기관에 속하는지 알고 있습니다. 귀속 조회는 목적지 지갑과 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 레일로 라우팅됩니다.
응답은 여기서 중요한 필드로 줄여서 보여 줍니다.
  • originator_data는 전송하신 값입니다. 예시의 주체에게 승인된 KYC 인증이 없기 때문입니다. 인증된 사용자의 경우 Didit는 인증된 데이터로 이 값을 생성합니다(검증된 데이터 및 출처 참고). EMAIL 레일은 수신자에게 data_sharing_fields에서 공유하도록 설정한 송금인 필드만 보여 줍니다.
  • beneficiary_data.chain은 ETH입니다. 요청에 체인이 지정되지 않아서 Didit가 통화에서 체인을 가져왔기 때문입니다.
  • status는 APPROVED입니다. 어떤 규칙도 이 거래에 작용하지 않았기 때문입니다. 고객님의 규칙에 따라 다른 상태로 설정될 수 있습니다.
  • 컴플라이언스 담당자는 수령 링크를 받습니다. 담당자가 응답하면 전송이 완료되거나, 이름 일치 검사에 실패하거나, 거부됩니다. 응답하지 않으면 만료됩니다(이메일 수령 참고).
protocol은 응답 이후에 레일이 선택되므로 여전히 null입니다(예를 들어 counterparty_vasp_email을 제공하면 EMAIL). 선택된 후에는 거래 조회에서 확인할 수 있으며, 이후의 모든 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 작업이 생성됩니다.
거래에 내장된 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/의 각 행에도 같은 블록이 포함되며, 전송이 없는 거래의 경우 null입니다. 아래의 전용 전송 엔드포인트도 이 필드들을 반환하며, 추가로 ivms_version, ownership_confirmed, deadline_at, timeline, created_at을 반환하지만, obligations_count와 metadata는 반환하지 않습니다.
COMPLETED 상태의 아웃바운드 전송을 온체인으로 브로드캐스트한 후 마감하려면 다음과 같이 호출합니다.
이 요청은 전송이 아웃바운드 전송이고 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가 반환됩니다.
디스커버리가 거래 상대방을 찾지 못했을 때 직접 지정하려면, {"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}/의 travel_rule 블록, POST /v3/transactions/의 201 응답, 그리고 GET /v3/transactions/의 각 행에 이 정보가 포함되며, 전송 엔드포인트가 반환하는 전송 객체에도 포함됩니다.
  • 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이 지워집니다. 실패한 전송으로 발생한 상태 전이의 타임라인 항목에도 reason 아래에 같은 값이 기록됩니다. 위의 예시 송금의 컴플라이언스 이메일을 메일 제공업체가 거부하면, 해당 거래의 travel_rule 블록은 요약하면 다음과 같습니다:
여기서 delivery는 reason과 일치합니다. 전송 자체가 거부되었기 때문입니다. 리마인더 발송에서 거부되었거나 나중에 반송이 보고된 경우에는 delivery.status가 DELIVERED로 남습니다. 첫 이메일은 실제로 발송되었기 때문입니다. 송금이 멈춘 이유를 알려면 delivery가 아니라 reason을 확인하십시오. 문제를 해결하려면 거래 상대방의 컴플라이언스 이메일을 다시 입력한 다음 재전송하십시오:
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으로 설정하십시오.
이 선언은 라우팅을 완전히 건너뜁니다:
  • 레일은 시도되지 않습니다. 정보를 주고받을 거래 상대방 VASP가 없기 때문입니다. 송금은 UNCONFIRMED_OWNERSHIP 상태로 바로 생성됩니다.
  • auto_wallet_verification이 켜져 있으면(기본값), Didit은 거래에 연결된 지갑 소유권 위젯 세션을 자동으로 생성하고 이를 거래의 action_required 블록에 반환합니다. SDK는 클라이언트 측 제출 시 이 위젯을 자동으로 실행합니다.
  • 고객이 지갑에 대한 소유권을 증명하면, 송금은 수취인 이름 대조를 실행하고 COMPLETED로 진행합니다(대조에 실패하면 COUNTERPARTY_MISMATCHED_DATA로 진행합니다).
이는 EU TFR의 자체 보관 지갑 의무를 API 형태로 구현한 것입니다. 거래 상대방 VASP와 데이터를 주고받는 대신 언호스티드 지갑의 소유권을 확인합니다. 이 선언은 발신 송금의 목적지 측 결제 수단과 수신 송금의 소스 측 결제 수단에서 읽힙니다. 어느 경우든 거래 상대방 참여자입니다. 언호스티드로 선언된 송금은 이미 레일을 거치지 않는 최종 경로에 있으므로, UNCONFIRMED_OWNERSHIP은 재전송할 수 있는 상태가 아닙니다. action: "resend"를 호출하는 대신 소유권 증명을 완료하거나(또는 취소) 하십시오.

수신

지갑 소유권 확인

송금은 다음 두 가지 경우에 UNCONFIRMED_OWNERSHIP 상태로 대기합니다:
  • 수신 송금이 아직 소유권이 확인되지 않은 주소록 항목으로 확인되는 경우.
  • 거래 상대방의 지갑이 자체 보관 지갑으로 선언된 경우(counterparty.payment_method.method_type: "unhosted_wallet"). 출금 송금이든 입금 송금이든 해당합니다. 선언된 자체 보관 지갑을 참고하세요.
귀사의 애플리케이션이 송금을 확인하거나 거부합니다:
요청 본문에는 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으로 거부됩니다.
  • 네트워크를 통해 이미 응답이 끝난 송금(VerifyVASP 송금, 또는 이미 결정된 GTR 또는 Sygna Bridge 송금)은 409를 반환합니다(네트워크를 통한 송금 참고).
응답은 위에서 설명한 더 상세한 송금 객체입니다. 새로운 INTERNAL 송금은 여기서 대기하지 않습니다. 지갑 소유를 이미 입증한 고객에게만 전달되기 때문입니다. 이 규칙이 적용되기 전에 생성되어 아직 대기 중인 이전 INTERNAL 송금은 지갑 제어권(서명된 메시지 또는 테스트 입금)을 증명할 때까지 발신자의 정보를 숨기며, 증명하기 전까지 확인 요청은 409를 반환합니다. 거부는 여전히 가능합니다. 거래 상대방이 지갑이 자체 보관 지갑이라고 응답한 경우(현재 샌드박스에서는 Didit Test VASP가 이 응답을 제공합니다), 확인하면 수취인의 이름으로 지갑을 주소록에 추가하고 이름 일치 검사를 실행합니다. 일치하면 송금이 완료되고 지갑은 확인 완료로 표시됩니다.

네트워크를 통한 송금

Didit은 거래 상대방이 GTR, VERIFYVASP 또는 SYGNA를 통해 보내는 Travel Rule 송금도 수신할 수 있습니다. 수신 기능은 Didit이 네트워크별로 따로 켭니다. 현재 어떤 네트워크도 켜져 있지 않습니다. 각 네트워크에 Didit의 endpoint를 등록해야 하며, Didit은 아직 이 네트워크들의 회원 자격이 없기 때문입니다. TRUST 송금은 수신할 수 없습니다. 네트워크의 수신 기능이 켜지면, 네트워크가 전달하는 메시지는 다음과 같이 처리됩니다:
  • 라우팅. 메시지가 해당 네트워크에서 귀사가 연결한 회원 자격의 참가자 코드를 지정하면 귀사의 애플리케이션에 전달됩니다. Didit 자체 회원 자격을 수신인으로 지정한 메시지는 수취 지갑의 제어권을 입증한 활성 Didit 고객이 귀사뿐인 경우에만 귀사에 전달됩니다. 그 밖의 메시지는 모두 거부되므로, 어떤 고객도 다른 고객의 송금을 받지 않습니다.
  • 결정. Didit은 귀사의 애플리케이션에 수신 송금을 생성하고, TRP 문의와 같은 방식으로 결정합니다. 지갑 일치, 소유권, 이름 일치 검사, 그리고 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에 감사 로그 항목이 기록됩니다:
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에는 필드 목록이 담깁니다. 두 값 모두 필드 이름과 결과만 포함하고 실제 값은 포함하지 않으므로, 데이터가 마스킹된 상태에서도 읽을 수 있습니다:
타임라인 항목은 필드를 알려줍니다. 예를 들어 “수취인의 생년월일이 지갑 소유자와 일치하지 않음”처럼 표시됩니다. 불일치 후 발생한 TRP 거절은 발신 VASP에 서로 다른 필드를 알려줍니다. 예를 들어 “수취인 데이터가 지갑 소유자와 일치하지 않음: name”이라고 표시됩니다. 상대 VASP가 알게 되는 정보는 의도적으로 제한됩니다. 이름이 일치하지 않는 동안에는 이름만 보고되며(mismatched_fields: ["name"], 다른 모든 필드는 not_compared), 이름이 일치하면 일치하지 않는 모든 필드가 보고됩니다. 이 규칙은 INTERNAL 교환에서 발신자 측과 TRP 거절에 적용됩니다. 양쪽 모두 이름이 없는 경우에는 not_compared로 표시되며 불일치로 간주되지 않습니다. 국가는 가리키는 국가를 기준으로 비교하므로 ES와 ESP는 일치하는 것으로 봅니다.

이름 일치 점수

이름이 비교된 모든 송금에는 name_match가 포함되며, 그 결과를 결정한 타임라인 항목에는 detail 옆에 같은 객체가 포함됩니다:
  • 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/로 송금의 메시지를 조회하고 보냅니다:
  • 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}/를 사용합니다.
PATCH는 holder_name, holder_vendor_data, entity_type, travel_address를 받으며, 이 네 필드만 반환합니다. DELETE는 항목을 삭제합니다(204). 주소는 다시 등록할 수 있도록 해제되며, 새 전송과의 일치도 즉시 중단됩니다.
주소 일치 검사는 대소문자를 구분하지 않으며, 메모 네트워크에서는 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/를 사용하면 한 번의 호출로 지갑을 최대 10,000개까지 가져올 수 있습니다. 형식은 JSON({"entries": [...]} 또는 배열만 있는 형태) 또는 헤더 행이 있는 UTF-8 CSV 파일(최대 5MB)입니다. 각 행은 address, address_hash(소문자로 변환한 주소의 SHA-256, 64자리 16진수 또는 44자리 base64 문자열), 또는 둘 다로 지갑을 지정하며, 위의 항목 필드를 원하는 만큼 추가할 수 있습니다. 주소를 공개하지 않고 지갑을 등록하려면 해시만 보내십시오. 이 항목도 다른 항목과 마찬가지로 해당 주소로의 전송과 일치하며, 지갑 소유권 위젯으로 소유권을 증명할 수 있습니다.
가져오기는 멱등성이 있으며, 보낸 순서대로 모든 행의 결과를 보고합니다. 결과는 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를 호출합니다. 위의 예시 전송의 수령 세부 정보는 다음과 같습니다:
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자리 전체를 포함합니다.
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 키로 아직 대기 중인 입금을 조회하세요:
응답은 GET /v3/travel-rule/transfers/와 같은 행을 사용하며, 송신 VASP의 데이터를 기다리는 인바운드 입금으로 범위가 제한됩니다. 데이터가 별도의 수신 메시지로 도착했다면 직접 두 항목을 연결하세요:
{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/로 사후에 등록하세요.
originating_vasp.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 체크 디지트는 확인하지 않습니다. Didit는 인바운드 travelRule 거래를 생성합니다. 아직 Travel Rule 송금이 없고 같은 해시와 지갑으로 이미 제출한 입금이 있으면, 그 입금을 재사용합니다. 그런 다음 주소와 메모를 기준으로 송금을 지갑 주소록과 대조해 처리합니다:
  • 일치하는 항목이 없으면 -> ON_HOLD. POST .../travel-rule/ownership/로 해결합니다. confirmed: true를 보내면 지갑이 확인된 상태로 주소록에 추가되고 이름 일치 확인이 실행됩니다. 일치하면 송금은 COMPLETED를 거쳐 FINISHED로 바뀝니다.
  • 항목은 있으나 소유권이 증명되지 않았으면 -> UNCONFIRMED_OWNERSHIP.
  • 수취인 이름이 항목의 holder_name과 일치하지 않으면 -> COUNTERPARTY_MISMATCHED_DATA.
  • 확인된 항목이 있고 이름이 일치하면 -> COMPLETED, 이어서 즉시 FINISHED(입금은 이미 정산되었습니다).
위의 예시에서 등록한 확인된 주소록 항목을 기준으로 응답은 다음과 같습니다:
새 거래의 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를 보내고, 그 뒤에 이 이벤트들을 보냅니다. 등록에는 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 웹뷰 안에서는 절대 열면 안 됩니다)는 지갑 소유권 위젯을 참고하세요.

디렉터리

GET /v3/travel-rule/vasps/는 공유 디렉터리 항목과 Didit 네트워크 멤버를 함께 검색합니다. 각 행에는 현재 애플리케이션이 해당 VASP에 어떤 방식으로 도달할 수 있는지가 표시됩니다.
search는 이름이나 별칭의 일부, 정확한 LEI 또는 도메인(counterparty.example 또는 해당 도메인의 URL)과 일치합니다. 다음 매개변수로 결과를 좁힐 수 있습니다. 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에만 전송하므로, 디렉터리 경로를 사용할 수 없습니다. 모든 경로는 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로 애플리케이션 자체 연락처를 관리합니다.
  • 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 네트워크의 멤버 모두에 대해 동일하게 작동합니다. 디렉터리에 표시되는 id로 지정하십시오.

점수와 위험 수준

dd_score는 0에서 100까지이며 높을수록 안전함입니다. 모든 결과는 dd_score_semantic으로 이를 나타내며, 이 값은 항상 higher_is_safer입니다. risk_level은 점수가 속한 구간입니다. 점수는 일곱 개 구성 요소의 합계입니다. dd_score_breakdown은 각 구성 요소를 {points, max, reason, detail} 형식으로 보여 줍니다. max는 해당 구성 요소의 가중치이고, reason은 번역할 수 있는 기계 판독용 코드이며, detail은 한 줄짜리 영어 설명입니다. 점수는 소수점 첫째 자리까지 반올림하고, 합계는 정수로 반올림합니다. 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이 모든 디렉터리 항목에 대해 수행하는 네 가지 점검에 대한 간략한 보고서입니다. 이 점검은 플랫폼이 확인한 출처에서만 수행하며, 고객이 보고한 데이터는 사용하지 않습니다. checks의 각 점검은 status(pass, fail, 또는 해당 사항을 판단할 수 있는 기록상의 출처가 없으면 unknown), 기계 판독용 reason, 한 줄짜리 영어 detail을 보고합니다. passed, failed, unknown, total은 점검 개수를 셉니다. level은 essential이며, checked_at은 보고서가 작성된 시점을 나타냅니다. essentials 구성 요소는 결정된 점검 중 통과한 비율을 점수로 반영하므로, unknown 점검은 항목에 유리하게도 불리하게도 계산되지 않습니다. 주간 디렉터리 새로 고침이 항목을 처음 점검하기 전까지 보고서는 비어 있으며, Didit 네트워크 멤버에 대해서는 항상 비어 있습니다.

응답성

responsiveness는 VASP가 Travel Rule 요청에 응답하는지 여부를 나타냅니다.
total은 최근 90일 동안 Didit 고객이 해당 VASP를 대상으로 시작했고 결과가 확정된 요청의 수이며, answered는 그중 VASP가 응답한 요청의 수입니다.
  • 집계 대상: 네트워크 또는 프로토콜 경로로 보낸 요청과, 공유 주소로 이메일을 보낸 요청입니다. 공유 주소는 항목의 컴플라이언스 주소 또는 플랫폼 연락처입니다.
  • 집계 제외: 고객이 직접 입력한 주소나 고객의 개인 연락처로 보낸 이메일 요청입니다. 해당 교환은 고객 본인의 거래 내역에만 남으며, 다른 모든 고객에게 해당 VASP의 평가를 표시하지 않습니다.
  • 응답함: COMPLETED, FINISHED, COUNTERPARTY_VASP_GENERAL_DECLINE, COUNTERPARTY_MISMATCHED_DATA. 거절이나 불일치도 응답으로 봅니다. VASP가 관여했다는 뜻이기 때문입니다.
  • 응답하지 않음: EXPIRED.
  • 둘 다 아님: 그 밖의 모든 상태입니다. CANCELLED 요청은 발신자 본인의 행위이고, COUNTERPARTY_VASP_NOT_REACHABLE은 발신자 측의 문제일 수 있으며, 아직 진행 중인 요청은 결정되지 않은 상태입니다.
Didit 네트워크 멤버는 Didit 내부에서 응답하며, 항상 건수가 0인 UNKNOWN으로 표시됩니다. 응답하지 않는 거래 상대방에 대해서는 무응답 정책 또는 travel-rule-counterparty-unresponsive 프리셋으로 조치할 수 있습니다.

거래 상대방에 대한 결정

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

거래 상대방 스크리닝

거래 상대방 스크리닝은 거래 상대방 VASP 자체를 법인으로서 제재 및 부정적 언론 보도에 대해 점검합니다. 선택 사항이며 과금됩니다. 설정에서 counterparty_screening_enabled를 켜면 활성화됩니다. 꺼져 있으면 아무것도 스크리닝되거나 과금되지 않습니다.
  • 과금. 새 스크리닝마다 조직의 기업 AML 스크리닝 가격이 청구됩니다. 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/는 거래 상대방의 등록된 컴플라이언스 주소로 설문지를 이메일로 보내며, 답변 페이지로 연결되는 일회용 링크를 함께 보냅니다:
  • 발송 위치. 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개 질문이 있습니다:

특정 거래 상대방과의 이체

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

초기 시행 및 응답 없음 정책

이체가 만료되면(AWAITING_COUNTERPARTY 상태에서 confirmation_timeout_hours가 경과한 경우), timeout_outcome이 해당 거래의 처리 방식을 결정합니다. 두 가지 설정을 사용하면 흔한 타임아웃 사유 두 가지를 다르게 처리할 수 있습니다: 두 설정 모두 HOLD, REJECT, PROCEED 중 하나를 값으로 가지며, 의미는 timeout_outcome과 같습니다. 또는 빈 문자열을 설정하여 timeout_outcome을 따르도록 할 수 있습니다. 두 설정 모두 기본값은 비어 있으므로, 직접 설정하기 전까지는 아무것도 바뀌지 않습니다:
적용되는 설정이 여러 개일 경우 처음으로 해당되는 설정이 결과를 결정합니다:
  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 결과에서도 거래를 검토 대상으로 옮길 수 있습니다.

규칙 필드와 프리셋

규칙 필드 네 개는 거래의 Travel Rule 교환이 도달한 거래 상대방에 대해 애플리케이션이 알고 있는 정보를 담습니다. 해당 정보는 디렉터리 항목이거나, INTERNAL 교환의 경우 교환을 받은 Didit 고객입니다. 알려진 거래 상대방에 도달하지 않은 교환에서는 각 필드가 비어 있습니다. travel_rule 번들의 프리셋 5개가 이 필드에 따라 작동합니다. Transactions → Rules → Library에서 설치하거나 규칙 라이브러리 API로 설치할 수 있습니다: 다섯 프리셋은 모두 Travel Rule 거래에만 적용됩니다.

webhook

travel_rule.status.updated 이벤트를 구독하도록 webhook 대상을 설정하면, 애플리케이션의 모든 전송에 대한 모든 상태 전환을 아웃바운드와 인바운드 모두 받아볼 수 있으며, INTERNAL 레일 구간도 포함됩니다. 라우팅은 거래 생성 응답이 반환된 후에 실행되므로, COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, 또는 즉시 정산되는 INTERNAL 교환과 같은 결과는 생성 응답이 아니라 이 이벤트를 통해 전달됩니다.
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가 응답한 전송에는 rail: "TEST_VASP", test_vasp: true, test_vasp_scenario가 포함됩니다. 전체 이벤트 목록, 서명 검증, 재시도 정책은 webhook 가이드를 참고하십시오.

요금

Travel Rule 요금은 시작되는 아웃바운드 교환 건당 $0.02이며, 거래가 생성될 때 한 번 청구됩니다. 이후 교환이 상태 전환을 몇 번 거치든 추가로 청구되지 않습니다. resend는 이 요금을 다시 청구하지 않습니다.
  • Travel Rule이 켜진 애플리케이션의 아웃바운드 travelRule 거래 중 금액이 threshold_amount 이상이어서 Didit가 관리형 교환을 시작하는 거래에 적용됩니다. 자체 보관 지갑 또는 사용자 본인의 인증된 지갑으로의 전송도 포함되며, 이 경우 소유권 증명이 곧 교환입니다.
  • Travel Rule 데이터가 포함된 그 밖의 아웃바운드 거래는 표준 Transaction Screening으로 청구됩니다. 요금제에 포함된 월간 거래 허용량까지는 무료이며, 초과분은 $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 번들에 포함되어 있습니다. 다른 모든 사전 설정 번들과 마찬가지로, Business Console에서 Transactions → Rules → Library를 통해 활성화하십시오. 이 규칙은 전송 생성 시, 소유권 확인 시, 이메일 수신 응답 시, 그리고 만료 처리기에서 발생하는 관련 상태 전환마다 실행됩니다. 따라서 끝까지 해결되지 않는 전송도 조용히 정산되지 않고 컴플라이언스 검토 대상으로 표시됩니다.

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

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

FAQ

거래 교환 방식

Didit이 교환을 직접 실행합니다. travelRule 전송을 제출하면 Didit은 실시간으로 거래 상대방을 탐색하고, 송금인 및 수취인 데이터를 이를 전달하는 경로를 통해 수신 VASP에 보냅니다. 상대방 측이 확인한 후에만 전송을 COMPLETED로 표시합니다. 이는 정산 후 로그가 아니라 관리되는 핸드셰이크입니다. 상대방의 응답은 TRUST를 제외한 모든 경로에서 처리됩니다. TRUST는 전송만 하므로, 이 경로를 사용한 전송은 완료되지 않고 만료됩니다. GTR, VERIFYVASP, SYGNA에서는 Didit이 네트워크의 응답에서 답변을 읽거나 5분마다 조회하며, 아직 이들 경로로 실시간 네트워크를 통해 완료된 교환은 없습니다. TRP에서는 상대방이 즉시 응답하지 않는 경우 해당 응답을 처리하려면 Didit의 공개 TRP 호스트가 필요하며, 이 호스트는 아직 가동 중이 아닙니다(Travel Rule 네트워크 참조). Didit은 모든 교환의 전체 기록과 컴플라이언스 상태도 보관합니다(13개 상태의 수명 주기, webhook, 감사 추적). Travel Rule을 자체 프로세스로 운영하길 원하면, Didit은 귀사의 단일 기준 시스템으로 데이터를 수동적으로 기록하여 사용할 수 있습니다(아래의 수동 기록 질문 참조). 두 가지 모드를 모두 사용할 수 있습니다. 설정에서 is_enabled: true를 설정하면 능동 교환이 실행되며, 기본값은 false입니다. 그 전까지 Didit은 데이터를 수동으로 기록합니다.
고유 기능입니다. 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는 전송만 합니다. 개요를 참조하십시오.
네이티브로 구현되어 있으며, 엄격한 우선순위에 따라 시도됩니다. INTERNAL(두 VASP 모두 Didit 사용), TRP(OpenVASP Travel Rule Protocol), GTR, TRUST, VerifyVASP, Sygna Bridge, 그리고 도구가 없는 상대방을 위한 EMAIL 대체 경로입니다. 네트워크 경로에는 오늘날 귀사의 멤버십이 필요하고, TRUST는 전송만 하며, 네트워크를 통한 수신은 아직 활성화되지 않았습니다. Travel Rule 네트워크를 참조하십시오. CODE, TRISA, Notabene, Veriscope는 Didit Marketplace에 액세스 요청 타일로 표시되며, 아직 연동되어 있지 않습니다. 전체 라우팅 표는 Didit이 교환을 라우팅하는 방식을, 각 네트워크가 현재 도달할 수 있는 범위는 Travel Rule 네트워크를 참조하십시오.
TRP를 기본으로 권장합니다. 공유 멤버십이 필요하지 않으며, Travel Address를 공유하면 TRP를 지원하는 모든 VASP가 귀사에 도달할 수 있습니다. 상대방을 지갑 주소로만 알고 있는 경우, 귀속된 디렉터리 항목에 이미 TRP 엔드포인트가 있을 때만 블록체인 분석 귀속을 통해 TRP로 라우팅됩니다. 아웃바운드의 경우 Didit은 TRP 자산 식별자에 나열된 자산을 이 경로로 전송하며, 즉시 응답하지 않는 상대방에게는 아직 가동되지 않은 Didit의 공개 TRP 호스트가 필요합니다. 멤버십 네트워크의 경우 상대방이 활동하는 지역에 따라 선택하십시오. GTR은 폭넓은 글로벌 범위를 제공하고, TRUST는 미국 VASP에 중점을 두며, VerifyVASP(현재 Sygna Bridge도 운영)는 아시아에서 강력한 범위를 보유하고 있습니다. 예, 오늘날에는 자체 멤버십이 필요합니다. Didit은 아직 이들 네트워크에 플랫폼 멤버십을 보유하고 있지 않으며, 제공자로 온보딩 중입니다. Marketplace에서 멤버십을 연결하십시오. 단일 네트워크에만 묶여 있지 않습니다. TRUST는 전송만 하므로 이를 사용한 전송은 완료되지 않으며, 아직 실시간 네트워크를 통해 완료된 네트워크 교환은 없다는 점을 기억하십시오.
해결 체인을 통해 찾습니다. 먼저 지갑의 소유권을 증명한 다른 Didit 고객(INTERNAL 경로), 다음으로 저장된 Travel Address에 대한 주소록, 그다음 프로토콜 및 네트워크 경로, 마지막으로 블록체인 분석 귀속 순서입니다. 전송에 대상 지갑만 포함된 경우, Didit은 블록체인 분석 데이터와 대조하여 상대방 거래소 또는 커스터디 서비스를 귀속시키고 이를 VASP 디렉터리에 등록합니다. 전송은 해당 디렉터리 항목에 이미 경로(예: TRP 엔드포인트)가 있을 때에만 자동으로 라우팅됩니다. 그렇지 않으면 상대방 이름이 기록된 채 COUNTERPARTY_VASP_NOT_FOUND로 종료되며, 경로가 생기면 다시 전송할 수 있습니다. 예를 들어 해당 지갑의 주소록 항목에 상대방의 travel_address를 저장한 후 다시 전송할 수 있습니다(TRP 자산 식별자에 나열된 자산에 해당).

요금 및 라이선스

Travel Rule은 사용량 기반으로 별도 판매되는 제품입니다. 신원 인증에 포함되어 있지 않습니다. **시작되는 아웃바운드 교환 건당 $0.02**가 부과됩니다([공개 요금](#요금), 사용량 등급 없음, 영업 담당자 상담 불필요). 라우팅을 위해 상대방 VASP를 식별하는 블록체인 분석 조회가 필요한 경우, 해당 조회는 [귀속 요율](#어트리뷰션-요금)에 따라 한 번 청구됩니다. 관리형은 $0.15부터, 기존 제공자 키가 있으면 $0.02입니다. 암호화폐 모니터링이 같은 지갑을 이미 스크리닝했다면 다시 청구되지 않습니다. 임계값 미만 전송이나 Travel Rule이 꺼진 상태에서 기록된 전송은 대신 거래 스크리닝으로 청구됩니다. 수신 전송(예: Didit이 수취인 측에서 생성하는 레그)에는 Travel Rule 요금이 부과되지 않습니다.
아니요. travelRule 거래는 한 줄로 청구됩니다. 아웃바운드 교환을 시작하는 경우에는 Travel Rule 요금($0.02)이, 그 외에는 표준 거래 스크리닝 요금이 부과됩니다. 블록체인 분석 조회는 한 번만 청구됩니다. 암호화폐 모니터링이 해당 거래의 대상 지갑을 이미 스크리닝했다면 귀속은 저장된 결과를 추가 비용 없이 재사용합니다. Didit이 수취인 측에서 생성한 수신 레그에는 Travel Rule 요금이 절대 청구되지 않습니다.
예. Didit의 KYT 및 거래 모니터링, AML 및 제재 스크리닝, 블록체인 분석 기반 지갑 위험, Travel Rule 기능은 모두 docs.didit.me에 공개 문서화되어 있으며, 이 페이지들을 규제 관련 파일에 자유롭게 인용하실 수 있습니다. AIFC/AFSA, MiCA 또는 기타 VASP 라이선스 신청과 같이 특정 제출물에 맞춘 공식 제품 역량 설명서가 필요하다면, 규제 기관이 요구하는 형식을 알려 Didit 계정 팀에 문의하십시오.

운영 및 데이터

이 전송은 즉시 NOT_APPLICABLE로 처리됩니다. 어떤 연동 경로도 시도되지 않고, 신원 데이터는 Didit 외부로 전송되지 않습니다. 이 거래는 Travel Rule 수수료가 아니라 표준 거래 스크리닝으로 과금됩니다. Didit는 임계값을 애플리케이션의 기본 통화로 환산한 금액과 비교합니다. Didit가 금액을 환산할 수 없으면 임계값은 적용되지 않고 정보가 교환됩니다. EU TFR에는 최소 금액이 없으므로, EU CASP는 threshold_amount를 0.00으로 유지해야 합니다.
confirmation_timeout_hours가 지날 때까지 전송은 AWAITING_COUNTERPARTY 상태에 머무릅니다. 그 후 스위퍼가 전송을 EXPIRED로 옮기고, 원거래에 timeout_outcome 설정을 적용합니다. 값은 검토를 위한 HOLD, 거부를 위한 REJECT, 거래를 그대로 두는 PROCEED입니다. tr_eu_expired_review 사전 정의 규칙은 만료된 모든 건을 분석가에게 추가로 알립니다. 거래 상대방이 메시지를 아예 받지 못하는 경우는 다릅니다. Didit가 재시도하며, 여섯 번째 실패 시도 후에는 전송이 COUNTERPARTY_VASP_NOT_REACHABLE 상태로 옮겨집니다(전송 및 재시도 참고).
네, 그럴 수 있습니다. 이때 사용하는 것이 EMAIL 경로입니다. 상대방의 컴플라이언스 담당자는 일회용 수령 링크를 받고, Didit 계정, API 키, 연동 없이 호스팅 페이지에서 정보 교환을 확인하거나 거부할 수 있습니다. 교환이 가능해지기 전에 이미 정산된 입금에는 입금 후 sunrise 등록을 사용하세요.
대상을 자체 보관 지갑으로 선언하세요(payment_method.method_type: "unhosted_wallet"). 그러면 Didit는 VASP 라우팅을 건너뛰고, 지갑 소유권 위젯을 통해 고객이 해당 지갑을 통제한다는 증명을 수집합니다. 증명 방식은 메시지 서명, Satoshi 테스트, 스크린샷 또는 자기 선언입니다. 이는 API 형태로 구현된 EU TFR 자체 보관 지갑 의무입니다.
네. 귀사 측에서 지갑 소유권이 확인되기 전까지는, 인바운드 전송의 거래 상대방 개인정보가 전송 객체와 거래의 거래 상대방 객체 모두에서 마스킹됩니다(마스킹된 거래 상대방 데이터 참고). 이메일 수령 페이지에는 전송 요약(송금인 VASP 이름, 금액, 대상 지갑)만 표시되며, 개인 정보 페이로드는 표시되지 않습니다. 연동 경로의 정보 교환은 HTTPS로 이루어지며, 네트워크 경로(GTR, TRUST, VERIFYVASP, SYGNA)는 IVMS 페이로드를 수신 VASP의 공개 키로 추가 암호화합니다.
네. Travel Rule을 비활성화한 상태에서 제출하는 travel_rule_details는 모두 거래에 별도의 처리 없이 그대로 기록됩니다. 여기에는 호출자가 지정한 status(현재 값 또는 레거시 값)도 포함됩니다. 연동 경로는 호출되지 않으며, 협상도 이루어지지 않습니다. 이 방식은 과거 기록을 채우거나 자체 절차를 운영하면서도 하나의 기록 시스템을 유지하려는 경우에 사용하세요.
Travel Rule이 켜진 샌드박스 애플리케이션을 사용하세요. 샌드박스에서는 규칙, 과금, transaction.created 및 transaction.status.updated 웹훅이 건너뛰어집니다. 모든 아웃바운드 교환은 TEST_VASP 경로를 통해 Didit 테스트 VASP로 전달되며, 테스트 VASP는 선택한 시나리오(수락, 거부, 이름 불일치, 도달 불가, 시간 초과, 자체 보관 지갑)에 따라 Didit 내부에서 응답합니다. environment: sandbox로 표시된 travel_rule.status.updated 웹훅을 받게 되며, 실제 거래 상대방에게는 어떤 메시지도 전달되지 않습니다. 인바운드 테스트의 경우 테스트 VASP 인바운드 전송 트리거를 사용해 테스트 VASP가 전송을 보내게 하거나, POST /v3/travel-rule/inbound/로 전송을 등록하세요. 두 방식 모두 지갑 주소록을 기준으로 결정되며, 결과는 ON_HOLD, UNCONFIRMED_OWNERSHIP, COUNTERPARTY_MISMATCHED_DATA, 또는 COMPLETED 후 FINISHED입니다. 실제 다른 애플리케이션과 두 당사자 간 교환을 테스트하려면, INTERNAL 경로에서 본인 소유의 라이브 애플리케이션 두 개를 사용하세요. 수신 측 애플리케이션은 검색 가능해야 하며, 서명된 메시지나 테스트 입금으로 대상 지갑에 대한 통제권을 증명해야 합니다.

다음 단계

지갑 소유권 위젯

서명, Satoshi 테스트, 스크린샷 또는 자기 선언으로 고객이 지갑을 통제한다는 사실을 증명합니다.

상호 운용성

다른 VASP가 귀사에 연결할 때 사용하는 거래 상대방용 엔드포인트입니다: TRP, 콜백, 이메일 수령, sunrise.

네트워크

각 경로와 네트워크가 현재 도달할 수 있는 범위와 멤버십 승인을 기다리는 항목을 확인합니다.

샌드박스에서 테스트

실서비스 전에 Didit 테스트 VASP에서 모든 결과를 실행해 보세요.

거래 제출

거래 생성 페이로드에 Travel Rule 데이터를 포함하는 방법입니다.

규칙 라이브러리

Travel Rule 번들을 포함한 전체 사전 정의 규칙 카탈로그입니다.

웹훅

travel_rule.status.updated와 다른 모든 이벤트 계열을 구독하세요.

콘솔 가이드

거래 상세 보기에서 Travel Rule 상태를 확인하는 방법입니다.
마지막 수정일 2026년 11월 7일