# 자산 이전 허가 요청

## 자산의 이전 허가 요청을 검토하고, 결과를 반환합니다.

<mark style="color:green;">`POST`</mark> `/v1/beneficiary/transfer`

가상자산을 전송하려는 VASP 는 자산을 보내기 전에 수취 VASP 에 '자산 이전 허가' 를 요청합니다. 이 '자산 이전 허가 요청' 을 처리하기 위한 API 를 구현해야 합니다.

**API 동작 명세**

1. 암호화된 payload 를 자신의 Private Key 로 복호화합니다.
2. 이전할 자산 정보와 Originator 정보, OriginatingVASP 정보를 확인하고 이 자산 이전을 허용할지 판단할 수 있습니다. VerifyVASP 에서 온 요청은 originatingVASP 객체가 payload 밖에 있습니다. payload -> ivms101 안으로 덮어써 주세요.
3. Beneficiary 에 담겨있는 수취인의 주소 정보가 실제로 수취 가능한 주소인지 확인합니다.
4. Beneficiary 에 수취인의 이름이 있을 경우에는 해당 주소의 소유자와 일치하는지도 확인합니다. 이름 정보는 PrimaryIdentifier 만 정의돼 있다면 성과 이름을 모두 포함하는 경우입니다.
5. 필요하다면 수취 주소에 대해서 Sanction screening 도 수행합니다.
6. 이 내용을 토대로 자산 이전 정보를 DB 에 저장하고, `status` 값을 부여합니다.  `verified` 또는 `denied` 로 지정할 수 있습니다.
7. 요청 메시지의 내용을 확인한 결과를 토대로 응답( response) 메시지를 생성합니다. 이 때, Originator, OriginatingVASP 정보는 요청의 값을 그대로 복사하여 사용하고, Beneficiary 정보는 수취 VASP 의 정보를 기준으로 작성합니다.
8. 응답 메시지 서명을 첨부해서 응답을 보냅니다.

**REQUEST Body Schema: application/jsonRequest**

중요: Originator 의 개인정보는 2022.3.25 현재 개인인 경우 개인의 이름, 법인인 경우 법인과 대표자의 이름에 대한 정보만 보내기로 합니다.

* transferId(Required):
  * 자산을 전송하려는 VASP 가 생성하는 자산 이전 내역의 고유 식별번호입니다.
  * 요청을 보내는 클라이언트에서 UUID v4 값을 생성해서 보냅니다.
* currency(Required): 이전하려는 가상 자산의 심볼입니다.(대소문자를 구분하지 않습니다)
* amount(Required): 이전하려는 가상 자산의 총 볼륨입니다.
* historicalCost(Optional): 이전하려는 가상자산의 취득 원가입니다. (국세청 요구 사항이지만, 아직은 사용하지 않습니다.)
* tradePrice(Required):
  * 법정 화폐 환산한 가상 자산 전송 금액입니다. 자체 시세 정보가 없는 경우에는 타 VASP의 시세 API 를 사용하여 환산합니다.
* tradeCurrency(Required): 법정 화폐로 환산할 때 사용한 ISO 4217 표준을 따르는 법정 화폐 코드입니다.

:exclamation:tradePrice에는 해당 자산의 수량과 가격을 곱하여 법정 화폐로 계산한 값을, tradeCurrency에는 법정 화폐의 종류를 입력합니다. \
예를 들어 2 BTC를 이전하는 경우, 그 시점의 가격이 $42,708라고 한다면 총 가격은 42,708\*2 = 85,416 달러 입니다. 이 경우  다음과 같이 입력 합니다.&#x20;

```
"tradePrice": "85416", 
"tradeCurrency": "USD",
```

