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

# KYC Session Failed

> Webhook event fired when a KYC verification session fails

<Info>
  **Event Name:** `kyc_session_failed`
</Info>

<Note>
  This event is emitted when a KYC verification session fails and the entity could not be verified. The session state is `REJECTED`.
</Note>

## Event Payload

The webhook payload contains the following structure:

<CodeGroup>
  ```json Example Payload (User) theme={null}
  {
    "eventId": "c4f6d7a7-cf31-4ae4-af15-7571c0e7f743",
    "eventType": "kyc_session_failed",
    "timestamp": "2026-03-17T16:49:37.314Z",
    "data": {
      "sessionId": "43b972aa-88c2-4791-9a7b-7a338e182a3c",
      "userId": "15f8e008-d774-4a0f-bc39-a78c8e4859c9",
      "entityType": "user",
      "checks": [
        {
          "type": "manual_review",
          "state": 2,
          "data": {
            "type": "manual_review",
            "provider": "MOCK",
            "timestamp": "2026-03-17T12:40:24-04:00"
          },
          "reasons": [
            {
              "type": "ManualReview",
              "code": "KYC.DOC.DETERIORATED",
              "message": "Document deteriorated or illegible (stains, tears).",
              "category": "DOC"
            }
          ]
        }
      ]
    }
  }
  ```

  ```json Example Payload (Related Party) theme={null}
  {
    "eventId": "5749f57c-6559-43ee-8f88-3cf512b83bbb",
    "eventType": "kyc_session_failed",
    "timestamp": "2026-03-17T16:52:11.926Z",
    "data": {
      "sessionId": "7dedcb4e-feef-44a9-a7df-3b93b819d62c",
      "relatedPartyId": "7b280385-b7b7-4148-b9c0-d6d6f7dcb67b",
      "entityType": "related-party",
      "checks": [
        {
          "type": "manual_review",
          "state": 2,
          "data": {
            "type": "manual_review",
            "provider": "MOCK",
            "timestamp": "2026-03-17T12:45:03-04:00"
          },
          "reasons": [
            {
              "type": "ManualReview",
              "code": "KYC.DOC.DETERIORATED",
              "message": "Document deteriorated or illegible (stains, tears).",
              "category": "DOC"
            }
          ]
        }
      ]
    }
  }
  ```
</CodeGroup>

## Field Description

<ParamField path="eventId" type="string" required>
  Unique event identifier (UUID)
</ParamField>

<ParamField path="eventType" type="string" required>
  Event type - always `kyc_session_failed` for this webhook
</ParamField>

<ParamField path="timestamp" type="string" required>
  Event timestamp in ISO 8601 format
</ParamField>

<ParamField path="data" type="object" required>
  Contains the event data
</ParamField>

<ParamField path="data.sessionId" type="string" required>
  Unique KYC session ID (UUID)
</ParamField>

<ParamField path="data.userId" type="string">
  Unique user ID (UUID)
</ParamField>

<ParamField path="data.entityType" type="string" required>
  Entity type associated with the KYC session. Possible values: `user`, `related-party`
</ParamField>

<ParamField path="data.relatedPartyId" type="string">
  Unique related party ID (UUID)
</ParamField>

<ParamField path="data.checks" type="array">
  List of failed verification checks included in the `kyc_session_failed` event
</ParamField>

<ParamField path="data.checks[].type" type="string" required>
  Type of verification check, for example `manual_review`
</ParamField>

<ParamField path="data.checks[].state" type="integer" required>
  Check state. Current values are `1` for validated and `2` for refused
</ParamField>

<ParamField path="data.checks[].data" type="object" required>
  Additional raw check data. Its structure varies depending on the check type and may include provider-specific fields
</ParamField>

<ParamField path="data.checks[].reasons" type="array">
  Reasons associated with a refused check. This field is typically omitted or empty for validated checks
</ParamField>

<ParamField path="data.checks[].reasons[].type" type="string" required>
  Reason type or issue category detected during verification
</ParamField>

<ParamField path="data.checks[].reasons[].code" type="string" required>
  Machine-readable code identifying the failure reason
</ParamField>

<ParamField path="data.checks[].reasons[].message" type="string" required>
  Human-readable explanation of the failure reason
</ParamField>

<ParamField path="data.checks[].reasons[].category" type="string">
  Optional higher-level classification for the reason
</ParamField>

## Expected Responses

