D

Credit Scoring Guide

D-ME integrates Skorix AI credit scoring into the verification flow. Add a single flag to your verification request and receive a pseudonymised credit score alongside the identity result — no extra API call needed at verification time.

How scoring works

  1. Add "score_requested": true to your verification request
  2. After Smile Identity confirms the identity, D-ME extracts pseudonymised behavioural features
  3. These features are sent to the Skorix scoring engine (no PII — see below)
  4. The score (0–1000) is attached to the verification and included in the webhook
  5. Retrieve the score anytime via POST /score/get
Privacy by design. No names, dates of birth, or ID numbers are sent to Skorix. Only pseudonymised signals derived from document quality and verification behaviour are transmitted.

Requesting a score

Add score_requested=true to your verification request. Your API key must have the score.get scope.

Verification with scoring
curl -X POST https://api.d-id.me/api/v1/kyc/verify \
  -H "Authorization: Bearer dme_live_xxxx" \
  -F "id_type=national_id" \
  -F "country=SN" \
  -F "external_ref=user_001" \
  -F "score_requested=true" \
  -F "front_image=@id_front.jpg" \
  -F "selfie_image=@selfie.jpg"

Retrieve a score

After verification completes, retrieve the score using the verification ID:

POST /score/get
curl -X POST https://api.d-id.me/api/v1/score/get \
  -H "Authorization: Bearer dme_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"verificationId": "550e8400-e29b-41d4-a716-446655440000"}'
Score response
{
  "data": {
    "scoreId": "sk_7f3a9c2d-...",
    "score": 742,
    "band": "high",
    "bandDescription": "Good profile. Suitable for most credit products.",
    "confidence": 0.95,
    "modelVersion": "mock-v1.0.0",
    "computedAt": "2026-05-06T10:30:25Z",
    "featuresUsed": [
      "document_authenticity_score",
      "liveness_score",
      "country_risk_band",
      "id_type_weight",
      "age_band",
      "verification_timestamp",
      "day_of_week",
      "hour_of_day"
    ],
    "flags": []
  }
}

Score fields

FieldTypeDescription
scoreinteger0–1000 composite score
bandstringlow / medium / high / premium
bandDescriptionstringHuman-readable band explanation
confidencefloatModel confidence, 0.0–1.0
modelVersionstringSkorix model version used
featuresUsedstring[]Feature names used (no values)
flagsstring[]Risk flags (empty in most cases)

Score bands

BandRangeDescriptionRecommended Use
premium800–1000Excellent profilePremium credit products, highest limits
high550–799Good profileMost credit products
medium300–549Moderate riskStandard products, lower limits
low0–299Higher riskManual review recommended

Score in the webhook

The verification.completed webhook includes the score object:

{
  "event": "verification.completed",
  "data": {
    "verificationId": "550e8400-e29b-41d4-a716-446655440000",
    "status": "approved",
    "externalRef": "user_001",
    "completedAt": "2026-05-06T10:30:25Z",
    "livenessCheckPassed": true,
    "documentVerified": true,
    "score": {
      "id": "local-score-uuid",
      "score": 742,
      "band": "high",
      "bandDescription": "Good profile. Suitable for most credit products.",
      "confidence": 0.95
    }
  },
  "timestamp": "2026-05-06T10:30:26Z"
}

The score field is null when scoring was not requested or failed. Always handle the null case gracefully:

Handling scores in your application

Score-based decision logic
// Always handle null score — scoring can fail without failing verification
function handleVerification(result) {
  if (!result.score) {
    // Verification succeeded but score unavailable
    // Option 1: approve with manual review
    // Option 2: request score separately via POST /score/get
    console.log('Score unavailable, proceeding without scoring');
    return { decision: 'manual_review', reason: 'scoring_unavailable' };
  }

  const { score, band, confidence } = result.score;

  // Low confidence → manual review
  if (confidence < 0.70) return { decision: 'manual_review', reason: 'low_confidence' };

  // Band-based decision
  if (band === 'premium') return { decision: 'approve', limit: 5_000_000 };  // XOF
  if (band === 'high')    return { decision: 'approve', limit: 2_000_000 };
  if (band === 'medium')  return { decision: 'approve', limit: 500_000 };
  if (band === 'low')     return { decision: 'manual_review', reason: 'low_band' };

  return { decision: 'decline', reason: 'insufficient_score' };
}

Feature pseudonymisation

The following signals are derived from the identity verification result and sent to Skorix. No PII is ever transmitted.

  • document_authenticity_score — derived from Smile Identity result code (0.0–1.0)
  • liveness_score — liveness check pass/fail → numeric (0.0–1.0)
  • country_risk_band — country-level risk category (SN=medium, MA=low)
  • id_type_weight — reliability weight by document type (passport=1.0, national_id=0.9)
  • age_band — statistical default, not derived from raw DOB
  • verification_timestamp / day_of_week / hour_of_day — timing signals
D-ME is contractually prohibited from sending names, dates of birth, ID numbers, or addresses to any scoring engine. The pseudonymisation layer is compliance-critical and unit-tested on every CI run.

When scoring fails

Scoring failure never fails or delays the verification. If Skorix is unavailable, the verification completes with score: null and the verification metadata includes:

"metadata": {
  "scoring_skipped": true,
  "scoring_error": "UNAVAILABLE"
}

You can retry scoring separately using POST /score/get with the verification ID once the service recovers.

Sandbox scores

In sandbox mode, D-ME uses the mock Skorix API which produces deterministic scores based on input features. The same verification input always returns the same score — making sandbox tests reproducible. Sandbox calls are never billed.

Pricing

Each successful POST /score/get call generates a metered usage event of type score.get. See Billing docs for pricing. Sandbox score requests are free.