* isExceedingThreshold(Required):
  * tradePrice 가 법령에서 지정한 트래블룰 적용 기준을 초과하는지 여부입니다.
  * 해당 필드는 true 또는false로 입력하며, 필드값이 true 일 경우, 요청(request)의Beneficiary 이름을 실제 가상 자산 주소를 소유한 Beneficiary 이름과 비교하게 됩니다.
  * 필드값이 true일 경우, 요청(request)에 Beneficiary이름이 없거나 다를 경우 '거절(deny)' 응답을 보냅니다.
* originatingVasp(Optional, 대소문자 주의): VerifyVASP 로부터 들어오는 요청에 한하여 암호화 영역인 payload 외부에 originatingVASP 객체가 포함돼 있습니다. payload 밖에 originatingVASP 객체가 있으면 payload 영역의 ivms101 에 덮어 써주세요.
  * countryOfRegistration(Optional) : VerifyVASP는 해당 필드값이 포함되지 않습니다.
* payload(Required): IVMS101 메시지를 담기 위한 객체입니다.
  * ivms101(Required): IVMS101 메시지 표준을 따르는 송금인(Originator), 수취인(Beneficiary), 송신 VASP(OriginatorVASP), 수취VASP(BeneficiaryVASP) 등 가상자산 이전에 관여하는 각 주체를 IVMS101 국제 표준에 맞춰서 정의한 객체입니다. '자산 이전 허가 요청' 에서는 Originator 의 이름과 자산 주소, Beneficiary 의 자산 주소, OriginatingVASP 정보가 반드시 포함되어야 하고, Beneficiary 이름은 선택 사항입니다.
    * Originator(Required): 자산을 이전하고자 하는 송금인(개인) 또는 법인 및 대표자에 대한 정보입니다.
      * originatorPersons(Required): naturalPerson(개인), legalPerson(법인) 두 종류의 객체가 있으며, 법인의 경우에는 legalPerson(법인)과 naturalPerson(대표) 정보를 모두 설정해야 합니다. 배열 객체이며, 배열의 각 요소는(element) naturalPerson 또는 legalPerson 중 하나 만을 정의해야 합니다. 자세한 내용은 본 매뉴얼 위쪽의 `IVMS101 표준` 항목을 참고해주세요.
        * naturalPerson(Required): 개인에 대한 정보를 설정하기 위한 객체로 name(이름) 정보를 필수로 설정해야 합니다.
          * name(Required):
            * nameIdentifier: 법적 성명을 기입합니다. 국내 VASP 간 거래인 경우는 한글로 기입하고, 해외 VASP 와 거래하는 경우 영문으로 기입합니다. `IVMS101 표준` 항목을 참고해주세요.
              * primaryIdentifier: 성명 중 성을 기입합니다. 분리할 수 없는 경우는 성과 이름을 순서대로 함께 입력합니다.
              * secondaryIdentifier: 성명 중 이름을 기입합니다. 성과 이름을 분리할 수 없는 경우는 생략합니다.
              * nameIdentifierType: 'LEGL'(legal) 로 고정합니다.
            * localNameIdentifier: 해외 VASP 와 거래하는 경우 한글 이름을 추가로 전달하기 위해 정의합니다.
              * primaryIdentifier: 한글 표기 성명 중 성을 기입합니다. 분리할 수 없는 경우 성과 이름을 순서대로 함께 입력합니다.
              * secondaryIdentifier: 한글 표기 성명 중 이름을 기입합니다. 분리할 수 없는 경우는 생략합니다.
              * nameIdentifierType: 'LEGL'(legal) 로 고정합니다.
          * customerIdentification(Optional): 자산을 전송하는 송금인을 VASP에서 식별 가능한 식별자 (UID 또는 IDX)
        * legalPerson(Optional): 법인에 대한 정보를 설정하기 위한 객체로 name 객체를 필수로 설정해야 합니다.
          * name(Required):
            * nameIdentifier: 법인의 등록 상 명칭을 기입합니다. 국내 VASP 간 거래인 경우는 한글 또는 영문으로 기입하고, 해외 VASP 와 거래하는 경우 영문으로 기입합니다.
              * legalPersonName: 법인명을  기입합니다.
              * legalPersonNameIdentifierType: 'LEGL'(legal) 로 고정됩니다.
          * customerIdentification(Optional): 자산을 전송하는 송금인을 VASP에서 식별 가능한 식별자 (UID 또는 IDX)
      * accountNumber(Required): 자산을 전송하는 지갑 주소입니다. tag 나 memo 값이 필요한 경우는 ':' 로 구분하여 하나의 문자열로 만듭니다. 검증 방법은 [지갑 주소 검증하기](/code-api-docs-ko/api-reference/ivms101/verify-wallet.md) 페이지를 참조해 주세요.&#x20;
    * Beneficiary(Required): 자산을 수신 받는 수취인(개인) 또는 법인 및 대표자에 대한 정보를 기입합니다. 요청(request)을 보낼 때, Beneficiary 정보를 함께 기입하여 보내야 하며, ①이름과 ②지갑 주소로 구성되어 있습니다. 지갑 주소 정보는 필수입니다. 이름 정보는 tradePrice가 트래블룰 적용 기준을 초과할 경우, 필수로 설정해야하고, 초과하지 않을 경우 옵션입니다. \
      \
      ※ 이름 정보는 'isExceedingThreshold'가 true일 때 Required, 'isExceedingThreshold'가 false일 때 Optional입니다.<br>
      * beneficiaryPersons(Required): Beneficiary 상위 객체에는 반드시 beneficiaryPersons라는 하위 객체가 포함되어야 합니다. beneficiaryPersons는 originatorPersons와 구조가 동일합니다. beneficiaryPersons 하위에는 naturalPerson 또는 legalPerson로 나눌 수 있습니다. 수취 VASP는 입력한 이름과 실제 수취인의 이름을 비교했을 때, 이름이 다를 경우 거절(denied) 응답을 보냅니다.
        * naturalPerson(Required or Optional): 개인에 대한 정보를 설정하기 위한 객체이며, 'isExceedingThreshold'가 true일 경우 Required, 'isExceedingThreshold'가 false일 경우 Optional입니다.
        * legalPerson(Required or Optional): 법인에 대한 정보를 설정하기 위한 객체이며, 'isExceedingThreshold'가 true일 경우 Required, 'isExceedingThreshold'가 false일 경우 Optional입니다.
      * accountNumber(Required): 자산을 수신하는 지갑 주소입니다. tag 나 memo 값이 필요한 경우는 ':' 로 구분해서 하나의 문자열로 만듭니다. 검증 방법은 [지갑 주소 검증하기](/code-api-docs-ko/api-reference/ivms101/verify-wallet.md) 페이지를 참조해 주세요.&#x20;
    * OriginatingVASP(Required): 자산을 전송하려는 송신 VASP 정보입니다.

      * originatingVASP(Required):

        * legalPerson(Required): 자산을 전송하려는 VASP의 법인 정보입니다.
          * name(Required):
            * nameIdentifier: 국제 표기법을 따르는 영문 이름 정보입니다.
              * legalPersonName: 영문 법인명입니다.
              * legalPersonNameIdentifierType: 'LEGL'(legal) 로 고정됩니다.

        :exclamation:'geographicAddress'와 'nationalIdentification' **둘 중 하나는 반드시 입력**해야 하며, 빈 값으로 입력할 수 없습니다.&#x20;

        * geographicAddress(Optional): 법인의 등록 서류상 소재지입니다.&#x20;
          * addressType: 'GEOG' 로 입력합니다.
          * townName: 시/도 이름을 입력합니다.
          * addressLine: townName 하위 주소를 문자열의 배열 형식으로 입력합니다.
          * country: ISO-3166-1 alpha-2 에서 정하는 2글자 국가 코드입니다. 예) KR, JP, US 등
        * nationalIdentification(Optional): 국가의 인증을 받은 법인 식별 번호, 즉 사업자등록번호입니다.&#x20;
          * nationalIdentifier: 사업자등록번호
          * nationalIdentifierType: RAID(Registration authority identifier)
          * registrationAuthority: RA000657 (대한민국 국세청 RA 식별번호)
        * countryOfRegistration(Required): 등록 국가. ISO-3166-1 alpha-2 에서 정하는 2글자 국가 코드입니다. 예) KR, JP, US 등