<CardGroup cols={3}>
  <Card title="Success Response" icon="circle-check" color="#16a34a">
    **HTTP 200 OK**

    The webhook was processed successfully.
  </Card>

  <Card title="Client Error" icon="triangle-exclamation" color="#ea580c">
    **HTTP 4xx Status**

    Client-side error. Will not be retried.
  </Card>

  <Card title="Server Error" icon="circle-xmark" color="#dc2626">
    **HTTP 5xx Status**

    Server-side error. Will be retried with exponential backoff.
  </Card>
</CardGroup>

## Implementation Example

<CodeGroup>
  ```javascript Node.js theme={null}
  app.post('/webhooks/ralio', async (req, res) => {
    const { eventType, data } = req.body;

    if (eventType === 'kyc_session_failed') {
      const { sessionId, entityType, userId, relatedPartyId, checks = [] } = data;
      const subjectId = userId ?? relatedPartyId;

      console.log(`KYC session ${sessionId} failed for ${entityType} ${subjectId}`);
      for (const check of checks) {
        for (const reason of check.reasons ?? []) {
          console.log(`Check ${check.type} failed with code ${reason.code}: ${reason.message}`);
        }
      }

      await updateKycSessionStatus(sessionId, 'REJECTED');

      if (entityType === 'user' && userId) {
        await notifyUserVerificationFailed(userId);
      }

      if (entityType === 'related-party' && relatedPartyId) {
        await notifyRelatedPartyVerificationFailed(relatedPartyId);
      }

      res.status(200).json({ received: true });
    }
  });
  ```

  ```python Python theme={null}
  @app.route('/webhooks/ralio', methods=['POST'])
  def handle_webhook():
      payload = request.get_json()

      if payload['eventType'] == 'kyc_session_failed':
          data = payload['data']
          session_id = data['sessionId']
          entity_type = data['entityType']
          user_id = data.get('userId')
          related_party_id = data.get('relatedPartyId')
          subject_id = user_id or related_party_id
          checks = data.get('checks', [])

          print(f"KYC session {session_id} failed for {entity_type} {subject_id}")
          for check in checks:
              for reason in check.get('reasons', []):
                  print(f"Check {check['type']} failed with code {reason['code']}: {reason['message']}")

          update_kyc_session_status(session_id, 'REJECTED')

          if entity_type == 'user' and user_id:
              notify_user_verification_failed(user_id)

          if entity_type == 'related-party' and related_party_id:
              notify_related_party_verification_failed(related_party_id)

          return jsonify({'received': True}), 200
  ```

  ```go Go theme={null}
  func handleWebhook(w http.ResponseWriter, r *http.Request) {
      var payload struct {
          EventType string `json:"eventType"`
          Data  struct {
              SessionID      string `json:"sessionId"`
              UserID         string `json:"userId"`
              RelatedPartyID string `json:"relatedPartyId"`
              EntityType     string `json:"entityType"`
              Checks         []struct {
                  Type    string                 `json:"type"`
                  State   int                    `json:"state"`
                  Data    map[string]interface{} `json:"data"`
                  Reasons []struct {
                      Type     string `json:"type"`
                      Code     string `json:"code"`
                      Message  string `json:"message"`
                      Category string `json:"category"`
                  } `json:"reasons"`
              } `json:"checks"`
          } `json:"data"`
      }

      if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
          http.Error(w, "Invalid JSON", http.StatusBadRequest)
          return
      }

      if payload.EventType == "kyc_session_failed" {
          subjectID := payload.Data.UserID
          if subjectID == "" {
              subjectID = payload.Data.RelatedPartyID
          }

          log.Printf("KYC session %s failed for %s %s", payload.Data.SessionID, payload.Data.EntityType, subjectID)
          for _, check := range payload.Data.Checks {
              for _, reason := range check.Reasons {
                  log.Printf("Check %s failed with code %s: %s", check.Type, reason.Code, reason.Message)
              }
          }

          if err := updateKycSessionStatus(payload.Data.SessionID, "REJECTED"); err != nil {
              http.Error(w, "Internal error", http.StatusInternalServerError)
              return
          }

          if payload.Data.EntityType == "user" && payload.Data.UserID != "" {
              if err := notifyUserVerificationFailed(payload.Data.UserID); err != nil {
                  http.Error(w, "Internal error", http.StatusInternalServerError)
                  return
              }
          }

          if payload.Data.EntityType == "related-party" && payload.Data.RelatedPartyID != "" {
              if err := notifyRelatedPartyVerificationFailed(payload.Data.RelatedPartyID); err != nil {
                  http.Error(w, "Internal error", http.StatusInternalServerError)
                  return
              }
          }

          w.Header().Set("Content-Type", "application/json")
          w.WriteHeader(http.StatusOK)
          json.NewEncoder(w).Encode(map[string]bool{"received": true})
      }
  }
  ```
</CodeGroup>
