> ## 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。参见[所需 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 | 是  | 载荷契约的版本。目前为 `"1.0"`。                                |
| `externalVerificationProvider` | String | 是  | `IDOLOGY`、`OSCILAR`、`PERSONA` 或 `CUSTOM`。           |
| `externalVerificationId`       | String | 是  | 您提供商针对本次尝试的全局唯一 id——幂等键。                            |
| `decision`                     | String | 是  | `PASSED` 或 `FAILED`。                                |
| `decisionReason`               | String | 否  | 产生该决策的人类可读原因或规则。                                    |
| `decisionedAt`                 | String | 否  | 决策的 ISO 8601 时间戳。默认为摄取时间。                           |
| `person`                       | JSON   | 是  | 由提供商确立的已验证身份。                                       |
| `verifications`                | JSON   | 是  | 支撑该决策的提供商验证——至少包含 `document`、`ssn` 或 `database` 之一。 |
| `externalProviderData`         | JSON   | 否  | 供审计使用的自由格式提供商上下文。                                   |

参见 [postKycVerification](/api-reference/mutations/post-kyc-verification) 获取完整字段参考，包括 `person` 与 `verifications` 对象结构。

## 示例

```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 会拉取并存储这些图像。