```
{
  "transferId": "string",
  "currency": "string",
  "amount": "string",
  "historicalCost": "",
  "tradePrice": "1000001",
  "tradeCurrency": "KRW",
  "isExceedingThreshold": "true",
  "originatingVasp": {
  },
  "payload": {
    "ivms101": {
      "Originator": {
        "originatorPersons": [
          // 개인일 경우 배열의 첫번째 요소에 naturalPerson 하나만 입력
          {
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "Barnes",
                    "secondaryIdentifier": "Robert",
                    "nameIdentifierType": "LEGL"
                  }
                ],
                "localNameIdentifier": [
                  {
                    "primaryIdentifier": "로버트 반스",
                    "secondaryIdentifier": "",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              },
              "customerIdentification": "customernumber in Max 50 Text"
            }
          },
          // 또는 법인일 경우 배열의 첫번째 요소에 legalPerson 을 입력하고,
          // 두번째 요소부터 naturalPerson(대표자) 정보를 입력
          {  // 법인 정보
            "legalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "legalPersonName": "(주)코인원",
                    "legalPersonNameIdentifierType": "LEGL"
                  }
                ]
              },
              "customerIdentification": "customernumber in Max 50 Text"
            }
          },
          {  // 법인의 대표자
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "Barnes",
                    "secondaryIdentifier": "Robert",
                    "nameIdentifierType": "LEGL"
                  }
                ],
                "localNameIdentifier": [
                  {
                    "primaryIdentifier": "로버트 반스",
                    "secondaryIdentifier": "",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              }
            }
          }
        ],
        "accountNumber": ["rJChk8e71gxVhyJSr1srzZxWhVisWMMYKZ:tag or memo"]  // 전송 고객의 지갑 주소:Tag or memo
      },
      "Beneficiary" : {
        "beneficiaryPersons": [
          {
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "Smith",
                    "secondaryIdentifier": "Alice",
                    "nameIdentifierType": "LEGL"
                  }
                ],
                "localNameIdentifier": [
                  {
                    "primaryIdentifier": "앨리스 스미스",
                    "secondaryIdentifier": "",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              }
            }
          }
        ],
        "accountNumber": ["rHcFoo6a9qT5NHiVn1THQRhsEGcxtYCV4d:tag or memo"] // 수신 고객의 지갑 주소:Tag or memo
      },
      "OriginatingVASP": {
        "originatingVASP": {
          "legalPerson": {
            "name": {
              "nameIdentifier": [
                {
                  "legalPersonName": "Korbit Inc.",
                  "legalPersonNameIdentifierType": "LEGL"
                }
              ]
            },
            "geographicAddress": [
              {
                "addressType":"GEOG",
                "townName":"Seoul",
                "addressLine": ["100 Teheran-ro 1-gil, Gangnam-gu", "10th floor"],
                "country":"KR"
              }
            ],
            "nationalIdentification": {
              "nationalIdentifier": "사업자번호",
              "nationalIdentifierType": "RAID",
              "registrationAuthority": "RA000657"
            },
            "countryOfRegistration": "KR"
          }
        }
      }
    }
  }
}           
```

