> For the complete documentation index, see [llms.txt](https://docs-kr.verifyvasp.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-kr.verifyvasp.com/reference/vasp-api-reference/callback-api.md).

# Callback API

## Callback 호출

<mark style="color:green;">`POST`</mark> `VEGA_VERIFICATION_CALLBACK_API_PATH`

{% hint style="info" %}
**Enclave 환경 변수:**

<mark style="color:green;">**`VEGA_VERIFICATION_CALLBACK_API_PATH`**</mark> 변수에, Callback API 엔드포인트를 명시합니다.
{% endhint %}

> 비동기 방식으로 API가 호출되었을 경우 사용되는 Callback API입니다.
>
> 비동기로 동작하는 API는 아래와 같습니다.
>
> * 사용자 검증 요청 API
> * 트랜잭션 결과 리포트 API
> * 에러 상황 리포트 API
> * Chainalysis KYT 기능을 이용한 위험 평가 API
> * Refinitiv WCO 기능을 이용한 위험 평가 API
>
> Callback API는 enclave에서 호출하는 API로 다음과 같은 경우에 호출됩니다.
>
> * 이전에 요청했던 Verification에 대한 결괏값이 도착했을 경우 (callbackType : VERIFICATION\_RESULT)
> * 송신 VASP에서 특정 verification에 대응되는 txHash 또는 txID의 report가 도착했을 경우 (callbackType : TX\_REPORT)
> * 송신 VASP에서 특정 verification에 대응되는 에러 상황 report가 도착했을 경우 (callbackType : ERROR\_REPORT)
> * 기존에 등록한 Chainalysis KYT 요청에 대한 위험 평가 결과를 수신했을 경우 (callbackType : CHAINALYSIS\_KYT\_RESULT)
> * 기존에 등록한 Refinitiv WCO 요청에 대한 위험 평가 결과를 수신했을 경우 (callbackType : REFINITIV\_WCO\_RESULT)

{% hint style="info" %}
**응답 시간:**

이 Callback API는 1초 이내에 응답해야 합니다.
{% endhint %}

{% hint style="info" %}
**구현 정책**

* **200 OK** 코드 외 다른 HTTP response status code를 반환해서는 안 됩니다.
* API에 대한 response는 가급적 빨리 반환해야 합니다. 따라서 callback API 내부적으로 시간이 오래 걸리는 작업은 비동기로 처리해야 합니다.
* 멱등성(Idempotent)을 보장하도록 구현해야 합니다.
  * enclave 내부에서 데이터 유실을 방지하기 위해 똑같은 데이터로 callback이 여러 번 호출될 수 있습니다.
  * 따라서 중복 데이터를 수신했을 경우에도 이전과 똑같은 동작을 하도록 보장해야 합니다. (예를 들어 이미 받은 데이터는 무시한다든지)
    {% endhint %}

## Request Header

| Field Name    | Description                                        |
| ------------- | -------------------------------------------------- |
| Authorization | Bearer \<VEGA\_VERIFICATION\_AUTHORIZATION\_TOKEN> |

## Request Body

<details>

<summary>Request Body Example for VERIFICATION_RESULT callbackType</summary>

```json
{
  "callbackType": "VERIFICATION_RESULT",
  "data": {
    "verificationUuid": "64ab871b-14a3-47df-9b80-368e29fe8180",
    "verificationResult": "DENIED",
    "reason": "UNKNOWN-ADDRESS",
    "ivms101": {
      "originator": {
        "originatorPersons": [
          {
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "James",
                    "secondaryIdentifier": "Din",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              }
            }
          }
        ],
        "accountNumber": ["0x5811001506550d8356a215be229c15b6ef371a9a"]
      },
      "beneficiary": {
        "beneficiaryPersons": [
          {
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "Taylor",
                    "secondaryIdentifier": "Robbins",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              }
            }
          }
        ],
        "accountNumber": ["0xb0bFf9721871e22653358956cf59a5FdBF3D752F"]
      },
      "originatingVASP": {
        "legalPerson": {
          "name": {
            "nameIdentifier": [
              {
                "legalPersonName": "Lambda256",
                "legalPersonNameIdentifierType": "LEGL"
              }
            ]
          },
          "geographicAddress": [
            {
              "addressType": "GEOG",
              "postcode": "123123c",
              "townName": "Yeoksam-dong",
              "addressLine": ["sechogu street", "100-100"],
              "country": "KR"
            }
          ],
          "nationalIdentification": {
            "nationalIdentifier": "KR0001",
            "nationalIdentifierType": "RAID",
            "registrationAuthority": "RA000151"
          }
        }
      },
      "beneficiaryVASP": {
        "legalPerson": {
          "name": {
            "nameIdentifier": [
              {
                "legalPersonName": "Lambda256 Exchange",
                "legalPersonNameIdentifierType": "LEGL"
              }
            ]
          },
          "geographicAddress": [
            {
              "addressType": "GEOG",
              "postcode": "234234",
              "townName": "Yeoksam-dong",
              "addressLine": ["sechogu street", "100-100"],
              "country": "KR"
            }
          ],
          "nationalIdentification": {
            "nationalIdentifier": "123456",
            "nationalIdentifierType": "RAID",
            "registrationAuthority": "RA000677"
          }
        }
      }
    }
  }
}
```

</details>

<details>

<summary>Request Body Example for TX_REPORT callbackType</summary>

```json
{
  "callbackType": "TX_REPORT",
  "data": {
    "verificationUuid": "64ab871b-14a3-47df-9b80-368e29fe8181",
    "txHash": "8a54d58ca4100112a5430818776d74898f2232770bae03046862575cb851a042",
    "vout": "2"
  }
}
```

</details>

<details>

<summary>Request Body Example for ERROR_REPORT callbackType</summary>

```json
{
  "callbackType": "ERROR_REPORT",
  "data": {
    "verificationUuid": "64ab871b-14a3-47df-9b80-368e29fe8182",
    "result": "ERROR",
    "reason": "BLACKLISTED",
    "message": "Originator is included in the blacklist."
  }
}
```

</details>

<details>

<summary>Request Body Example for CHAINALYSIS_KYT_RESULT callbackType</summary>

```json
{
  "callbackType": "CHAINALYSIS_KYT_RESULT",
  "data": {
    "verificationUuid": "69a310e6-810f-4a31-83d1-bcdafccf5304",
    "riskAssessment": {
      "chainalysisKYT": {
        "counterpartyVaspId": "15952089931162059995",
        "apiType": "ATTEMPT",
        "userId": "15952089931162059995",
        "direction": "OUTGOING",
        "network": "ETHEREUM",
        "asset": "ETH",
        "amount": "1",
        "usdPrice": "1820.17",
        "outputAddress": "bb3fd383d1c5540e52ef0a7bcb9433375793aeaf",
        "timestamp": "2023-05-18T12:39:44.000Z",
        "externalId": "79382ac9-c7be-3fab-ad56-8c61c654e2fc",
        "status": "PROCESSED",
        "alertCount": 1,
        "createdAt": "2023-05-18T12:39:46.000Z",
        "assessedAt": "2023-05-18T12:39:45.263Z"
      },
      "chainalysisKYTAlerts": [
        {
          "counterpartyVaspId": "15952089931162059995",
          "externalId": "79382ac9-c7be-3fab-ad56-8c61c654e2fc",
          "direction": "OUTGOING",
          "alertId": "118b8cc8-f579-11ed-b86d-a3210c6ca9b8",
          "alertLevel": "MEDIUM",
          "entityCategory": "high risk exchange",
          "serviceName": "HIGH RISK EXCHANGE: SimpleSwap.io bb3fd383d1c5540e52ef0a7bcb9433375793aeaf",
          "exposureType": "DIRECT",
          "alertAmount": "1820.17",
          "createdAt": "2023-05-18T12:39:52.461Z"
        }
      ]
    }
  }
}
```

</details>

<details>

<summary>Request Body Example for REFINITIV_WCO_RESULT callbackType</summary>

```json
{
  "callbackType": "REFINITIV_WCO_RESULT",
  "data": {
    "verificationUuid": "69a310e6-810f-4a31-83d1-bcdafccf5304",
    "riskAssessment": {
      "refinitivWorldCheckOne": {
        "counterpartyVaspId": "15952089931162058999",
        "direction": "INCOMING",
        "caseSystemId": "5jb7r2c9xjfk1hoc95gfayv6m",
        "status": "PROCESSED",
        "matchStrength": "EXACT",
        "aggregatedSummaryResult": "{\"caseId\":\"69a310e6-810f-4a31-83d1-bcdafccf5304-INCOMING-1684413585757\", ... }}}",
        "createdAt": "2023-05-18T12:39:48.000Z",
        "assessedAt": "2023-05-18T12:39:57.834Z"
      }
    }
  }
}
```

</details>

| Field Name   | Data Type | Required | Description                                      |
| ------------ | --------- | -------- | ------------------------------------------------ |
| callbackType | string    | true     | Callback API가 어떠한 상황에서 호출되었는지 구분하는 값. (하단 설명 참조) |
| data         | object    | true     | callbackType에 따라 data 형식이 달라집니다. (위 샘플 코드 참조)    |

* <mark style="color:blue;">**callbackType**</mark> 필드에는 다음과 같은 값이 들어갈 수 있습니다.
  * **VERIFICATION\_RESULT**: 이전에 호출했던 사용자 검증 요청 API에 대한 결과가 도착했을 때 호출
  * **TX\_REPORT**: Originating VASP로부터 트랜잭션 결과 리포트가 도착했을 때 호출
  * **ERROR\_REPORT**: Beneficiary VASP로부터 에러 상황 리포트가 도착했을 때 호출
  * **CHAINALYSIS\_KYT\_RESULT**: Chainalysis KYT API를 이용하여 위험 평가를 수행한 결과가 도출되었을 때 호출
  * **REFINITIV\_WCO\_RESULT**: Refinitiv WCO API를 이용하여 위험 평가를 수행한 결과가 도출되었을 때 호출
* <mark style="color:blue;">**data.reason**</mark> 필드에는 다음과 같은 값이 들어갈 수 있습니다.

  * 단, 사용자 검증 요청에 대한 결과 `data.result` 필드의 값이 **DENIED** 일 때에만 유효합니다.
  * `data.message` 필드에는, 아래 표에 설명된 값을 string으로 전달합니다. (하단 표 내용 참조)

  | <p>reason</p><p>(string)</p>      | <p>result</p><p>(string)</p> | <p>message</p><p>(string)</p>    | Description                                                       |
  | --------------------------------- | ---------------------------- | -------------------------------- | ----------------------------------------------------------------- |
  | `UNKNOWN-SYMBOL`                  | DENIED                       | 해당 VASP에서 취급하지 않는다고 판명된 심벌 이름    | 거래소에서 취급하지 않는 심벌일 경우 (VASP가 취급하지 않는 자산일 경우)                       |
  | `UNKNOWN-NETWORK`                 | DENIED                       | 해당 VASP에서 취급하지 않는다고 판명된 네트워크 이름  | 거래소에서 취급하지 않는 네트워크이거나 네트워크 정보가 불충분한 경우 (심벌은 동일하지만 네트워크가 맞지 않는 경우) |
  | `UNKNOWN-ADDRESS`                 | DENIED                       | 해당 VASP 소유의 주소가 아니라고 판명된 지갑 주소   | 가상 자산 주소가 해당 VASP의 주소가 아닌 경우                                      |
  | `LACK-OF-INFORMATION`             | DENIED                       | 부족한 개인 정보 필드 코드 목록 (쉼표로 구분)      | 상대방 사용자의 정보가 검증을 수행하기에 부족한 경우                                     |
  | `UNAVAILABLE-INFORMATION`         | DENIED                       | 넘겨줄 수 없는 개인 정보 필드 코드 목록 (쉼표로 구분) | 상대방이 요청한 개인 정보가 없거나 넘겨줄 수 없는 경우                                   |
  | `BLACKLISTED`                     | DENIED                       | -                                | 상대방 사용자에 대한 sanction screening 결과 문제가 있는 경우                       |
  | `UNVERIFIED-KYC`                  | DENIED                       | -                                | 자신의 VASP의 해당 사용자가 KYC 인증이 되어 있지 않은 경우                             |
  | `MISMATCHED-NAME`                 | DENIED                       | -                                | 수신자의 이름이 송신 VASP에서 보내준 이름과 일치하지 않는 경우                             |
  | `NOT-ALLOWED`                     | DENIED                       | 거부 사유                            | 수신 VASP에서 어떤 이유로든 해당 사용자 검증 요청을 거부한 경우                            |
  | `UNDEFINED-ERROR`                 | DENIED                       | 에러 내용                            | 그 밖에, 따로 정의되어 있지 않은 에러가 발생한 경우                                    |
  | `BENEFICIARY-ACCOUNT-NOT-MATCHED` | ERROR                        | -                                | 송신자가 입력한 수신자 주소가 수신 VASP에 의해 변경되어 반환되었을 경우                        |
  | `REQUEST-TIMEOUT`                 | ERROR                        | -                                | 수신 VASP에서 사용자 검증 VASP API 호출이 시간 초과로 실패했을 경우                      |

## Response Body

<details>

<summary>Response Body Examples</summary>

* 200 OK

```json
{
  "result": true
}
```

</details>

| Field Name | Data Type | Required | Example | Description           |
| ---------- | --------- | -------- | ------- | --------------------- |
| result     | boolean   | true     | true    | 결괏값. (에러는 반환하지 않습니다.) |
