Skip to main content
The kyc.status.changed event is sent whenever a user’s KYC verification status changes.

Event Type

kyc.status.changed

Successful KYC Payload

When KYC verification completes successfully, failure_code and failure_reason are null. Headers:
Content-Type: application/json
X-Timestamp: 1767711032
X-Signature: 9af85e3e50d7a5c0ee1dcc519a063f272efc8a0d4c75a55eb0c1296e45e13564
Body:
{
  "api_version": "v1",
  "event_id": "c386021a-d7a5-4aff-8924-4374f1d1d84f",
  "event_category": "kyc",
  "event_type": "kyc.status.changed",
  "user_id": "ecf74983-b52f-40ff-8a4c-aa052748459b",
  "event_object_id": "ecf74983-b52f-40ff-8a4c-aa052748459b",
  "event_object_status": "completed",
  "event_object_changes": {
    "status": ["pending", "completed"]
  },
  "event_object": {
    "status": "completed",
    "failure_code": null,
    "failure_reason": null
  },
  "occurred_at": "2026-01-06T14:50:31.481Z"
}

Failed KYC Payload

When KYC is denied, event_object includes the failure_code and failure_reason from Veriff (our KYC provider):
{
  "api_version": "v1",
  "event_id": "d486021a-e8b6-5bff-9035-5485f2e2e95g",
  "event_category": "kyc",
  "event_type": "kyc.status.changed",
  "user_id": "ecf74983-b52f-40ff-8a4c-aa052748459b",
  "event_object_id": "ecf74983-b52f-40ff-8a4c-aa052748459b",
  "event_object_status": "failed",
  "event_object_changes": {
    "status": ["pending", "failed"]
  },
  "event_object": {
    "status": "failed",
    "failure_code": "103",
    "failure_reason": "Person showing the document does not appear to match document photo"
  },
  "occurred_at": "2026-01-06T15:02:47.000Z"
}

KYC Status Values

The event_object.status field reflects the current verification state:
StatusDescription
pendingKYC verification is in progress
completedKYC verification was successful
failedKYC verification was denied
kyc_checkedKYC verified, awaiting manual review
edd_requiredEnhanced Due Diligence is required
edd_submittedEnhanced Due Diligence has been submitted

Failure Codes Reference

When status is failed, the failure_code and failure_reason fields provide details from Veriff. Use these to understand why verification was denied and to provide appropriate guidance to the user.
CodeReason
101Physical document not used
102Suspected document tampering
103Person showing the document does not appear to match document photo
104Name entered does not match name on document
105Suspicious behaviour
106Known fraud
108Velocity/abuse — duplicated end-user
109Velocity/abuse — duplicated device
110Velocity/abuse — duplicated ID
111Session fraudulently accessed
112Restricted IP location
113Suspicious behaviour — Identity Farming
120Person on the portrait does not appear to match reference photo
121User ID missing
122No reference found
123Unable to pass registry checks
124Face already exists in the face collection
127Face match with blocklist
128End User ID mismatch
129Invalid workflow
CodeReason
201Video and/or photos missing
202Face not clearly visible
203Full document not visible
204Poor image quality
205Document damaged
206Document type not supported
207Document expired
208Selfie missing
209Document photo missing
210Document front photo submitted twice
211Document not visible in ID document image
212Document framing — part of the document is out of the image
213Document obscured — ID document is partially obscured (e.g. by fingers)
214Selfie poor quality — too dark or too blurry
215Document poor quality — document is not readable (too blurry, too dark)
216Document poor quality — glare
217Custom resubmission

Handling KYC Events

A typical implementation pattern:
async function handleKycEvent(event: WebhookEvent): Promise<void> {
  const { user_id, user_external_id, event_object } = event;
  const { status, failure_code, failure_reason } = event_object;

  switch (status) {
    case 'completed':
      // Grant access, activate card, etc.
      await activateUser(user_id);
      break;

    case 'failed':
      // Notify the user and log the failure reason
      await notifyUserKycFailed(user_external_id, failure_code, failure_reason);
      break;

    case 'edd_required':
      // Prompt the user to complete Enhanced Due Diligence
      await requestEdd(user_external_id);
      break;

    case 'pending':
    case 'kyc_checked':
    case 'edd_submitted':
      // Informational — no action required
      break;
  }
}