**Response**

*200 OK*

*RESPONSE SCHEMA: application/json*

* result(Required): 가상 자산의 이전 허가 결과입니다.
  * verified: 허가된 경우 반환합니다.
  * denied: 자산 이전을 거부한 경우 반환합니다. reasonType값으로 상세 내용을 구분합니다.
* reasonType(Optional): denied 인 경우, 이유에 해당하는 필드를 추가합니다.
  * NOT\_FOUND\_ADDRESS: 가상 자산 주소를 찾을 수 없는 경우입니다.
  * NOT\_SUPPORTED\_SYMBOL: 거래할 수 없는 화폐 심볼입니다.
  * NOT\_KYC\_USER: 수신 주소의 소유자가 KYC 인증을 진행하지 않은 경우입니다.
  * INPUT\_NAME\_MISMATCHED: 요청 메시지로 전송한 수취인 이름이 실제 소유자의 이름과 일치하지 않는 경우입니다.
  * SANCTION\_LIST: 가상 자산 주소 또는 소유자가 수취 VASP 의 제재 대상입니다.
  * LACK\_OF\_INFORMATION: 자산 이전을 결정하는데 필요한 정보가 없을 경우입니다.
  * UNKNOWN: 그 밖에 다른 이유입니다.
* reasonMsg(Optional): reasonType 을 추가 설명하는 메시지.
* transferId(Required): '자산 이전 허가 요청' 부터는 이후 프로세스가 실행되는 중에 트랜잭션 상태를 추적할 고유 아이디가 필요합니다. 요청을 보내는 클라이언트에서 UUID 를 생성해서 보냅니다.
* beneficiaryVasp(Optional, 대소문자 주의): VerifyVASP 로부터 들어오는 응답에 한하여 암호화 영역인 payload 외부에 beneficiaryVASP 객체가 포함되어 있습니다. 만약, payload 밖에 beneficiaryVASP 객체가 있으면 payload 영역의 ivms101 에 덮어 써주세요.
* payload(Required): IVMS101 메시지를 담기 위한 객체입니다.
  * ivms101(Required): IVMS101 메시지 표준을 따르는 송금인(Originator), 수취인(Beneficiary), 송신 VASP(OriginatorVASP), 수취VASP(BeneficiaryVASP) 등 가상자산 이전에 관여하는 각 주체를 IVMS101 국제 표준에 맞춰서 정의한 객체입니다.  응답(response) 객체에서는 Originator, OriginatingVASP 정보는 요청(request)의 데이터를 복사하고, Beneficiary, BeneficiaryVASP 데이터는 수신 VASP 의 정보를 기준으로 작성합니다.
    * Originator(Required): 자산을 전송하고자 하는 송금인(개인) 또는 법인 및 대표자에 대한 정보로 요청의 값을 그대로 복사해서 사용합니다.
    * Beneficiary(Required): 자산을 수신하는 수취인(개인) 또는 법인 및 대표자에 대한 정보입니다. 응답(response) 에서는 이름과 자산 주소를 기입해서 보내야 합니다.
      * beneficiaryPersons(Required): 상위 객체인 Beneficiary 객체에 반드시 포함되어야 하며 구조는 originatorPersons 와 같으므로 요청의 originatorPersons 설명을 참고해 주세요.
        * naturalPerson(Required): 개인에 대한 정보를 설정하기 위한 객체로 name(이름) 정보를 필수로 설정해야 한다.
        * legalPerson(Optional): 법인에 대한 정보를 설정하기 위한 객체로 name(이름) 정보를 필수로 설정해야 한다.
      * accountNumber(Required): 자산을 수신하는 지갑 주소입니다. tag 나 memo 값이 필요한 경우는 ':' 로 구분해서 하나의 문자열로 만듭니다.
    * OriginatingVASP(Required): 자산을 전송하려는 송신 VASP 정보입니다. 요청의 값을 그대로 복사해서 사용합니다.
    * BeneficiaryVASP(Required): 자산을 수신하는 수취 VASP 정보입니다. 구조는 OriginatingVASP 와 같으므로 요청의 OriginatingVASP 설명을 참고해 주세요.

