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

# 由外部提供者進行驗證

> 從您自家的身分驗證供應商提交已決策的 KYC 結果，而不是由 Fluz 執行驗證。

如果您的平台已使用自有的身分驗證供應商進行 KYC，您的客戶不應該需要重複驗證。使用 `postKycVerification` 將已決策的結果提交給 Fluz。

與其他 API 方法不同，Fluz 在此不會對身分本身進行篩查 — 決策由您做出。Fluz 只會驗證並記錄該結果，並據此更新客戶狀態。

<Info>
  **先決條件**

  * 一個經 Fluz 核准、適用於您應用程式的外部 KYC 計畫。請在整合此方法前與您的客戶經理洽談。
  * Fluz 為您的應用程式啟用 `VERIFY_KYC` 權限範圍。參見 [所需 scope](/user-kyc-verification#required-scope)。
  * 為待驗證客戶所產生的 [使用者存取權杖](/recipes/generate-user-access-token)，且其 scopes 中包含 `VERIFY_KYC`。
  * 為您的環境專屬配置的安全擷取端點，將於導入時提供。
</Info>

<Warning>
  將此 mutation 送至「專屬安全擷取端點」，而非標準 API 主機。此端點會在傳輸過程中安全地權杖化像是 `person.ssn` 的個人敏感資訊（PII）。任何以未權杖化方式傳送 SSN 或其他 PII 的請求都會被拒絕。請一律以 GraphQL「變數」傳遞參數；若將值內嵌於查詢文件中，將無法進行權杖化。

  * Staging: `https://secure.transactional-graph.staging.fluzapp.com/api/v1/graphql`
  * Production: 導入時提供
</Warning>

## 運作方式

`postKycVerification` mutation 為同步操作。您提交已驗證的身分與支撐您決策的供應商驗證資料，Fluz 會在回應本文中回傳 `APPROVED`、`DECLINED`、`DUPLICATE` 或 `ERROR`。不會有面向客戶的互動步驟。

此方法有幾個特定行為：

* **僅接受已決策結果。** `decision` 必須為 `PASSED` 或 `FAILED`。請勿送出待處理或未決策的驗證。
* **冪等性。** 以 `externalVerificationId`（您供應商對此嘗試的全域唯一 ID）做為冪等鍵。重送相同 id 會回傳原始結果且不會寫入；若同一驗證重新決策，必須以新的 id 提交。
* **狀態轉換。** `PASSED` 會將未驗證的客戶變更為已驗證。若客戶已經被驗證 — 不論透過任何方法 — 此次驗證仍會為稽核而記錄，但其狀態不會被變更；回應訊息會註明已保留既有狀態。`FAILED` 會被記錄，且不變更狀態。
* **無嘗試次數限制。** 因為您是在回報結果而非請求篩查，此方法沒有嘗試次數限制，且不計入 [透過 SSN 驗證](/verify-customers-by-ssn) 或 [KYC 自動填寫](/verify-customers-by-autofill) 的限制中。

## 請求

客戶由 `Authorization` 標頭中的使用者存取權杖識別 — `person.userId` 與您的應用程式身分會由 Fluz 自動填入，且任何您提交的值都會被覆蓋。

| 欄位                             | 類型     | 必填  | 說明                                                      |
| :----------------------------- | :----- | :-- | :------------------------------------------------------ |
| `schemaVersion`                | String | Yes | 載荷契約的版本。目前為 `"1.0"`。                                    |
| `externalVerificationProvider` | String | Yes | `IDOLOGY`、`OSCILAR`、`PERSONA`，或 `CUSTOM`。               |
| `externalVerificationId`       | String | Yes | 您供應商對此次嘗試的全域唯一 ID — 冪等鍵。                                |
| `decision`                     | String | Yes | `PASSED` 或 `FAILED`。                                    |
| `decisionReason`               | String | No  | 產生該決策的人類可讀原因或規則。                                        |
| `decisionedAt`                 | String | No  | 決策的 ISO 8601 時戳。預設為擷取時間。                                |
| `person`                       | JSON   | Yes | 由供應商所確認之已驗證身分。                                          |
| `verifications`                | JSON   | Yes | 支撐該決策的供應商驗證 — 至少需包含 `document`、`ssn`，或 `database` 其中之一。 |
| `externalProviderData`         | JSON   | No  | 供稽核用的自由格式供應商情境資料。                                       |

完整欄位參考，包括 `person` 與 `verifications` 物件結構，請見 [postKycVerification](/api-reference/mutations/post-kyc-verification)。

## 範例

```javascript theme={null}
import { GraphQLClient, gql } from 'graphql-request';

const API_URL =
  'https://secure.transactional-graph.staging.fluzapp.com/api/v1/graphql';

const POST_KYC_VERIFICATION = gql`
  mutation postKycVerification(
    $schemaVersion: String!
    $externalVerificationProvider: String!
    $externalVerificationId: String!
    $decision: String!
    $decisionReason: String
    $person: JSON!
    $verifications: JSON!
  ) {
    postKycVerification(
      schemaVersion: $schemaVersion
      externalVerificationProvider: $externalVerificationProvider
      externalVerificationId: $externalVerificationId
      decision: $decision
      decisionReason: $decisionReason
      person: $person
      verifications: $verifications
    ) {
      status
      verificationId
      message
    }
  }
`;

const client = new GraphQLClient(API_URL, {
  headers: {
    Authorization: `Bearer <<USER_ACCESS_TOKEN>>`,
    'Content-Type': 'application/json',
  },
});

const response = await client.request(POST_KYC_VERIFICATION, {
  schemaVersion: '1.0',
  externalVerificationProvider: 'PERSONA',
  externalVerificationId: 'inq_gCf28LrXY9wrxDoZbnbEqTrn',
  decision: 'PASSED',
  decisionReason: 'All checks passed',
  person: {
    firstName: 'JANE Q',
    lastName: 'SAMPLE',
    dateOfBirth: '1990-01-01',
    ssn: '900-98-7654',
    ssnLast4: '7654',
    address: {
      streetLine1: '123 EXAMPLE STREET',
      city: 'SAMPLETOWN',
      subdivision: 'CA',
      postalCode: '90001',
      countryCode: 'US',
    },
  },
  verifications: {
    document: {
      verificationId: 'ver_p8qmKydzkQwyLKbaAJRvADi9',
      status: 'PASSED',
      documentClass: 'DRIVER_LICENSE',
      documentNumber: 'X90000000000001',
      issuingCountryCode: 'US',
      photos: { front: 'https://files.provider.example/front.jpg' },
      checks: [{ name: 'id_expired_detection', status: 'PASSED' }],
    },
    ssn: {
      verificationId: 'ver_tin_snW2CvL2x4kTdZjbmGBjqcuV',
      status: 'PASSED',
      source: 'TIN_DATABASE',
    },
  },
});

console.log(response);
```

```json Response theme={null}
{
  "data": {
    "postKycVerification": {
      "status": "APPROVED",
      "verificationId": "4b99e8c5-4201-45fd-a5dc-1c3b88e4f6c7",
      "message": "User verification successful"
    }
  }
}
```

## 處理回應

| 狀態          | 處理方式                                                                          |
| :---------- | :---------------------------------------------------------------------------- |
| `APPROVED`  | 驗證已被記錄。若客戶先前未完成驗證，現在即為已驗證；若其原本已驗證，`message` 會註明已保留既有狀態。                       |
| `DECLINED`  | `FAILED` 結果已被記錄。客戶狀態維持不變。                                                     |
| `DUPLICATE` | 驗證已被記錄，但 SSN 或證件號碼與另一位 Fluz 客戶相符。該帳戶將被標記為待審查 — 請您方先行調查後再繼續。Fluz 不會揭露相符的是哪位客戶。 |
| `ERROR`     | 請檢視 `message` — 最常見為未通過契約驗證的載荷。未記錄任何資料。                                       |

## 測試

使用虛構身分在 staging 端點進行測試 — 因為決策由您做出，無需如其他方法般遵循供應商的測試身分要求。請使用 `900-XX-XXXX` 範圍（從未發放）的 SSN、每次嘗試產生新的 `externalVerificationId`（重放同一 id 會回傳原結果，無法測試新情境），並確保您提供的任何照片 URL 可被存取 — Fluz 會擷取並儲存影像。
