> ## 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.

# postKycVerification

> Post a decisioned KYC verification result performed by an external identity provider.

Post a decisioned KYC verification result performed by an external identity provider. Fluz validates the payload, creates a verification record and updates the user's KYC status according to the result. Requires the `VERIFY_KYC` scope on a user access token.

Use this mutation when your platform runs its own KYC and pushes the outcome to Fluz. To have Fluz run the verification instead, use [verifyUserInformation](/api-reference/mutations/verify-user-information) or [verifyUserPrefillInformation](/api-reference/mutations/verify-user-prefill-information).

<Warning>
  Send this mutation to the dedicated secure ingestion endpoint, not the standard API host. This endpoint securely tokenizes PII data like `person.ssn` in transit. A request whose SSN or other PII data arrive untokenized is rejected. Always pass the arguments as GraphQL **variables**; values inlined into the query document cannot be tokenized.

  * Staging: `https://secure.transactional-graph.staging.fluzapp.com/api/v1/graphql`
  * Production: provided during onboarding
</Warning>

```graphql theme={null}
mutation {
  postKycVerification(
    schemaVersion: String!
    externalVerificationProvider: String!
    externalVerificationId: String!
    decision: String!
    decisionReason: String
    decisionedAt: String
    person: JSON!
    verifications: JSON!
    externalProviderData: JSON
  ): PostKycVerificationResponse
}
```

## Key behaviors

* **Idempotency.** Submissions are idempotent on `externalVerificationId`: replaying the same identifier returns the original result and writes nothing. A re-decisioned verification must be submitted with a new identifier.
* **Only decisioned results.** `decision` must be `PASSED` or `FAILED` — do not send pending or undecisioned verifications.
* **KYC status transitions.** A `PASSED` result moves an unverified user to `PASS` (US address) or `PASS_INTERNATIONAL` (non-US address). If the user is already verified, the verification is still recorded for audit but the status is never changed — the response message notes that the existing status was preserved. A `FAILED` result is recorded and leaves the status untouched.
* **Photo URLs** must be HTTPS and remain fetchable for at least 24 hours after delivery — Fluz fetches and stores the images.

## Arguments

<ParamField body="schemaVersion" type="String!" required>
  Version of the payload contract. Currently `"1.0"`.
</ParamField>

<ParamField body="externalVerificationProvider" type="String!" required>
  The external KYC provider that performed the verification: `IDOLOGY`,
  `OSCILAR`, `PERSONA`, or `CUSTOM` for a provider not listed.
</ParamField>

<ParamField body="externalVerificationId" type="String!" required>
  The provider's globally unique identifier for this verification attempt (for
  example a Persona inquiry id). This is the idempotency key — one attempt, one
  id, forever.
</ParamField>

<ParamField body="decision" type="String!" required>
  The final decision for this verification attempt: `PASSED` or `FAILED`.
</ParamField>

<ParamField body="decisionReason" type="String">
  Human-readable reason or rule that produced the decision; recorded on the KYC
  status log when present.
</ParamField>

<ParamField body="decisionedAt" type="String">
  When the decision was made, as an ISO 8601 / RFC 3339 timestamp (UTC
  preferred). Defaults to the time of ingestion.
</ParamField>

<ParamField body="person" type="JSON!" required>
  The verified identity, exactly as established by the provider (not raw user
  input).

  <Expandable title="person fields">
    <ParamField body="firstName" type="String" required>
      The person's first name.
    </ParamField>

    <ParamField body="middleName" type="String">
      The person's middle name.
    </ParamField>

    <ParamField body="lastName" type="String" required>
      The person's last name.
    </ParamField>

    <ParamField body="dateOfBirth" type="String" required>
      RFC 3339 full date (`YYYY-MM-DD`).
    </ParamField>

    <ParamField body="ssn" type="String">
      Full US SSN, digits with optional dashes. Required together with
      `ssnLast4` when `verifications.ssn` is present. Sent in clear over TLS to
      the secure endpoint, where it is tokenized in transit — it never reaches
      Fluz systems and is never stored in clear. Never send a masked or partial
      value.
    </ParamField>

    <ParamField body="ssnLast4" type="String">
      The last four digits of the SSN. Required whenever `ssn` is provided and
      must equal its last four digits — a mismatch is rejected.
    </ParamField>

    <ParamField body="phoneNumber" type="String">
      E.164 format.
    </ParamField>

    <ParamField body="emailAddress" type="String">
      The person's email address.
    </ParamField>

    <ParamField body="address" type="Object" required>
      The person's verified legal address: `streetLine1` (no PO Boxes), optional
      `streetLine2`, `city`, `subdivision` (state/province code, required for US
      addresses), `postalCode` (for US, the 5-digit ZIP), and `countryCode` (ISO
      3166-1 alpha-2 — drives `PASS` vs `PASS_INTERNATIONAL`).
    </ParamField>

    <ParamField body="personData" type="JSON">
      Open object for any additional provider data about the person.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="verifications" type="JSON!" required>
  The provider verifications backing the decision. At least one of `document`,
  `ssn`, or `database` is required. Presence is semantic: a `ssn` section means
  the SSN was validated against an authoritative source; `person.ssn` without a
  `ssn` section means the SSN was merely collected.

  <Expandable title="verifications fields">
    <ParamField body="document" type="Object">
      Government-id documentary verification: `verificationId` (provider id of
      this resource), `status` (`PASSED` or `FAILED`), `documentClass`
      (`DRIVER_LICENSE`, `PASSPORT`, or `GOVERNMENT_ID`), `documentNumber`
      (stored only as a hash), `issuingCountryCode` (ISO 3166-1 alpha-2),
      optional `issuingSubdivision`, `issueDate`, `expirationDate`,
      `confidenceScore` (0–100), `address` (as extracted from the document),
      `photos` (`front` required when present, optional `back` and `selfie` —
      HTTPS URLs), `checks`, and `documentData`.
    </ParamField>

    <ParamField body="ssn" type="Object">
      SSN verification against an authoritative source: `verificationId`,
      `status`, `source` (for example `TIN_DATABASE`, `ECBSV`, `CREDIT_BUREAU`),
      optional `checks` and `ssnData`. Requires `person.ssn` and
      `person.ssnLast4` to carry the verified value.
    </ParamField>

    <ParamField body="database" type="Object">
      Database verification of personal data against public or private sources,
      without a document or SSN: `verificationId`, `status`, optional `checks`
      and `databaseData`.
    </ParamField>

    <ParamField body="checks" type="Array">
      Each section accepts a `checks` array of individual check outcomes: `name`
      (a stable snake\_case identifier, consistent across submissions — for
      example `id_expired_detection`), `status` (`PASSED`, `FAILED`, or
      `NOT_APPLICABLE`), and optional `reasons`. Fluz records all outcomes for
      audit and records `FAILED` check names as decline codes.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="externalProviderData" type="JSON">
  Freeform provider context for audit (template ids, partner reference ids,
  original provider identifiers). Must not contain clear PII beyond what the
  typed fields already carry.
</ParamField>

## Returns

[`PostKycVerificationResponse`](/api-reference/types/post-kyc-verification-response) — Response type for the postKycVerification mutation.

## Example

```json Variables theme={null}
{
  "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"
    }
  }
}
```

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