```
{
  "result": "string",
  "reasonType": "",
  "reasonMsg": "",
  "transferId": "string",
  "beneficiaryVasp": {
  },
  "payload": {
    "ivms101": {
      "Originator": {
        "originatorPersons": [
          // 개인일 경우 배열의 첫번째 요소에 naturalPerson 하나만 입력
          {
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "로버트 반스",
                    "secondaryIdentifier": "",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              },
              "customerIdentification": "customernumber in Max 50 Text"
            }
          },
          // 법인일 경우 배열의 첫번째 요소에 legalPerson 을 입력하고,
          // 두번째 요소부터 naturalPerson(대표자) 정보를 입력
          {  // 법인 정보
            "legalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "legalPersonName": "(주)코인원",
                    "legalPersonNameIdentifierType": "LEGL"
                  }
                ]
              },
              "customerIdentification": "customernumber in Max 50 Text"
            }
          },
          {  // 법인의 대표자
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "로버트 반스",
                    "secondaryIdentifier": "",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              }
            }
          }
        ],
        "accountNumber": ["rJChk8e71gxVhyJSr1srzZxWhVisWMMYKZ:tag or memo"]  // 송금 고객의 지갑 주소:Tag or memo
      },
      "Beneficiary": {
        "beneficiaryPersons": [
          // 개인일 경우 배열의 첫번째 요소에 naturalPerson 하나만 입력
          {
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "로버트 반스",
                    "secondaryIdentifier": "",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              },
              "customerIdentification": "customernumber in Max 50 Text"
            }
          },
          // 법인일 경우 배열의 첫번째 요소에 legalPerson 을 입력하고,
          // 두번째 요소부터 naturalPerson(대표자) 정보를 입력
          {  // 법인 정보
            "legalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "legalPersonName": "(주)코인원",
                    "legalPersonNameIdentifierType": "LEGL"
                  }
                ]
              },
              "customerIdentification": "customernumber in Max 50 Text"
            }
          },
          {  // 법인의 대표자
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "로버트 반스",
                    "secondaryIdentifier": "",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              }
            }
          }
        ],
        "accountNumber": [ "rHcFoo6a9qT5NHiVn1THQRhsEGcxtYCV4d:tag or memo" ] // 수신 고객의 지갑 주소:Tag or memo
      },
      "OriginatingVASP": {
        "originatingVASP": {
          "legalPerson": {
            "name": {
              "nameIdentifier": [
                {
                  "legalPersonName": "Korbit Inc.",
                  "legalPersonNameIdentifierType": "LEGL"
                }
              ]
            },
            "geographicAddress": [
              {
                "addressType":"GEOG",
                "townName":"Seoul",
                "addressLine": ["100 Teheran-ro 1-gil, Gangnam-gu", "10th floor"],
                "country":"KR"
              }
            ],
            "nationalIdentification": {
              "nationalIdentifier": "사업자번호",
              "nationalIdentifierType": "RAID",
              "registrationAuthority": "RA000657"
            },
            "countryOfRegistration": "KR"
          }
        }
      },
      "BeneficiaryVASP": {
        "beneficiaryVASP": {
          "legalPerson": {
            "name": {
              "nameIdentifier": [
                {
                  "legalPersonName": "Coinone Inc.",
                  "legalPersonNameIdentifierType": "LEGL"
                }
              ]
            },
            "geographicAddress": [
              {
                "addressType":"GEOG",
                "townName":"Seoul",
                "addressLine": ["100 Teheran-ro 1-gil, Gangnam-gu", "10th floor"],
                "country":"KR"
              }
            ],
            "nationalIdentification": {
              "nationalIdentifier": "6948624434",
              "nationalIdentifierType": "RAID",
              "registrationAuthority": "RA000657"
            },
            "countryOfRegistration": "KR"
          }
        }
      }
    }
  }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://codevasp.gitbook.io/code-api-docs-ko/vasp-api/ben-transfer-authorization.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
